/* 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 DBViewWrapper against a single local folder. Try and test all the * features we can without having a fake newsgroup. (Some features are * newsgroup specific.) */ /* import-globals-from resources/viewWrapperTestUtils.js */ load("resources/viewWrapperTestUtils.js"); initViewWrapperTestUtils({ mode: "local" }); var { SyntheticMessageSet } = ChromeUtils.import( "resource://testing-common/mailnews/MessageGenerator.jsm" ); var { setTimeout } = ChromeUtils.importESModule( "resource://gre/modules/Timer.sys.mjs" ); /* ===== Real Folder, no features ===== */ /** * Open a pre-populated real folder, make sure all the messages show up. */ add_task(async function test_real_folder_load() { let viewWrapper = make_view_wrapper(); let [[msgFolder], msgSet] = await messageInjection.makeFoldersWithSets(1, [ { count: 1 }, ]); viewWrapper.open(msgFolder); verify_messages_in_view(msgSet, viewWrapper); Assert.ok("test ran to completion"); }); /** * Open a real folder, add some messages, make sure they show up, remove some * messages, make sure they go away. */ add_task(async function test_real_folder_update() { let viewWrapper = make_view_wrapper(); // start with an empty folder let msgFolder = await messageInjection.makeEmptyFolder(); viewWrapper.open(msgFolder); verify_empty_view(viewWrapper); // add messages (none -> some) let [setOne] = await messageInjection.makeNewSetsInFolders([msgFolder], [{}]); verify_messages_in_view(setOne, viewWrapper); // add more messages! (some -> more) let [setTwo] = await messageInjection.makeNewSetsInFolders([msgFolder], [{}]); verify_messages_in_view([setOne, setTwo], viewWrapper); // remove the first set of messages (more -> some) await messageInjection.trashMessages(setOne); verify_messages_in_view(setTwo, viewWrapper); // remove the second set of messages (some -> none) await messageInjection.trashMessages(setTwo); verify_empty_view(viewWrapper); }); /** * Open a real folder, verify, open another folder, verify. We are testing * ability to change folders without exploding. */ add_task(async function test_real_folder_load_after_real_folder_load() { let viewWrapper = make_view_wrapper(); let [[folderOne], setOne] = await messageInjection.makeFoldersWithSets(1, [ {}, ]); viewWrapper.open(folderOne); verify_messages_in_view(setOne, viewWrapper); let [[folderTwo], setTwo] = await messageInjection.makeFoldersWithSets(1, [ {}, ]); viewWrapper.open(folderTwo); verify_messages_in_view(setTwo, viewWrapper); }); /* ===== Real Folder, Threading Modes ==== */ /* * The first three tests that verify setting the threading flags has the * expected outcome do this by creating the view from scratch with the view * flags applied. The view threading persistence test handles making sure * that changes in threading on-the-fly work from the perspective of the * bits and what not. None of these are tests of the view implementation's * threading/grouping logic, just sanity checking that we are doing the right * thing. */ add_task(async function test_real_folder_threading_unthreaded() { let viewWrapper = make_view_wrapper(); let folder = await messageInjection.makeEmptyFolder(); // create a single maximally nested thread. const count = 10; let messageSet = new SyntheticMessageSet( gMessageScenarioFactory.directReply(count) ); await messageInjection.addSetsToFolders([folder], [messageSet]); // verify that we are not threaded (or grouped) viewWrapper.open(folder); viewWrapper.beginViewUpdate(); viewWrapper.showUnthreaded = true; // whitebox test view flags (we've gotten them wrong before...) assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should not be set." ); assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should not be set." ); viewWrapper.endViewUpdate(); verify_view_level_histogram({ 0: count }, viewWrapper); }); add_task(async function test_real_folder_threading_threaded() { let viewWrapper = make_view_wrapper(); let folder = await messageInjection.makeEmptyFolder(); // create a single maximally nested thread. const count = 10; let messageSet = new SyntheticMessageSet( gMessageScenarioFactory.directReply(count) ); await messageInjection.addSetsToFolders([folder], [messageSet]); // verify that we are threaded (in such a way that we can't be grouped) viewWrapper.open(folder); viewWrapper.beginViewUpdate(); viewWrapper.showThreaded = true; // whitebox test view flags (we've gotten them wrong before...) assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should be set." ); assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should not be set." ); // expand everything so our logic below works. view_expand_all(viewWrapper); viewWrapper.endViewUpdate(); // blackbox test view flags: make sure IsContainer is true for the root verify_view_row_at_index_is_container(viewWrapper, 0); // do the histogram test to verify threading... let expectedHisto = {}; for (let i = 0; i < count; i++) { expectedHisto[i] = 1; } verify_view_level_histogram(expectedHisto, viewWrapper); }); add_task(async function test_real_folder_threading_grouped_by_sort() { let viewWrapper = make_view_wrapper(); // create some messages that belong to the 'in this week' bucket when sorting // by date and grouping by date. const count = 5; let [[folder]] = await messageInjection.makeFoldersWithSets(1, [ { count, age: { days: 2 }, age_incr: { mins: 1 } }, ]); // group-by-sort sorted by date viewWrapper.open(folder); viewWrapper.beginViewUpdate(); viewWrapper.showGroupedBySort = true; // whitebox test view flags (we've gotten them wrong before...) assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should be set." ); assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should be set." ); viewWrapper.sort( Ci.nsMsgViewSortType.byDate, Ci.nsMsgViewSortOrder.ascending ); // expand everyone view_expand_all(viewWrapper); viewWrapper.endViewUpdate(); // make sure the level depths are correct verify_view_level_histogram({ 0: 1, 1: count }, viewWrapper); // and make sure the first dude is a dummy verify_view_row_at_index_is_dummy(viewWrapper, 0); }); /** * Verify that we the threading modes are persisted. We are only checking * flags here; we trust the previous tests to have done their job. */ add_task(async function test_real_folder_threading_persistence() { let viewWrapper = make_view_wrapper(); let folder = await messageInjection.makeEmptyFolder(); // create a single maximally nested thread. const count = 10; let messageSet = new SyntheticMessageSet( gMessageScenarioFactory.directReply(count) ); await messageInjection.addSetsToFolders([folder], [messageSet]); // open the folder, set threaded mode, close it viewWrapper.open(folder); viewWrapper.showThreaded = true; // should be instantaneous verify_view_row_at_index_is_container(viewWrapper, 0); assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should be set." ); assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should not be set." ); viewWrapper.close(); // open it again, make sure we're threaded, go unthreaded, close viewWrapper.open(folder); assert_true(viewWrapper.showThreaded, "view should be threaded"); assert_false(viewWrapper.showUnthreaded, "view is lying about threading"); assert_false(viewWrapper.showGroupedBySort, "view is lying about threading"); verify_view_row_at_index_is_container(viewWrapper, 0); assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should be set." ); assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should not be set." ); viewWrapper.showUnthreaded = true; assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should not be set." ); assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should not be set." ); viewWrapper.close(); // open it again, make sure we're unthreaded, go grouped, close viewWrapper.open(folder); assert_true(viewWrapper.showUnthreaded, "view should be unthreaded"); assert_false(viewWrapper.showThreaded, "view is lying about threading"); assert_false(viewWrapper.showGroupedBySort, "view is lying about threading"); assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should not be set." ); assert_bit_not_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should not be set." ); viewWrapper.showGroupedBySort = true; assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should be set." ); assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should be set." ); viewWrapper.close(); // open it again, make sure we're grouped. viewWrapper.open(folder); assert_true(viewWrapper.showGroupedBySort, "view should be grouped"); assert_false(viewWrapper.showThreaded, "view is lying about threading"); assert_false(viewWrapper.showUnthreaded, "view is lying about threading"); assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kThreadedDisplay, "View threaded bit should be set." ); assert_bit_set( viewWrapper._viewFlags, Ci.nsMsgViewFlagsType.kGroupBySort, "View group-by-sort bit should be set." ); }); /* ===== Real Folder, View Flags ===== */ /* * We cannot test the ignored flag for a local folder because we cannot ignore * threads in a local folder. Only newsgroups can do that and that's not * easily testable at this time. * XXX ^^^ ignoring now works on mail as well. */ /** * Test the kUnreadOnly flag usage. This functionality is equivalent to the * mailview kViewItemUnread case, so it uses roughly the same test as * test_real_folder_mail_views_unread. */ add_task(async function test_real_folder_flags_show_unread() { let viewWrapper = make_view_wrapper(); let [[folder], setOne, setTwo] = await messageInjection.makeFoldersWithSets( 1, [{}, {}] ); // everything is unread to start with! #1 viewWrapper.open(folder); viewWrapper.beginViewUpdate(); viewWrapper.showUnreadOnly = true; viewWrapper.endViewUpdate(); verify_messages_in_view([setOne, setTwo], viewWrapper); // add some more things (unread!), make sure they appear. #2 let [setThree] = await messageInjection.makeNewSetsInFolders([folder], [{}]); verify_messages_in_view([setOne, setTwo, setThree], viewWrapper); // make some things read, make sure they disappear. #3 (after refresh) setTwo.setRead(true); viewWrapper.refresh(); // refresh to get the messages to disappear verify_messages_in_view([setOne, setThree], viewWrapper); // make those things un-read again. #2 setTwo.setRead(false); viewWrapper.refresh(); // QUICKSEARCH-VIEW-LIMITATION-REMOVE or not? verify_messages_in_view([setOne, setTwo, setThree], viewWrapper); }); /* ===== Real Folder, Mail Views ===== */ /* * For these tests, we are testing the filtering logic, not grouping or sorting * logic. The view tests are responsible for that stuff. We test that: * * 1) The view is populated correctly on open. * 2) The view adds things that become relevant. * 3) The view removes things that are no longer relevant. Because views like * to be stable (read: messages don't disappear as you look at them), this * requires refreshing the view (unless the message has been deleted). */ /** * Test the kViewItemUnread mail-view case. This functionality is equivalent * to the kUnreadOnly view flag case, so it uses roughly the same test as * test_real_folder_flags_show_unread. */ add_task(async function test_real_folder_mail_views_unread() { let viewWrapper = make_view_wrapper(); let [[folder], setOne, setTwo] = await messageInjection.makeFoldersWithSets( 1, [{}, {}] ); // everything is unread to start with! #1 viewWrapper.open(folder); await new Promise(resolve => setTimeout(resolve)); viewWrapper.setMailView(MailViewConstants.kViewItemUnread, null); verify_messages_in_view([setOne, setTwo], viewWrapper); // add some more things (unread!), make sure they appear. #2 let [setThree] = await messageInjection.makeNewSetsInFolders([folder], [{}]); verify_messages_in_view([setOne, setTwo, setThree], viewWrapper); // make some things read, make sure they disappear. #3 (after refresh) setTwo.setRead(true); viewWrapper.refresh(); // refresh to get the messages to disappear verify_messages_in_view([setOne, setThree], viewWrapper); // make those things un-read again. #2 setTwo.setRead(false); viewWrapper.refresh(); // QUICKSEARCH-VIEW-LIMITATION-REMOVE verify_messages_in_view([setOne, setTwo, setThree], viewWrapper); }); add_task(async function test_real_folder_mail_views_tags() { let viewWrapper = make_view_wrapper(); // setup the initial set with the tag let [[folder], setOne, setTwo] = await messageInjection.makeFoldersWithSets( 1, [{}, {}] ); setOne.addTag("$label1"); // open, apply mail view constraint, see those messages viewWrapper.open(folder); await new Promise(resolve => setTimeout(resolve)); viewWrapper.setMailView(MailViewConstants.kViewItemTags, "$label1"); verify_messages_in_view(setOne, viewWrapper); // add some more with the tag setTwo.addTag("$label1"); // make sure they showed up viewWrapper.refresh(); // QUICKSEARCH-VIEW-LIMITATION-REMOVE verify_messages_in_view([setOne, setTwo], viewWrapper); // remove them all setOne.removeTag("$label1"); setTwo.removeTag("$label1"); // make sure they all disappeared. #3 viewWrapper.refresh(); verify_empty_view(viewWrapper); }); /* add_task(async function test_real_folder_mail_views_not_deleted() { // not sure how to test this in the absence of an IMAP account with the IMAP // deletion model... }); add_task(async function test_real_folder_mail_views_custom_people_i_know() { // blurg. address book. }); */ // recent mail = less than 1 day add_task(async function test_real_folder_mail_views_custom_recent_mail() { let viewWrapper = make_view_wrapper(); // create a set that meets the threshold and a set that does not let [[folder], setRecent] = await messageInjection.makeFoldersWithSets(1, [ { age: { mins: 0 } }, { age: { days: 2 }, age_incr: { mins: 1 } }, ]); // open the folder, ensure only the recent guys show. #1 viewWrapper.open(folder); await new Promise(resolve => setTimeout(resolve)); viewWrapper.setMailView("Recent Mail", null); verify_messages_in_view(setRecent, viewWrapper); // add two more sets, one that meets, and one that doesn't. #2 let [setMoreRecent] = await messageInjection.makeNewSetsInFolders( [folder], [ { age: { mins: 0 } }, { age: { days: 2, hours: 1 }, age_incr: { mins: 1 } }, ] ); // make sure that all we see is our previous recent set and our new recent set verify_messages_in_view([setRecent, setMoreRecent], viewWrapper); // we aren't going to mess with the system clock, so no #3. // (we are assuming that the underlying code handles message deletion. also, // we are taking the position that message timestamps should not change.) }); add_task(async function test_real_folder_mail_views_custom_last_5_days() { let viewWrapper = make_view_wrapper(); // create a set that meets the threshold and a set that does not let [[folder], setRecent] = await messageInjection.makeFoldersWithSets(1, [ { age: { days: 2 }, age_incr: { mins: 1 } }, { age: { days: 6 }, age_incr: { mins: 1 } }, ]); // open the folder, ensure only the recent guys show. #1 viewWrapper.open(folder); await new Promise(resolve => setTimeout(resolve)); viewWrapper.setMailView("Last 5 Days", null); verify_messages_in_view(setRecent, viewWrapper); // add two more sets, one that meets, and one that doesn't. #2 let [setMoreRecent] = await messageInjection.makeNewSetsInFolders( [folder], [ { age: { mins: 0 } }, { age: { days: 5, hours: 1 }, age_incr: { mins: 1 } }, ] ); // make sure that all we see is our previous recent set and our new recent set verify_messages_in_view([setRecent, setMoreRecent], viewWrapper); // we aren't going to mess with the system clock, so no #3. // (we are assuming that the underlying code handles message deletion. also, // we are taking the position that message timestamps should not change.) }); add_task(async function test_real_folder_mail_views_custom_not_junk() { let viewWrapper = make_view_wrapper(); let [[folder], setJunk, setNotJunk] = await messageInjection.makeFoldersWithSets(1, [{}, {}]); setJunk.setJunk(true); setNotJunk.setJunk(false); // open, see non-junk messages. #1 viewWrapper.open(folder); await new Promise(resolve => setTimeout(resolve)); viewWrapper.setMailView("Not Junk", null); verify_messages_in_view(setNotJunk, viewWrapper); // add some more messages, have them be non-junk for now. #2 let [setFlippy] = await messageInjection.makeNewSetsInFolders([folder], [{}]); setFlippy.setJunk(false); viewWrapper.refresh(); // QUICKSEARCH-VIEW-LIMITATION-REMOVE verify_messages_in_view([setNotJunk, setFlippy], viewWrapper); // oops! they should be junk! #3 setFlippy.setJunk(true); viewWrapper.refresh(); verify_messages_in_view(setNotJunk, viewWrapper); }); add_task(async function test_real_folder_mail_views_custom_has_attachments() { let viewWrapper = make_view_wrapper(); let attachSetDef = { attachments: [ { filename: "foo.png", contentType: "image/png", encoding: "base64", charset: null, body: "YWJj\n", format: null, }, ], }; let noAttachSetDef = {}; let [[folder], , setAttach] = await messageInjection.makeFoldersWithSets(1, [ noAttachSetDef, attachSetDef, ]); viewWrapper.open(folder); await new Promise(resolve => setTimeout(resolve)); viewWrapper.setMailView("Has Attachments", null); verify_messages_in_view(setAttach, viewWrapper); let [setMoreAttach] = await messageInjection.makeNewSetsInFolders( [folder], [attachSetDef, noAttachSetDef] ); verify_messages_in_view([setAttach, setMoreAttach], viewWrapper); }); /* ===== Real Folder, Special Views ===== */ add_task(async function test_real_folder_special_views_threads_with_unread() { let viewWrapper = make_view_wrapper(); let folder = await messageInjection.makeEmptyFolder(); // create two maximally nested threads and add them to the folder. const count = 10; let setThreadOne = new SyntheticMessageSet( gMessageScenarioFactory.directReply(count) ); let setThreadTwo = new SyntheticMessageSet( gMessageScenarioFactory.directReply(count) ); await messageInjection.addSetsToFolders( [folder], [setThreadOne, setThreadTwo] ); // open the view, set it to this special view viewWrapper.open(folder); viewWrapper.beginViewUpdate(); viewWrapper.specialViewThreadsWithUnread = true; view_expand_all(viewWrapper); viewWrapper.endViewUpdate(); // no one is read at this point, make sure both threads show up. verify_messages_in_view([setThreadOne, setThreadTwo], viewWrapper); // mark both threads read, make sure they disappear (after a refresh) setThreadOne.setRead(true); setThreadTwo.setRead(true); viewWrapper.refresh(); verify_empty_view(viewWrapper); // make the first thread visible by marking his last message unread setThreadOne.slice(-1).setRead(false); view_expand_all(viewWrapper); viewWrapper.refresh(); verify_messages_in_view(setThreadOne, viewWrapper); // make the second thread visible by marking some message in the middle setThreadTwo.slice(5, 6).setRead(false); view_expand_all(viewWrapper); viewWrapper.refresh(); verify_messages_in_view([setThreadOne, setThreadTwo], viewWrapper); }); /** * Make sure that we restore special views from their persisted state when * opening the view. */ add_task(async function test_real_folder_special_views_persist() { let viewWrapper = make_view_wrapper(); let folder = await messageInjection.makeEmptyFolder(); viewWrapper.open(folder); viewWrapper.beginViewUpdate(); viewWrapper.specialViewThreadsWithUnread = true; viewWrapper.endViewUpdate(); viewWrapper.close(); viewWrapper.open(folder); assert_true( viewWrapper.specialViewThreadsWithUnread, "We should be in threads-with-unread special view mode." ); }); add_task(async function test_real_folder_mark_read_on_exit() { // set a pref so that the local folders account will think we should // mark messages read when leaving the folder. Services.prefs.setBoolPref("mailnews.mark_message_read.none", true); let viewWrapper = make_view_wrapper(); let folder = await messageInjection.makeEmptyFolder(); viewWrapper.open(folder); // add some unread messages. let [setOne] = await messageInjection.makeNewSetsInFolders([folder], [{}]); setOne.setRead(false); // verify that we have unread messages. assert_equals( folder.getNumUnread(false), setOne.synMessages.length, "all messages should have been added as unread" ); viewWrapper.close(false); // verify that closing the view does the expected marking of the messages // as read. assert_equals( folder.getNumUnread(false), 0, "messages should have been marked read on view close" ); Services.prefs.clearUserPref("mailnews.mark_message_read.none"); });