diff options
Diffstat (limited to 'comm/mail/test/browser/quick-filter-bar')
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" + ); +}); |