summaryrefslogtreecommitdiffstats
path: root/comm/mail/test/browser/quick-filter-bar
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/test/browser/quick-filter-bar')
-rw-r--r--comm/mail/test/browser/quick-filter-bar/browser.ini15
-rw-r--r--comm/mail/test/browser/quick-filter-bar/browser_filterLogic.js462
-rw-r--r--comm/mail/test/browser/quick-filter-bar/browser_keyboardInterface.js185
-rw-r--r--comm/mail/test/browser/quick-filter-bar/browser_stickyFilterLogic.js167
-rw-r--r--comm/mail/test/browser/quick-filter-bar/browser_toggleBar.js117
-rw-r--r--comm/mail/test/browser/quick-filter-bar/head.js56
6 files changed, 1002 insertions, 0 deletions
diff --git a/comm/mail/test/browser/quick-filter-bar/browser.ini b/comm/mail/test/browser/quick-filter-bar/browser.ini
new file mode 100644
index 0000000000..e1d90f25f0
--- /dev/null
+++ b/comm/mail/test/browser/quick-filter-bar/browser.ini
@@ -0,0 +1,15 @@
+[DEFAULT]
+head = head.js
+prefs =
+ mail.provider.suppress_dialog_on_startup=true
+ mail.spotlight.firstRunDone=true
+ mail.winsearch.firstRunDone=true
+ mailnews.start_page.override_url=about:blank
+ mailnews.start_page.url=about:blank
+ datareporting.policy.dataSubmissionPolicyBypassNotification=true
+subsuite = thunderbird
+
+[browser_filterLogic.js]
+[browser_keyboardInterface.js]
+[browser_stickyFilterLogic.js]
+[browser_toggleBar.js]
diff --git a/comm/mail/test/browser/quick-filter-bar/browser_filterLogic.js b/comm/mail/test/browser/quick-filter-bar/browser_filterLogic.js
new file mode 100644
index 0000000000..fb863ea154
--- /dev/null
+++ b/comm/mail/test/browser/quick-filter-bar/browser_filterLogic.js
@@ -0,0 +1,462 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Verify that we are constructing the filters that we expect and that they
+ * are hooked up to the right buttons.
+ */
+
+"use strict";
+
+var {
+ assert_messages_in_view,
+ assert_messages_not_in_view,
+ be_in_folder,
+ create_folder,
+ delete_messages,
+ get_about_3pane,
+ make_message_sets_in_folders,
+ mc,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var {
+ assert_quick_filter_bar_visible,
+ assert_results_label_count,
+ assert_text_constraints_checked,
+ clear_constraints,
+ set_filter_text,
+ toggle_boolean_constraints,
+ toggle_quick_filter_bar,
+ toggle_tag_constraints,
+ toggle_tag_mode,
+ toggle_text_constraints,
+ cleanup_qfb_button,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/QuickFilterBarHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+add_setup(async function () {
+ // Quick filter bar is hidden by default, need to toggle it on. To toggle
+ // quick filter bar, need to be inside folder
+ const folder = await create_folder("QuickFilterBarFilterFilterLogicSetup");
+ await be_in_folder(folder);
+ await ensure_table_view();
+ await toggle_quick_filter_bar();
+
+ registerCleanupFunction(async function () {
+ await ensure_cards_view();
+ await cleanup_qfb_button();
+ // Quick filter bar is hidden by default, need to toggle it off.
+ await toggle_quick_filter_bar();
+ });
+});
+
+add_task(async function test_filter_unread() {
+ let folder = await create_folder("QuickFilterBarFilterUnread");
+ let [unread, read] = await make_message_sets_in_folders(
+ [folder],
+ [{ count: 1 }, { count: 1 }]
+ );
+ read.setRead(true);
+
+ await be_in_folder(folder);
+ toggle_boolean_constraints("unread");
+ assert_messages_in_view(unread);
+ teardownTest();
+});
+
+add_task(async function test_filter_starred() {
+ let folder = await create_folder("QuickFilterBarFilterStarred");
+ let [, starred] = await make_message_sets_in_folders(
+ [folder],
+ [{ count: 1 }, { count: 1 }]
+ );
+ starred.setStarred(true);
+
+ await be_in_folder(folder);
+ toggle_boolean_constraints("starred");
+ assert_messages_in_view(starred);
+ teardownTest();
+});
+
+add_task(async function test_filter_simple_intersection_unread_and_starred() {
+ let folder = await create_folder("QuickFilterBarFilterUnreadAndStarred");
+ let [, readUnstarred, unreadStarred, readStarred] =
+ await make_message_sets_in_folders(
+ [folder],
+ [{ count: 1 }, { count: 1 }, { count: 1 }, { count: 1 }]
+ );
+ readUnstarred.setRead(true);
+ unreadStarred.setStarred(true);
+ readStarred.setRead(true);
+ readStarred.setStarred(true);
+
+ await be_in_folder(folder);
+ toggle_boolean_constraints("unread", "starred");
+
+ assert_messages_in_view(unreadStarred);
+ teardownTest();
+});
+
+add_task(async function test_filter_attachments() {
+ let attachSetDef = {
+ count: 1,
+ attachments: [
+ {
+ filename: "foo.png",
+ contentType: "image/png",
+ encoding: "base64",
+ charset: null,
+ body: "YWJj\n",
+ format: null,
+ },
+ ],
+ };
+ let noAttachSetDef = {
+ count: 1,
+ };
+
+ let folder = await create_folder("QuickFilterBarFilterAttachments");
+ let [, setAttach] = await make_message_sets_in_folders(
+ [folder],
+ [noAttachSetDef, attachSetDef]
+ );
+
+ await be_in_folder(folder);
+ toggle_boolean_constraints("attachments");
+
+ assert_messages_in_view(setAttach);
+ teardownTest();
+});
+
+/**
+ * Create a card for the given e-mail address, adding it to the first address
+ * book we can find.
+ */
+function add_email_to_address_book(aEmailAddr) {
+ let card = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance(
+ Ci.nsIAbCard
+ );
+ card.primaryEmail = aEmailAddr;
+
+ for (let addrbook of MailServices.ab.directories) {
+ addrbook.addCard(card);
+ return;
+ }
+
+ throw new Error("Unable to find any suitable address book.");
+}
+
+add_task(async function test_filter_in_address_book() {
+ let bookSetDef = {
+ from: ["Qbert Q Qbington", "q@q.invalid"],
+ count: 1,
+ };
+ add_email_to_address_book(bookSetDef.from[1]);
+ let folder = await create_folder("MesssageFilterBarInAddressBook");
+ let [setBook] = await make_message_sets_in_folders(
+ [folder],
+ [bookSetDef, { count: 1 }]
+ );
+ await be_in_folder(folder);
+ toggle_boolean_constraints("addrbook");
+ assert_messages_in_view(setBook);
+ teardownTest();
+});
+
+add_task(async function test_filter_tags() {
+ let folder = await create_folder("QuickFilterBarTags");
+ const tagA = "$label1",
+ tagB = "$label2",
+ tagC = "$label3";
+ let [setNoTag, setTagA, setTagB, setTagAB, setTagC] =
+ await make_message_sets_in_folders(
+ [folder],
+ [{ count: 1 }, { count: 1 }, { count: 1 }, { count: 1 }, { count: 1 }]
+ );
+ setTagA.addTag(tagA);
+ setTagB.addTag(tagB);
+ setTagAB.addTag(tagA);
+ setTagAB.addTag(tagB);
+ setTagC.addTag(tagC);
+
+ await be_in_folder(folder);
+ toggle_boolean_constraints("tags"); // must have a tag
+ assert_messages_in_view([setTagA, setTagB, setTagAB, setTagC]);
+
+ toggle_tag_constraints(tagA); // must have tag A
+ assert_messages_in_view([setTagA, setTagAB]);
+
+ toggle_tag_constraints(tagB);
+ // mode is OR by default -> must have tag A or tag B
+ assert_messages_in_view([setTagA, setTagB, setTagAB]);
+
+ toggle_tag_mode();
+ // mode is now AND -> must have tag A and tag B
+ assert_messages_in_view([setTagAB]);
+
+ toggle_tag_constraints(tagA); // must have tag B
+ assert_messages_in_view([setTagB, setTagAB]);
+
+ toggle_tag_constraints(tagB); // have have a tag
+ assert_messages_in_view([setTagA, setTagB, setTagAB, setTagC]);
+
+ toggle_boolean_constraints("tags"); // no constraints
+ assert_messages_in_view([setNoTag, setTagA, setTagB, setTagAB, setTagC]);
+
+ // If we have filtered to a specific tag and we disable the tag filter
+ // entirely, make sure that when we turn it back on we are just back to "any
+ // tag".
+ toggle_boolean_constraints("tags");
+ toggle_tag_constraints(tagC);
+ assert_messages_in_view(setTagC);
+
+ toggle_boolean_constraints("tags"); // no constraints
+ toggle_boolean_constraints("tags"); // should be any tag (not tagC!)
+ assert_messages_in_view([setTagA, setTagB, setTagAB, setTagC]);
+ teardownTest();
+});
+
+add_task(async function test_filter_text_single_word_and_predicates() {
+ let folder = await create_folder("QuickFilterBarTextSingleWord");
+ let whoFoo = ["zabba", "foo@madeup.invalid"];
+ let [, setSenderFoo, setRecipientsFoo, setSubjectFoo, setBodyFoo] =
+ await make_message_sets_in_folders(
+ [folder],
+ [
+ { count: 1 },
+ { count: 1, from: whoFoo },
+ { count: 1, to: [whoFoo] },
+ { count: 1, subject: "foo" },
+ { count: 1, body: { body: "foo" } },
+ ]
+ );
+ await be_in_folder(folder);
+
+ // by default, sender/recipients/subject are selected
+ assert_text_constraints_checked("sender", "recipients", "subject");
+
+ // con defaults, por favor
+ set_filter_text("foo");
+ assert_messages_in_view([setSenderFoo, setRecipientsFoo, setSubjectFoo]);
+ // note: we sequence the changes in the list so there is always at least one
+ // dude selected. selecting down to nothing has potential UI implications
+ // we don't want this test to get affected by.
+ // sender only
+ toggle_text_constraints("recipients", "subject");
+ assert_messages_in_view(setSenderFoo);
+ // recipients only
+ toggle_text_constraints("recipients", "sender");
+ assert_messages_in_view(setRecipientsFoo);
+ // subject only
+ toggle_text_constraints("subject", "recipients");
+ assert_messages_in_view(setSubjectFoo);
+ // body only
+ toggle_text_constraints("body", "subject");
+ assert_messages_in_view(setBodyFoo);
+ // everybody
+ toggle_text_constraints("sender", "recipients", "subject");
+ assert_messages_in_view([
+ setSenderFoo,
+ setRecipientsFoo,
+ setSubjectFoo,
+ setBodyFoo,
+ ]);
+
+ // sanity check non-matching
+ set_filter_text("notgonnamatchevercauseisayso");
+ assert_messages_in_view([]);
+ // disable body, still should get nothing
+ toggle_text_constraints("body");
+ assert_messages_in_view([]);
+
+ // (we are leaving with the defaults once again active)
+ assert_text_constraints_checked("sender", "recipients", "subject");
+ teardownTest();
+});
+
+/**
+ * Verify that the multi-word logic is actually splitting the words into
+ * different terms and that the terms can match in different predicates.
+ * This means that given "foo bar" we should be able to match "bar foo" in
+ * a subject and "foo" in the sender and "bar" in the recipient. And that
+ * constitutes sufficient positive coverage, although we also want to make
+ * sure that just a single term match is insufficient.
+ */
+add_task(async function test_filter_text_multi_word() {
+ let folder = await create_folder("QuickFilterBarTextMultiWord");
+
+ let whoFoo = ["foo", "zabba@madeup.invalid"];
+ let whoBar = ["zabba", "bar@madeup.invalid"];
+ let [, setPeepMatch, setSubjReverse] = await make_message_sets_in_folders(
+ [folder],
+ [
+ { count: 1 },
+ { count: 1, from: whoFoo, to: [whoBar] },
+ { count: 1, subject: "bar foo" },
+ { count: 1, from: whoFoo },
+ ]
+ );
+ await be_in_folder(folder);
+
+ // (precondition)
+ assert_text_constraints_checked("sender", "recipients", "subject");
+
+ set_filter_text("foo bar");
+ assert_messages_in_view([setPeepMatch, setSubjReverse]);
+ teardownTest();
+});
+
+/**
+ * Verify that the quickfilter bar has OR functionality using
+ * | (Pipe character) - Bug 586131
+ */
+add_task(async function test_filter_or_operator() {
+ let folder = await create_folder("QuickFilterBarOrOperator");
+
+ let whoFoo = ["foo", "zabba@madeup.invalid"];
+ let whoBar = ["zabba", "bar@madeup.invalid"];
+ let whoTest = ["test", "test@madeup.invalid"];
+ let [setInert, setSenderFoo, setToBar, , , setSubject3, setMail1] =
+ await make_message_sets_in_folders(
+ [folder],
+ [
+ { count: 1 },
+ { count: 1, from: whoFoo },
+ { count: 1, to: [whoBar] },
+ { count: 1, subject: "foo bar" },
+ { count: 1, subject: "bar test" },
+ { count: 1, subject: "test" },
+ { count: 1, to: [whoTest], subject: "logic" },
+ { count: 1, from: whoFoo, to: [whoBar], subject: "test" },
+ ]
+ );
+ await be_in_folder(folder);
+
+ assert_text_constraints_checked("sender", "recipients", "subject");
+ set_filter_text("foo | bar");
+ assert_messages_not_in_view([setInert, setSubject3, setMail1]);
+
+ set_filter_text("test | bar");
+ assert_messages_not_in_view([setInert, setSenderFoo]);
+
+ set_filter_text("foo | test");
+ assert_messages_not_in_view([setInert, setToBar]);
+
+ // consists of leading and trailing spaces and tab character.
+ set_filter_text("test | foo bar");
+ assert_messages_not_in_view([
+ setInert,
+ setSenderFoo,
+ setToBar,
+ setSubject3,
+ setMail1,
+ ]);
+
+ set_filter_text("test | foo bar |logic");
+ assert_messages_not_in_view([setInert, setSenderFoo, setToBar, setSubject3]);
+ teardownTest();
+});
+
+/**
+ * Make sure that when dropping all constraints on toggle off or changing
+ * folders that we persist/propagate the state of the
+ * sender/recipients/subject/body toggle buttons.
+ */
+add_task(async function test_filter_text_constraints_propagate() {
+ let whoFoo = ["foo", "zabba@madeup.invalid"];
+ let whoBar = ["zabba", "bar@madeup.invalid"];
+
+ let folderOne = await create_folder("QuickFilterBarTextPropagate1");
+ let [setSubjFoo, setWhoFoo] = await make_message_sets_in_folders(
+ [folderOne],
+ [
+ { count: 1, subject: "foo" },
+ { count: 1, from: whoFoo },
+ ]
+ );
+ let folderTwo = await create_folder("QuickFilterBarTextPropagate2");
+ let [, setWhoBar] = await make_message_sets_in_folders(
+ [folderTwo],
+ [
+ { count: 1, subject: "bar" },
+ { count: 1, from: whoBar },
+ ]
+ );
+
+ await be_in_folder(folderOne);
+ set_filter_text("foo");
+ // (precondition)
+ assert_text_constraints_checked("sender", "recipients", "subject");
+ assert_messages_in_view([setSubjFoo, setWhoFoo]);
+
+ // -- drop subject, close bar to reset, make sure it sticks
+ toggle_text_constraints("subject");
+ assert_messages_in_view([setWhoFoo]);
+
+ await toggle_quick_filter_bar();
+ await toggle_quick_filter_bar();
+
+ set_filter_text("foo");
+ assert_messages_in_view([setWhoFoo]);
+ assert_text_constraints_checked("sender", "recipients");
+
+ // -- now change folders and make sure the settings stick
+ await be_in_folder(folderTwo);
+ set_filter_text("bar");
+ assert_messages_in_view([setWhoBar]);
+ assert_text_constraints_checked("sender", "recipients");
+ teardownTest();
+});
+
+/**
+ * Here is what the results label does:
+ * - No filter active: results label is not visible.
+ * - Filter active, messages: it says the number of messages.
+ * - Filter active, no messages: it says there are no messages.
+ *
+ * Additional nuances:
+ * - The count needs to update as the user deletes messages or what not.
+ */
+add_task(async function test_results_label() {
+ let folder = await create_folder("QuickFilterBarResultsLabel");
+ let [setImmortal, setMortal, setGoldfish] =
+ await make_message_sets_in_folders(
+ [folder],
+ [{ count: 1 }, { count: 1 }, { count: 1 }]
+ );
+
+ await be_in_folder(folder);
+
+ // no filter, the label should not be visible
+ Assert.ok(
+ BrowserTestUtils.is_hidden(
+ get_about_3pane().document.getElementById("qfb-results-label")
+ ),
+ "results label should not be visible"
+ );
+
+ toggle_boolean_constraints("unread");
+ assert_messages_in_view([setImmortal, setMortal, setGoldfish]);
+ assert_results_label_count(3);
+
+ await delete_messages(setGoldfish);
+ assert_results_label_count(2);
+
+ await delete_messages(setMortal);
+ assert_results_label_count(1);
+
+ await delete_messages(setImmortal);
+ assert_results_label_count(0);
+ teardownTest();
+});
+
+function teardownTest() {
+ clear_constraints();
+}
diff --git a/comm/mail/test/browser/quick-filter-bar/browser_keyboardInterface.js b/comm/mail/test/browser/quick-filter-bar/browser_keyboardInterface.js
new file mode 100644
index 0000000000..7deced3391
--- /dev/null
+++ b/comm/mail/test/browser/quick-filter-bar/browser_keyboardInterface.js
@@ -0,0 +1,185 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Tests keyboard stuff that doesn't fall under some other test's heading.
+ * Namely, control-shift-k toggling the bar into existence happens in
+ * test-toggle-bar.js, but we test that repeatedly hitting control-shift-k
+ * selects the text entered in the quick filter bar.
+ */
+
+"use strict";
+
+var {
+ be_in_folder,
+ create_folder,
+ get_about_3pane,
+ make_message_sets_in_folders,
+ mc,
+ select_click_row,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var {
+ assert_constraints_expressed,
+ assert_filter_text,
+ assert_quick_filter_bar_visible,
+ clear_constraints,
+ set_filter_text,
+ toggle_boolean_constraints,
+ toggle_quick_filter_bar,
+ cleanup_qfb_button,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/QuickFilterBarHelpers.jsm"
+);
+
+var folder;
+
+add_setup(async function () {
+ folder = await create_folder("QuickFilterBarKeyboardInterface");
+ // We need a message so we can select it so we can find in message.
+ await make_message_sets_in_folders([folder], [{ count: 1 }]);
+ await be_in_folder(folder);
+ await ensure_table_view();
+
+ // Quick filter bar is hidden by default, need to toggle it on.
+ await toggle_quick_filter_bar();
+
+ registerCleanupFunction(async () => {
+ await ensure_cards_view();
+ await cleanup_qfb_button();
+ // Quick filter bar is hidden by default, need to toggle it off.
+ await toggle_quick_filter_bar();
+ });
+});
+
+/**
+ * The rules for pressing escape:
+ * - If there are any applied constraints:
+ * - If there is a 'most recent' constraint, it is relaxed and the 'most
+ * recent' field gets cleared, so that if escape gets hit again...
+ * - If there is no 'most recent' constraint, all constraints are cleared.
+ * - If there are no applied constraints, we close the filter bar.
+ *
+ * We test these rules two ways:
+ * 1) With the focus in the thread pane.
+ * 2) With our focus in our text-box.
+ */
+add_task(async function test_escape_rules() {
+ assert_quick_filter_bar_visible(true); // (precondition)
+
+ // the common logic for each bit...
+ async function legwork() {
+ // apply two...
+ toggle_boolean_constraints("unread", "starred", "addrbook");
+ assert_constraints_expressed({
+ unread: true,
+ starred: true,
+ addrbook: true,
+ });
+ assert_quick_filter_bar_visible(true);
+
+ // hit escape, should clear addrbook
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ assert_quick_filter_bar_visible(true);
+ assert_constraints_expressed({ unread: true, starred: true });
+
+ // hit escape, should clear both remaining ones
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ assert_quick_filter_bar_visible(true);
+ assert_constraints_expressed({});
+
+ // hit escape, bar should disappear
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ assert_quick_filter_bar_visible(false);
+
+ // bring the bar back for the next dude
+ await toggle_quick_filter_bar();
+ }
+
+ let about3Pane = get_about_3pane();
+
+ // 1) focus in the thread pane
+ about3Pane.document.getElementById("threadTree").focus();
+ await legwork();
+
+ // 2) focus in the text box
+ about3Pane.document.getElementById("qfb-qs-textbox").focus();
+ await legwork();
+
+ // 3) focus in the text box and pretend to type stuff...
+ about3Pane.document.getElementById("qfb-qs-textbox").focus();
+ set_filter_text("qxqxqxqx");
+
+ // Escape should clear the text constraint but the bar should still be
+ // visible. The trick here is that escape is clearing the text widget
+ // and is not falling through to the cmd_popQuickFilterBarStack case so we
+ // end up with a situation where the _lastFilterAttr is the textbox but the
+ // textbox does not actually have any active filter.
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ assert_quick_filter_bar_visible(true);
+ assert_constraints_expressed({});
+ assert_filter_text("");
+
+ // Next escape should close the box
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ assert_quick_filter_bar_visible(false);
+ teardownTest();
+});
+
+/**
+ * Control-shift-k expands the quick filter bar when it's collapsed. When
+ * already expanded, it focuses the text box and selects its text.
+ */
+add_task(function test_control_shift_k_shows_quick_filter_bar() {
+ let about3Pane = get_about_3pane();
+
+ let dispatcha = mc.window.document.commandDispatcher;
+ let qfbTextbox = about3Pane.document.getElementById("qfb-qs-textbox");
+
+ // focus explicitly on the thread pane so we know where the focus is.
+ about3Pane.document.getElementById("threadTree").focus();
+ // select a message so we can find in message
+ select_click_row(0);
+
+ // hit control-shift-k to get in the quick filter box
+ EventUtils.synthesizeKey("k", { accelKey: true, shiftKey: true });
+ if (dispatcha.focusedElement != qfbTextbox.inputField) {
+ throw new Error("control-shift-k did not focus quick filter textbox");
+ }
+
+ set_filter_text("search string");
+
+ // hit control-shift-k to select the text in the quick filter box
+ EventUtils.synthesizeKey("k", { accelKey: true, shiftKey: true });
+ if (dispatcha.focusedElement != qfbTextbox.inputField) {
+ throw new Error(
+ "second control-shift-k did not keep focus on filter textbox"
+ );
+ }
+ if (
+ qfbTextbox.inputField.selectionStart != 0 ||
+ qfbTextbox.inputField.selectionEnd != qfbTextbox.inputField.textLength
+ ) {
+ throw new Error(
+ "second control-shift-k did not select text in filter textbox"
+ );
+ }
+
+ // hit escape and make sure the text is cleared, but the quick filter bar is
+ // still open.
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ assert_quick_filter_bar_visible(true);
+ assert_filter_text("");
+
+ // hit escape one more time and make sure we finally collapsed the quick
+ // filter bar.
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ assert_quick_filter_bar_visible(false);
+ teardownTest();
+});
+
+function teardownTest() {
+ clear_constraints();
+}
diff --git a/comm/mail/test/browser/quick-filter-bar/browser_stickyFilterLogic.js b/comm/mail/test/browser/quick-filter-bar/browser_stickyFilterLogic.js
new file mode 100644
index 0000000000..2c373889c3
--- /dev/null
+++ b/comm/mail/test/browser/quick-filter-bar/browser_stickyFilterLogic.js
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Sticky logic only needs to test the general sticky logic plus any filters
+ * with custom propagateState implementations (currently: tags, text filter.)
+ */
+
+"use strict";
+
+var {
+ assert_messages_in_view,
+ be_in_folder,
+ close_tab,
+ create_folder,
+ get_about_3pane,
+ make_message_sets_in_folders,
+ mc,
+ open_folder_in_new_tab,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var {
+ assert_constraints_expressed,
+ assert_filter_text,
+ assert_tag_constraints_visible,
+ clear_constraints,
+ set_filter_text,
+ toggle_boolean_constraints,
+ toggle_tag_constraints,
+ toggle_quick_filter_bar,
+ cleanup_qfb_button,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/QuickFilterBarHelpers.jsm"
+);
+
+add_setup(async function () {
+ // Quick filter bar is hidden by default, need to toggle it on. To toggle
+ // quick filter bar, we need to be inside folder
+ const folder = await create_folder("QuickFilterBarFilterStickySetup");
+ await be_in_folder(folder);
+ await ensure_table_view();
+ await toggle_quick_filter_bar();
+
+ registerCleanupFunction(async () => {
+ await ensure_cards_view();
+ await cleanup_qfb_button();
+ // Quick filter bar is hidden by default, need to toggle it off.
+ await toggle_quick_filter_bar();
+ });
+});
+
+/**
+ * Persist the current settings through folder change and inherit into a new tab.
+ */
+add_task(async function test_sticky_basics() {
+ let folderOne = await create_folder("QuickFilterBarStickyBasics1");
+ let [unreadOne, readOne] = await make_message_sets_in_folders(
+ [folderOne],
+ [{ count: 1 }, { count: 1 }]
+ );
+ readOne.setRead(true);
+
+ let folderTwo = await create_folder("QuickFilterBarStickyBasics2");
+ let [unreadTwo, readTwo] = await make_message_sets_in_folders(
+ [folderTwo],
+ [{ count: 1 }, { count: 1 }]
+ );
+ readTwo.setRead(true);
+
+ // -- setup
+ await be_in_folder(folderOne);
+ toggle_boolean_constraints("sticky", "unread");
+ assert_messages_in_view(unreadOne);
+
+ // -- change folders
+ await be_in_folder(folderTwo);
+ assert_constraints_expressed({ sticky: true, unread: true });
+ assert_messages_in_view(unreadTwo);
+
+ // -- inherit into a new folder
+ // TODO: Reimplement this behaviour.
+ // let tabB = await open_folder_in_new_tab(folderOne);
+ // assert_constraints_expressed({ sticky: true, unread: true });
+ // assert_messages_in_view(unreadOne);
+
+ // close_tab(tabB);
+ teardownTest();
+});
+
+/**
+ * The semantics of sticky tags are not obvious; there were decisions involved:
+ * - If the user has the tag facet enabled but not explicitly filtered on
+ * specific tags then we propagate just "true" to cause the faceting to
+ * run in the new folder. In other words, the list of displayed tags should
+ * change.
+ * - If the user has filtered on specific tags, then we do and must propagate
+ * the list of tags.
+ *
+ * We only need to do folder changes from here on out since the logic is
+ * identical (and tested to be identical in |test_sticky_basics|).
+ */
+add_task(async function test_sticky_tags() {
+ let folderOne = await create_folder("QuickFilterBarStickyTags1");
+ let folderTwo = await create_folder("QuickFilterBarStickyTags2");
+ const tagA = "$label1",
+ tagB = "$label2",
+ tagC = "$label3";
+ let [, setTagA1, setTagB1] = await make_message_sets_in_folders(
+ [folderOne],
+ [{ count: 1 }, { count: 1 }, { count: 1 }]
+ );
+ let [, setTagA2, setTagC2] = await make_message_sets_in_folders(
+ [folderTwo],
+ [{ count: 1 }, { count: 1 }, { count: 1 }]
+ );
+ setTagA1.addTag(tagA);
+ setTagB1.addTag(tagB);
+ setTagA2.addTag(tagA);
+ setTagC2.addTag(tagC);
+
+ await be_in_folder(folderOne);
+ toggle_boolean_constraints("sticky", "tags");
+ assert_tag_constraints_visible(tagA, tagB);
+ assert_messages_in_view([setTagA1, setTagB1]);
+
+ // -- re-facet when we change folders since constraint was just true
+ await be_in_folder(folderTwo);
+ assert_tag_constraints_visible(tagA, tagC);
+ assert_messages_in_view([setTagA2, setTagC2]);
+
+ // -- do not re-facet since tag A was selected
+ toggle_tag_constraints(tagA);
+ await be_in_folder(folderOne);
+ assert_tag_constraints_visible(tagA, tagC);
+ assert_messages_in_view([setTagA1]);
+
+ // -- if we turn off sticky, make sure that things clear when we change
+ // folders. (we had a bug with this before.)
+ toggle_boolean_constraints("sticky");
+ await be_in_folder(folderTwo);
+ assert_constraints_expressed({});
+ teardownTest();
+});
+
+/**
+ * All we are testing propagating is the text value; the text states are always
+ * propagated and that is tested in test-filter-logic.js by
+ * |test_filter_text_constraints_propagate|.
+ */
+add_task(async function test_sticky_text() {
+ let folderOne = await create_folder("QuickFilterBarStickyText1");
+ let folderTwo = await create_folder("QuickFilterBarStickyText2");
+
+ await be_in_folder(folderOne);
+ toggle_boolean_constraints("sticky");
+ set_filter_text("foo");
+
+ await be_in_folder(folderTwo);
+ assert_filter_text("foo");
+ teardownTest();
+});
+
+function teardownTest() {
+ clear_constraints();
+}
diff --git a/comm/mail/test/browser/quick-filter-bar/browser_toggleBar.js b/comm/mail/test/browser/quick-filter-bar/browser_toggleBar.js
new file mode 100644
index 0000000000..779df5e467
--- /dev/null
+++ b/comm/mail/test/browser/quick-filter-bar/browser_toggleBar.js
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Test that the message filter bar toggles into and out of existence and
+ * states are updated as appropriate.
+ */
+
+"use strict";
+
+var {
+ assert_messages_in_view,
+ be_in_folder,
+ create_folder,
+ focus_thread_tree,
+ get_about_3pane,
+ make_message_sets_in_folders,
+ mc,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var {
+ assert_constraints_expressed,
+ assert_quick_filter_bar_visible,
+ assert_quick_filter_button_enabled,
+ clear_constraints,
+ toggle_boolean_constraints,
+ toggle_quick_filter_bar,
+ cleanup_qfb_button,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/QuickFilterBarHelpers.jsm"
+);
+
+var folder;
+var setUnstarred, setStarred;
+
+add_setup(async function () {
+ folder = await create_folder("QuickFilterBarToggleBar");
+ [setUnstarred, setStarred] = await make_message_sets_in_folders(
+ [folder],
+ [{ count: 1 }, { count: 1 }]
+ );
+ setStarred.setStarred(true);
+
+ registerCleanupFunction(async () => {
+ await ensure_cards_view();
+ });
+});
+
+add_task(async function test_hidden_on_account_central() {
+ await be_in_folder(folder.rootFolder);
+ await assert_quick_filter_button_enabled(false);
+ assert_quick_filter_bar_visible(false);
+ teardownTest();
+});
+
+add_task(async function test_visible_by_default() {
+ await be_in_folder(folder);
+ await ensure_table_view();
+ await assert_quick_filter_button_enabled(true);
+ assert_quick_filter_bar_visible(true);
+ teardownTest();
+});
+
+add_task(async function test_direct_toggle() {
+ assert_quick_filter_bar_visible(true);
+ await toggle_quick_filter_bar();
+ assert_quick_filter_bar_visible(false);
+ await toggle_quick_filter_bar();
+ assert_quick_filter_bar_visible(true);
+ teardownTest();
+});
+
+add_task(async function test_control_shift_k_triggers_display() {
+ // hide it
+ await toggle_quick_filter_bar();
+ assert_quick_filter_bar_visible(false);
+
+ // focus explicitly on the thread pane so we know where the focus is.
+ focus_thread_tree();
+
+ // hit control-shift-k
+ EventUtils.synthesizeKey("k", { accelKey: true, shiftKey: true });
+
+ // now we should be visible again!
+ assert_quick_filter_bar_visible(true);
+ teardownTest();
+});
+
+add_task(async function test_constraints_disappear_when_collapsed() {
+ // set some constraints
+ toggle_boolean_constraints("starred");
+ assert_constraints_expressed({ starred: true });
+ assert_messages_in_view(setStarred);
+
+ // collapse, now we should see them all again!
+ await toggle_quick_filter_bar();
+ assert_messages_in_view([setUnstarred, setStarred]);
+
+ // uncollapse, we should still see them all!
+ await toggle_quick_filter_bar();
+ assert_messages_in_view([setUnstarred, setStarred]);
+
+ // there better be no constraints left!
+ assert_constraints_expressed({});
+ teardownTest();
+});
+
+registerCleanupFunction(async () => {
+ await ensure_cards_view();
+ await cleanup_qfb_button();
+});
+
+function teardownTest() {
+ clear_constraints();
+}
diff --git a/comm/mail/test/browser/quick-filter-bar/head.js b/comm/mail/test/browser/quick-filter-bar/head.js
new file mode 100644
index 0000000000..2a8b342cc0
--- /dev/null
+++ b/comm/mail/test/browser/quick-filter-bar/head.js
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Helper method to switch to a cards view with vertical layout.
+ */
+async function ensure_cards_view() {
+ const { threadTree, threadPane } =
+ document.getElementById("tabmail").currentAbout3Pane;
+
+ Services.prefs.setIntPref("mail.pane_config.dynamic", 2);
+ Services.xulStore.setValue(
+ "chrome://messenger/content/messenger.xhtml",
+ "threadPane",
+ "view",
+ "cards"
+ );
+ threadPane.updateThreadView("cards");
+
+ await BrowserTestUtils.waitForCondition(
+ () => threadTree.getAttribute("rows") == "thread-card",
+ "The tree view switched to a cards layout"
+ );
+}
+
+/**
+ * Helper method to switch to a table view with classic layout.
+ */
+async function ensure_table_view() {
+ const { threadTree, threadPane } =
+ document.getElementById("tabmail").currentAbout3Pane;
+
+ Services.prefs.setIntPref("mail.pane_config.dynamic", 0);
+ Services.xulStore.setValue(
+ "chrome://messenger/content/messenger.xhtml",
+ "threadPane",
+ "view",
+ "table"
+ );
+ threadPane.updateThreadView("table");
+
+ await BrowserTestUtils.waitForCondition(
+ () => threadTree.getAttribute("rows") == "thread-row",
+ "The tree view switched to a table layout"
+ );
+}
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("mail.pane_config.dynamic");
+ Services.xulStore.removeValue(
+ "chrome://messenger/content/messenger.xhtml",
+ "threadPane",
+ "view"
+ );
+});