summaryrefslogtreecommitdiffstats
path: root/comm/mail/base/test/browser/browser_navigation.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/base/test/browser/browser_navigation.js')
-rw-r--r--comm/mail/base/test/browser/browser_navigation.js1035
1 files changed, 1035 insertions, 0 deletions
diff --git a/comm/mail/base/test/browser/browser_navigation.js b/comm/mail/base/test/browser/browser_navigation.js
new file mode 100644
index 0000000000..baa7bc6142
--- /dev/null
+++ b/comm/mail/base/test/browser/browser_navigation.js
@@ -0,0 +1,1035 @@
+/* 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/. */
+
+const { MessageGenerator } = ChromeUtils.import(
+ "resource://testing-common/mailnews/MessageGenerator.jsm"
+);
+const { PromiseTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/PromiseTestUtils.jsm"
+);
+
+let tabmail = document.getElementById("tabmail");
+let about3Pane = tabmail.currentAbout3Pane;
+let { messageBrowser, multiMessageBrowser, threadTree } = about3Pane;
+let mailboxService = MailServices.messageServiceFromURI("mailbox:");
+let folderA,
+ folderAMessages,
+ folderB,
+ folderBMessages,
+ folderC,
+ folderCMessages,
+ folderD,
+ folderDMessages;
+
+add_setup(async function () {
+ Services.prefs.setBoolPref("mailnews.mark_message_read.auto", false);
+
+ let generator = new MessageGenerator();
+
+ MailServices.accounts.createLocalMailAccount();
+ let account = MailServices.accounts.accounts[0];
+ account.addIdentity(MailServices.accounts.createIdentity());
+ let rootFolder = account.incomingServer.rootFolder;
+
+ rootFolder.createSubfolder("Navigation A", null);
+ folderA = rootFolder
+ .getChildNamed("Navigation A")
+ .QueryInterface(Ci.nsIMsgLocalMailFolder);
+ folderA.addMessageBatch(
+ generator.makeMessages({ count: 5 }).map(message => message.toMboxString())
+ );
+ folderAMessages = [...folderA.messages];
+ folderA.markAllMessagesRead(null);
+
+ rootFolder.createSubfolder("Navigation B", null);
+ folderB = rootFolder
+ .getChildNamed("Navigation B")
+ .QueryInterface(Ci.nsIMsgLocalMailFolder);
+ folderB.addMessageBatch(
+ generator.makeMessages({ count: 5 }).map(message => message.toMboxString())
+ );
+ folderBMessages = [...folderB.messages];
+ folderB.markAllMessagesRead(null);
+
+ rootFolder.createSubfolder("Navigation C", null);
+ folderC = rootFolder
+ .getChildNamed("Navigation C")
+ .QueryInterface(Ci.nsIMsgLocalMailFolder);
+ // Add a lot of messages so scrolling can be tested.
+ folderC.addMessageBatch(
+ generator
+ .makeMessages({ count: 500 })
+ .map(message => message.toMboxString())
+ );
+ folderC.addMessageBatch(
+ generator.makeMessages({ count: 5 }).map(message => message.toMboxString())
+ );
+ folderCMessages = [...folderC.messages];
+ folderC.markAllMessagesRead(null);
+
+ rootFolder.createSubfolder("Navigation D", null);
+ folderD = rootFolder
+ .getChildNamed("Navigation D")
+ .QueryInterface(Ci.nsIMsgLocalMailFolder);
+ folderD.addMessageBatch(
+ generator
+ .makeMessages({
+ count: 12,
+ msgsPerThread: 3,
+ })
+ .map(message => message.toMboxString())
+ );
+ folderDMessages = [...folderD.messages];
+ folderD.markAllMessagesRead(null);
+
+ registerCleanupFunction(() => {
+ MailServices.accounts.removeAccount(account, false);
+ Services.prefs.clearUserPref("mailnews.mark_message_read.auto");
+ });
+});
+
+/** Tests the next message/previous message commands. */
+add_task(async function testNextPreviousMessageInAbout3Pane() {
+ const aboutMessage = messageBrowser.contentWindow;
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+
+ about3Pane.displayFolder(folderA.URI);
+ assertSelectedMessage();
+ await assertNoDisplayedMessage(aboutMessage);
+
+ for (let i = 0; i < 5; i++) {
+ goDoCommand("cmd_nextMsg");
+ assertSelectedMessage(folderAMessages[i]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[i]);
+ }
+
+ threadTree.addEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ goDoCommand("cmd_nextMsg");
+ assertSelectedMessage(
+ folderAMessages[4],
+ "the selected message should not change"
+ );
+ await assertDisplayedMessage(aboutMessage, folderAMessages[4]);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ threadTree.removeEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+
+ for (let i = 3; i >= 0; i--) {
+ goDoCommand("cmd_previousMsg");
+ assertSelectedMessage(folderAMessages[i]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[i]);
+ }
+
+ threadTree.addEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ goDoCommand("cmd_previousMsg");
+ assertSelectedMessage(
+ folderAMessages[0],
+ "the selected message should not change"
+ );
+ await assertDisplayedMessage(aboutMessage, folderAMessages[0]);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ threadTree.removeEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+
+ threadTree.selectedIndex = -1;
+ await assertNoDisplayedMessage(aboutMessage);
+});
+
+async function subtestNextPreviousMessage(win, aboutMessage) {
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+
+ await assertDisplayedMessage(aboutMessage, folderAMessages[2]);
+
+ for (let i = 3; i < 5; i++) {
+ win.goDoCommand("cmd_nextMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[i]);
+ }
+
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ win.goDoCommand("cmd_nextMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[4]);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+
+ for (let i = 3; i >= 0; i--) {
+ win.goDoCommand("cmd_previousMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[i]);
+ }
+
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ win.goDoCommand("cmd_previousMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[0]);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+}
+
+/** Tests the next message/previous message commands in a message tab. */
+add_task(async function testNextPreviousMessageInATab() {
+ await withMessageInATab(folderAMessages[2], subtestNextPreviousMessage);
+});
+
+/** Tests the next message/previous message commands in a message window. */
+add_task(async function testNextPreviousMessageInAWindow() {
+ await withMessageInAWindow(folderAMessages[2], subtestNextPreviousMessage);
+});
+
+/** Tests the next unread message command. */
+add_task(async function testNextUnreadMessageInAbout3Pane() {
+ const aboutMessage = messageBrowser.contentWindow;
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+
+ folderA.markMessagesRead([folderAMessages[1], folderAMessages[3]], false);
+ folderC.markMessagesRead(
+ [folderCMessages[500], folderCMessages[501], folderCMessages[504]],
+ false
+ );
+ folderD.markMessagesRead(
+ [
+ folderDMessages[3],
+ folderDMessages[4],
+ folderDMessages[6],
+ folderDMessages[7],
+ ],
+ false
+ );
+
+ about3Pane.displayFolder(folderA.URI);
+ threadTree.selectedIndex = -1;
+ assertSelectedMessage();
+ await assertNoDisplayedMessage(aboutMessage);
+
+ // Select the first unread message.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ // Select the next unread message.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderAMessages[3]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[3]);
+
+ // Select the next unread message. Loops to start of folder.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ // Mark the message as read.
+ goDoCommand("cmd_markAsRead");
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ // Select the next unread message.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderAMessages[3]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[3]);
+
+ // Select the next unread message. Changes to the next folder.
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog("accept");
+ goDoCommand("cmd_nextUnreadMsg");
+ await dialogPromise;
+ await new Promise(resolve => setTimeout(resolve));
+ assertSelectedFolder(folderC);
+ assertSelectedMessage(folderCMessages[500]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ // Select the next unread message.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderCMessages[501]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[501]);
+
+ // Select the next unread message.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderCMessages[504]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[504]);
+
+ // Select the first message in folder D and make sure all threads are
+ // collapsed.
+ about3Pane.displayFolder(folderD.URI);
+ threadTree.selectedIndex = 0;
+ let selectPromise = BrowserTestUtils.waitForEvent(threadTree, "select");
+ goDoCommand("cmd_collapseAllThreads");
+ await selectPromise;
+ assertSelectedMessage(folderDMessages[0]);
+
+ // Go to the next thread without expanding it.
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertSelectedMessage(folderDMessages[3]);
+
+ // The next displayed message should be the root message of the now expanded
+ // thread.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderDMessages[3]);
+ await assertDisplayedMessage(aboutMessage, folderDMessages[3]);
+
+ // Select the next unread message in the thread.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderDMessages[4]);
+ await assertDisplayedMessage(aboutMessage, folderDMessages[4]);
+
+ // Select the next unread message.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderDMessages[6]);
+ await assertDisplayedMessage(aboutMessage, folderDMessages[6]);
+
+ // Select the next unread message.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedMessage(folderDMessages[7]);
+ await assertDisplayedMessage(aboutMessage, folderDMessages[7]);
+
+ // Mark folder D read again.
+ folderD.markAllMessagesRead(null);
+
+ // Go back to the first folder. The previous selection should be restored.
+ about3Pane.displayFolder(folderA.URI);
+ assertSelectedMessage(folderAMessages[3]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[3]);
+
+ // Select the next unread message. Changes to the next folder.
+ // The previous selection should NOT be restored.
+ dialogPromise = BrowserTestUtils.promiseAlertDialog("accept");
+ goDoCommand("cmd_nextUnreadMsg");
+ await dialogPromise;
+ await new Promise(resolve => setTimeout(resolve));
+ assertSelectedFolder(folderC);
+ assertSelectedMessage(folderCMessages[500]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ folderC.markAllMessagesRead(null);
+ // No more unread messages, prompt to move to the next folder.
+ // Cancel the prompt.
+ threadTree.addEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ dialogPromise = BrowserTestUtils.promiseAlertDialog("cancel");
+ goDoCommand("cmd_nextUnreadMsg");
+ await dialogPromise;
+ assertSelectedFolder(folderC);
+ assertSelectedMessage(folderCMessages[500]);
+
+ folderA.markAllMessagesRead(null);
+ // No unread messages anywhere, do nothing.
+ goDoCommand("cmd_nextUnreadMsg");
+ assertSelectedFolder(folderC);
+ assertSelectedMessage(folderCMessages[500]);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ threadTree.removeEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+
+ threadTree.selectedIndex = -1;
+ await assertNoDisplayedMessage(aboutMessage);
+});
+
+async function subtestNextUnreadMessage(win, aboutMessage) {
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+
+ folderA.markMessagesRead([folderAMessages[1], folderAMessages[3]], false);
+ folderC.markMessagesRead(
+ [folderCMessages[500], folderCMessages[501], folderCMessages[504]],
+ false
+ );
+ Assert.equal(folderC.getNumUnread(false), 3);
+
+ await assertDisplayedMessage(aboutMessage, folderAMessages[0]);
+
+ // Select the first unread message.
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ // Select the next unread message.
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[3]);
+
+ // Select the next unread message. Loops to start of folder.
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ // Mark the message as read.
+ win.goDoCommand("cmd_markAsRead");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ // Select the next unread message.
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[3]);
+
+ // Select the next unread message. Changes to the next folder.
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog("accept");
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await dialogPromise;
+ await new Promise(resolve => setTimeout(resolve));
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ // Select the next unread message.
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderCMessages[501]);
+
+ // Select the next unread message.
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderCMessages[504]);
+
+ folderC.markAllMessagesRead(null);
+ // No more unread messages, prompt to move to the next folder.
+ // Cancel the prompt.
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ dialogPromise = BrowserTestUtils.promiseAlertDialog("cancel");
+ win.goDoCommand("cmd_nextUnreadMsg");
+ await dialogPromise;
+
+ folderA.markAllMessagesRead(null);
+ // No unread messages anywhere, do nothing.
+ win.goDoCommand("cmd_nextUnreadMsg");
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+}
+
+/** Tests the next unread message command in a message tab. */
+add_task(async function testNextUnreadMessageInATab() {
+ await withMessageInATab(folderAMessages[0], subtestNextUnreadMessage);
+});
+
+/** Tests the next unread message command in a message window. */
+add_task(async function testNextUnreadMessageInAWindow() {
+ await withMessageInAWindow(folderAMessages[0], subtestNextUnreadMessage);
+});
+
+/** Tests the previous unread message command. This doesn't cross folders. */
+add_task(async function testPreviousUnreadMessageInAbout3Pane() {
+ const aboutMessage = messageBrowser.contentWindow;
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+
+ folderA.markMessagesRead([folderAMessages[1], folderAMessages[3]], false);
+ folderC.markMessagesRead(
+ [folderCMessages[500], folderCMessages[501], folderCMessages[504]],
+ false
+ );
+
+ about3Pane.displayFolder(folderC.URI);
+ threadTree.scrollToIndex(504, true);
+ // Ensure the scrolling from the previous line happens.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ threadTree.selectedIndex = 504;
+ assertSelectedMessage(folderCMessages[504]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[504]);
+
+ goDoCommand("cmd_previousUnreadMsg");
+ assertSelectedMessage(folderCMessages[501]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[501]);
+
+ goDoCommand("cmd_previousUnreadMsg");
+ assertSelectedMessage(folderCMessages[500]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ threadTree.addEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ goDoCommand("cmd_previousUnreadMsg");
+ assertSelectedMessage(folderCMessages[500]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ threadTree.removeEventListener("select", reportBadSelectEvent);
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+
+ threadTree.selectedIndex = -1;
+ await assertNoDisplayedMessage(aboutMessage);
+});
+
+async function subtestPreviousUnreadMessage(win, aboutMessage) {
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+
+ folderA.markMessagesRead([folderAMessages[1], folderAMessages[3]], false);
+ folderC.markMessagesRead(
+ [folderCMessages[500], folderCMessages[501], folderCMessages[504]],
+ false
+ );
+
+ await assertDisplayedMessage(aboutMessage, folderCMessages[504]);
+
+ win.goDoCommand("cmd_previousUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderCMessages[501]);
+
+ win.goDoCommand("cmd_previousUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ win.goDoCommand("cmd_previousUnreadMsg");
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+}
+
+/** Tests the previous unread message command in a message tab. */
+add_task(async function testPreviousUnreadMessageInATab() {
+ await withMessageInATab(folderCMessages[504], subtestPreviousUnreadMessage);
+});
+
+/** Tests the previous unread message command in a message window. */
+add_task(async function testPreviousUnreadMessageInAWindow() {
+ await withMessageInAWindow(
+ folderCMessages[504],
+ subtestPreviousUnreadMessage
+ );
+});
+
+/**
+ * Tests the next unread thread command. This command depends on marking the
+ * thread as read, despite mailnews.mark_message_read.auto being false in this
+ * test. Seems wrong, but it does make this test less complicated!
+ */
+add_task(async function testNextUnreadThreadInAbout3Pane() {
+ const aboutMessage = messageBrowser.contentWindow;
+
+ folderC.markMessagesRead(
+ [folderCMessages[500], folderCMessages[501], folderCMessages[504]],
+ false
+ );
+ folderD.markMessagesRead(
+ [
+ folderDMessages[0],
+ folderDMessages[1],
+ folderDMessages[2],
+ folderDMessages[8],
+ folderDMessages[9],
+ folderDMessages[10],
+ folderDMessages[11],
+ ],
+ false
+ );
+
+ // In folder C, there are no threads. Going to the next unread thread is the
+ // same as going to the next unread message. But as stated above, it does
+ // mark the current message as read.
+ about3Pane.displayFolder(folderC.URI);
+ threadTree.scrollToIndex(504, true);
+ // Ensure the scrolling from the previous line happens.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ threadTree.selectedIndex = 500;
+ assertSelectedMessage(folderCMessages[500]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ goDoCommand("cmd_nextUnreadThread");
+ assertSelectedMessage(folderCMessages[501]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[501]);
+
+ goDoCommand("cmd_nextUnreadThread");
+ assertSelectedMessage(folderCMessages[504]);
+ await assertDisplayedMessage(aboutMessage, folderCMessages[504]);
+
+ // No more unread messages, we'll move to folder D.
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog("accept");
+ goDoCommand("cmd_nextUnreadThread");
+ await dialogPromise;
+ assertSelectedFolder(folderD);
+ assertSelectedMessage(folderDMessages[0]);
+ await assertDisplayedMessage(aboutMessage, folderDMessages[0]);
+
+ goDoCommand("cmd_nextUnreadThread");
+ // The root message is read, we're looking at a single message in the thread.
+ assertSelectedMessage(folderDMessages[8]);
+ await assertDisplayedMessage(aboutMessage, folderDMessages[8]);
+
+ goDoCommand("cmd_nextUnreadThread");
+ // The root message is unread.
+ assertSelectedMessage(folderDMessages[9]);
+ await assertDisplayedMessage(aboutMessage, folderDMessages[9]);
+
+ // No more unread messages, prompt to move to the next folder.
+ // Cancel the prompt.
+ dialogPromise = BrowserTestUtils.promiseAlertDialog("cancel");
+ goDoCommand("cmd_nextUnreadThread");
+ await dialogPromise;
+ assertSelectedMessage(folderDMessages[9]);
+
+ threadTree.selectedIndex = -1;
+ await assertNoDisplayedMessage(aboutMessage);
+});
+
+async function subtestNextUnreadThread(win, aboutMessage) {
+ folderC.markMessagesRead(
+ [folderCMessages[500], folderCMessages[501], folderCMessages[504]],
+ false
+ );
+ folderD.markMessagesRead(
+ [
+ folderDMessages[0],
+ folderDMessages[1],
+ folderDMessages[2],
+ folderDMessages[8],
+ folderDMessages[9],
+ folderDMessages[10],
+ folderDMessages[11],
+ ],
+ false
+ );
+
+ await assertDisplayedMessage(aboutMessage, folderCMessages[500]);
+
+ win.goDoCommand("cmd_nextUnreadThread");
+ await assertDisplayedMessage(aboutMessage, folderCMessages[501]);
+
+ win.goDoCommand("cmd_nextUnreadThread");
+ await assertDisplayedMessage(aboutMessage, folderCMessages[504]);
+
+ // No more unread messages, we'll move to folder D.
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog("accept");
+ win.goDoCommand("cmd_nextUnreadThread");
+ await dialogPromise;
+ await assertDisplayedMessage(aboutMessage, folderDMessages[0]);
+
+ win.goDoCommand("cmd_nextUnreadThread");
+ // The root message is read, we're looking at a single message in the thread.
+ await assertDisplayedMessage(aboutMessage, folderDMessages[8]);
+
+ win.goDoCommand("cmd_nextUnreadThread");
+ // The root message is unread.
+ await assertDisplayedMessage(aboutMessage, folderDMessages[9]);
+
+ // No more unread messages, prompt to move to the next folder.
+ // Cancel the prompt.
+ dialogPromise = BrowserTestUtils.promiseAlertDialog("cancel");
+ win.goDoCommand("cmd_nextUnreadThread");
+ await dialogPromise;
+}
+
+/** Tests the next unread thread command in a message tab. */
+add_task(async function testNextUnreadThreadInATab() {
+ await withMessageInATab(folderCMessages[500], subtestNextUnreadThread);
+});
+
+/** Tests the next unread thread command in a message window. */
+add_task(async function testNextUnreadThreadInAWindow() {
+ await withMessageInAWindow(folderCMessages[500], subtestNextUnreadThread);
+});
+
+/** Tests that navigation with a closed message pane does not load messages. */
+add_task(async function testHiddenMessagePaneInAbout3Pane() {
+ const aboutMessage = messageBrowser.contentWindow;
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+
+ about3Pane.paneLayout.messagePaneVisible = false;
+ about3Pane.displayFolder(folderA.URI);
+ threadTree.selectedIndex = 0;
+ assertSelectedMessage(folderAMessages[0]);
+ await assertNoDisplayedMessage(aboutMessage);
+
+ messagePaneBrowser.addEventListener("load", reportBadLoad, true);
+ goDoCommand("cmd_nextMsg");
+ assertSelectedMessage(folderAMessages[1]);
+ await assertNoDisplayedMessage(aboutMessage);
+
+ goDoCommand("cmd_previousMsg");
+ assertSelectedMessage(folderAMessages[0]);
+ await assertNoDisplayedMessage(aboutMessage);
+
+ // Wait to prove bad things didn't happen.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ messagePaneBrowser.removeEventListener("load", reportBadLoad, true);
+
+ threadTree.selectedIndex = -1;
+ about3Pane.paneLayout.messagePaneVisible = true;
+});
+
+/** Tests the go back/forward commands. */
+add_task(async function testMessageHistoryInAbout3Pane() {
+ const aboutMessage = messageBrowser.contentWindow;
+ const { messageHistory } = aboutMessage;
+ messageHistory.clear();
+ about3Pane.displayFolder(folderA.URI);
+ threadTree.selectedIndex = 0;
+ assertSelectedMessage(folderAMessages[0]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[0]);
+
+ goDoCommand("cmd_nextMsg");
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ Assert.ok(messageHistory.canPop(-1), "Going back should be available");
+ Assert.ok(
+ !messageHistory.canPop(0),
+ "Should not be able to go back to the current message"
+ );
+ Assert.ok(
+ !messageHistory.canPop(1),
+ "Should not have any message to go forward to"
+ );
+ Assert.ok(
+ !window.getEnabledControllerForCommand("cmd_goForward"),
+ "Go forward should be disabled"
+ );
+
+ goDoCommand("cmd_goBack");
+ assertSelectedMessage(folderAMessages[0]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[0]);
+
+ Assert.ok(!messageHistory.canPop(-1), "Should have no message to go back to");
+ Assert.ok(messageHistory.canPop(1), "Should have a message to go forward to");
+ Assert.ok(
+ !window.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be disabled"
+ );
+
+ goDoCommand("cmd_goForward");
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(
+ !messageHistory.canPop(1),
+ "Should have no message to go forward to"
+ );
+ Assert.ok(
+ window.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be enabled"
+ );
+
+ // Switching folder to test going back/forward between folders.
+ about3Pane.displayFolder(folderB.URI);
+ threadTree.selectedIndex = 0;
+ assertSelectedMessage(folderBMessages[0]);
+ await assertDisplayedMessage(aboutMessage, folderBMessages[0]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(
+ !messageHistory.canPop(1),
+ "Should have no message to go forward to"
+ );
+ Assert.ok(
+ !window.getEnabledControllerForCommand("cmd_goForward"),
+ "Go forward should be disabled"
+ );
+
+ goDoCommand("cmd_goBack");
+
+ assertSelectedFolder(folderA);
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(messageHistory.canPop(1), "Should have a message to go forward to");
+ Assert.ok(
+ window.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be enabled"
+ );
+
+ goDoCommand("cmd_goForward");
+
+ assertSelectedFolder(folderB);
+ assertSelectedMessage(folderBMessages[0]);
+ await assertDisplayedMessage(aboutMessage, folderBMessages[0]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(
+ !messageHistory.canPop(1),
+ "Should have no message to go forward to"
+ );
+ Assert.ok(
+ !window.getEnabledControllerForCommand("cmd_goForward"),
+ "Go forward should be disabled"
+ );
+
+ goDoCommand("cmd_goBack");
+
+ assertSelectedFolder(folderA);
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(messageHistory.canPop(1), "Should have a message to go forward to");
+ Assert.ok(
+ window.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be enabled"
+ );
+
+ // Select a different message while going forward is possible, clearing the
+ // previous forward history.
+
+ goDoCommand("cmd_nextMsg");
+
+ assertSelectedMessage(folderAMessages[2]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[2]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(
+ !messageHistory.canPop(1),
+ "Should have no message to go forward to"
+ );
+ Assert.ok(
+ !window.getEnabledControllerForCommand("cmd_goForward"),
+ "Go forward should be disabled"
+ );
+
+ goDoCommand("cmd_goBack");
+
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(messageHistory.canPop(1), "Should have a message to go forward to");
+ Assert.ok(
+ window.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be enabled"
+ );
+
+ // Remove the previous message in the history from the folder it was
+ // displayed in.
+
+ let movedMessage = folderAMessages[0];
+ await moveMessage(folderA, movedMessage, folderB);
+
+ Assert.ok(!messageHistory.canPop(-1), "Should have no message to go back to");
+ Assert.ok(
+ !window.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be disabled"
+ );
+
+ // Display no message, so going back goes to the previously displayed message,
+ // which is also the current history entry.
+ threadTree.selectedIndex = -1;
+ await assertNoDisplayedMessage(aboutMessage);
+
+ Assert.ok(
+ messageHistory.canPop(0),
+ "Can go back to current history entry without selected message"
+ );
+ Assert.ok(
+ window.getEnabledControllerForCommand("cmd_goForward"),
+ "Go forward should be enabled"
+ );
+
+ goDoCommand("cmd_goBack");
+
+ assertSelectedMessage(folderAMessages[1]);
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ threadTree.selectedIndex = -1;
+ let currentFolderBMessages = [...folderB.messages];
+ movedMessage = currentFolderBMessages.find(
+ message => !folderBMessages.includes(message)
+ );
+ await moveMessage(folderB, movedMessage, folderA);
+ folderAMessages = [...folderA.messages];
+});
+
+async function subtestMessageHistory(win, aboutMessage) {
+ const { messageHistory } = aboutMessage;
+ await assertDisplayedMessage(aboutMessage, folderAMessages[0]);
+
+ Assert.ok(win.getEnabledControllerForCommand("cmd_nextMsg"));
+ win.goDoCommand("cmd_nextMsg");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ Assert.ok(messageHistory.canPop(-1), "Going back should be available");
+ Assert.ok(
+ !messageHistory.canPop(0),
+ "Should not be able to go back to the current message"
+ );
+ Assert.ok(
+ !messageHistory.canPop(1),
+ "Should not have any message to go forward to"
+ );
+ Assert.ok(
+ !win.getEnabledControllerForCommand("cmd_goForward"),
+ "Go forward should be disabled"
+ );
+
+ Assert.ok(win.getEnabledControllerForCommand("cmd_goBack"));
+ win.goDoCommand("cmd_goBack");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[0]);
+
+ Assert.ok(!messageHistory.canPop(-1), "Should have no message to go back to");
+ Assert.ok(messageHistory.canPop(1), "Should have a message to go forward to");
+ Assert.ok(
+ !win.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be disabled"
+ );
+
+ Assert.ok(win.getEnabledControllerForCommand("cmd_goForward"));
+ win.goDoCommand("cmd_goForward");
+ await assertDisplayedMessage(aboutMessage, folderAMessages[1]);
+
+ Assert.ok(messageHistory.canPop(-1), "Should have a message to go back to");
+ Assert.ok(
+ !messageHistory.canPop(1),
+ "Should have no message to go forward to"
+ );
+ Assert.ok(
+ win.getEnabledControllerForCommand("cmd_goBack"),
+ "Go back should be enabled"
+ );
+}
+
+/** Tests the go back/forward commands in a message tab. */
+add_task(async function testMessageHistoryInATab() {
+ await withMessageInATab(folderAMessages[0], subtestMessageHistory);
+});
+
+/** Tests the go back/forward commands in a message window. */
+add_task(async function testMessageHistoryInAWindow() {
+ await withMessageInAWindow(folderAMessages[0], subtestMessageHistory);
+});
+
+function assertSelectedFolder(expected) {
+ Assert.equal(about3Pane.gFolder.URI, expected.URI, "selected folder");
+}
+
+function assertSelectedMessage(expected, comment) {
+ if (expected) {
+ Assert.notEqual(
+ threadTree.selectedIndex,
+ -1,
+ "a message should be selected"
+ );
+ Assert.ok(
+ threadTree.getRowAtIndex(threadTree.selectedIndex),
+ "row for selected message should exist and be in view"
+ );
+ Assert.equal(
+ about3Pane.gDBView.getMsgHdrAt(threadTree.selectedIndex).messageId,
+ expected.messageId,
+ comment ?? "selected message"
+ );
+ } else {
+ Assert.equal(threadTree.selectedIndex, -1, "no message should be selected");
+ }
+}
+
+async function assertDisplayedMessage(aboutMessage, expected) {
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+ let mailboxURL = expected.folder.getUriForMsg(expected);
+ let messageURI = mailboxService.getUrlForUri(mailboxURL);
+
+ if (
+ messagePaneBrowser.webProgess?.isLoadingDocument ||
+ !messagePaneBrowser.currentURI.equals(messageURI)
+ ) {
+ await BrowserTestUtils.browserLoaded(
+ messagePaneBrowser,
+ undefined,
+ messageURI.spec
+ );
+ }
+ Assert.equal(
+ aboutMessage.gMessage.messageId,
+ expected.messageId,
+ "correct message loaded"
+ );
+ Assert.equal(
+ messagePaneBrowser.currentURI.spec,
+ messageURI.spec,
+ "correct message displayed"
+ );
+}
+
+async function assertDisplayedThread(firstMessage) {
+ let items = multiMessageBrowser.contentDocument.querySelectorAll("li");
+ Assert.equal(
+ items[0].dataset.messageId,
+ firstMessage.messageId,
+ "correct thread displayed"
+ );
+ Assert.ok(
+ BrowserTestUtils.is_visible(multiMessageBrowser),
+ "multimessageview visible"
+ );
+}
+
+async function assertNoDisplayedMessage(aboutMessage) {
+ const messagePaneBrowser = aboutMessage.getMessagePaneBrowser();
+ if (
+ messagePaneBrowser.webProgess?.isLoadingDocument ||
+ messagePaneBrowser.currentURI.spec != "about:blank"
+ ) {
+ await BrowserTestUtils.browserLoaded(
+ messagePaneBrowser,
+ undefined,
+ "about:blank"
+ );
+ }
+
+ Assert.equal(aboutMessage.gMessage, null, "no message loaded");
+ Assert.equal(
+ messagePaneBrowser.currentURI.spec,
+ "about:blank",
+ "no message displayed"
+ );
+ Assert.ok(BrowserTestUtils.is_hidden(messageBrowser), "about:message hidden");
+}
+
+function reportBadSelectEvent() {
+ Assert.report(
+ true,
+ undefined,
+ undefined,
+ "should not have fired a select event"
+ );
+}
+
+function reportBadLoad() {
+ Assert.report(
+ true,
+ undefined,
+ undefined,
+ "should not have reloaded the message"
+ );
+}
+
+function moveMessage(sourceFolder, message, targetFolder) {
+ let copyListener = new PromiseTestUtils.PromiseCopyListener();
+ MailServices.copy.copyMessages(
+ sourceFolder,
+ [message],
+ targetFolder,
+ true,
+ copyListener,
+ window.msgWindow,
+ true
+ );
+ return copyListener.promise;
+}
+
+async function withMessageInATab(message, subtest) {
+ let tabPromise = BrowserTestUtils.waitForEvent(window, "MsgLoaded");
+ window.OpenMessageInNewTab(message, { background: false });
+ await tabPromise;
+ await new Promise(resolve => setTimeout(resolve));
+
+ await subtest(window, tabmail.currentAboutMessage);
+
+ tabmail.closeOtherTabs(0);
+}
+
+async function withMessageInAWindow(message, subtest) {
+ let winPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
+ window.MsgOpenNewWindowForMessage(message);
+ let win = await winPromise;
+ await BrowserTestUtils.waitForEvent(win, "MsgLoaded");
+ await TestUtils.waitForCondition(() => Services.focus.activeWindow == win);
+
+ await subtest(win, win.messageBrowser.contentWindow);
+
+ await BrowserTestUtils.closeWindow(win);
+}