diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mail/base/test/browser/browser_threadTreeSorting.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | comm/mail/base/test/browser/browser_threadTreeSorting.js | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/comm/mail/base/test/browser/browser_threadTreeSorting.js b/comm/mail/base/test/browser/browser_threadTreeSorting.js new file mode 100644 index 0000000000..be3bf0f2eb --- /dev/null +++ b/comm/mail/base/test/browser/browser_threadTreeSorting.js @@ -0,0 +1,344 @@ +/* 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" +); + +let tabmail = document.getElementById("tabmail"); +let about3Pane = tabmail.currentAbout3Pane; +let { sortController, threadTree } = about3Pane; +let rootFolder, testFolder, sourceMessageIDs; +let menuHelper = new MenuTestHelper("menu_View"); + +add_setup(async function () { + Services.prefs.setBoolPref("mailnews.scroll_to_new_message", false); + let generator = new MessageGenerator(); + + MailServices.accounts.createLocalMailAccount(); + let account = MailServices.accounts.accounts[0]; + account.addIdentity(MailServices.accounts.createIdentity()); + rootFolder = account.incomingServer.rootFolder.QueryInterface( + Ci.nsIMsgLocalMailFolder + ); + + testFolder = rootFolder + .createLocalSubfolder("threadTreeSort") + .QueryInterface(Ci.nsIMsgLocalMailFolder); + testFolder.addMessageBatch( + generator + .makeMessages({ count: 320 }) + .map(message => message.toMboxString()) + ); + + about3Pane.restoreState({ + messagePaneVisible: false, + folderURI: testFolder.URI, + }); + await new Promise(resolve => requestAnimationFrame(resolve)); + + document.getElementById("toolbar-menubar").removeAttribute("autohide"); + + registerCleanupFunction(async () => { + await ensure_cards_view(); + MailServices.accounts.removeAccount(account, false); + Services.prefs.setBoolPref("mailnews.scroll_to_new_message", true); + }); +}); + +add_task(async function () { + const messagesByDate = [...testFolder.messages]; + const messagesBySubject = messagesByDate + .slice() + .sort((m1, m2) => (m1.subject < m2.subject ? -1 : 1)); + const dateHeaderButton = about3Pane.document.getElementById("dateCol"); + const subjectHeaderButton = about3Pane.document.getElementById("subjectCol"); + + // Sanity check. + + Assert.equal( + about3Pane.gViewWrapper.primarySortType, + Ci.nsMsgViewSortType.byDate, + "initial sort column should be byDate" + ); + Assert.equal( + about3Pane.gViewWrapper.primarySortOrder, + Ci.nsMsgViewSortOrder.ascending, + "initial sort order should be ascending" + ); + Assert.ok( + about3Pane.gViewWrapper.showThreaded, + "initial mode should be threaded" + ); + Assert.equal( + threadTree.view.rowCount, + 320, + "correct number of rows in the view" + ); + Assert.equal( + threadTree.getLastVisibleIndex(), + 319, + "should be scrolled to the bottom" + ); + + Assert.equal(getCardActualSubject(320 - 1), messagesByDate.at(-1).subject); + Assert.equal(getCardActualSubject(320 - 2), messagesByDate.at(-2).subject); + Assert.equal(getCardActualSubject(320 - 3), messagesByDate.at(-3).subject); + + // Switch to horizontal layout and table view so we can interact with the + // table header and sort rows properly. + await ensure_table_view(); + + // Check sorting with no message selected. + + await clickHeader(dateHeaderButton, "byDate", "descending"); + Assert.equal( + threadTree.view.rowCount, + 320, + "correct number of rows in the view" + ); + Assert.equal( + threadTree.getFirstVisibleIndex(), + 0, + "should be scrolled to the top" + ); + + Assert.equal(getActualSubject(0), messagesByDate.at(-1).subject); + Assert.equal(getActualSubject(1), messagesByDate.at(-2).subject); + Assert.equal(getActualSubject(2), messagesByDate.at(-3).subject); + + await clickHeader(dateHeaderButton, "byDate", "ascending"); + Assert.equal( + threadTree.view.rowCount, + 320, + "correct number of rows in the view" + ); + Assert.equal( + threadTree.getLastVisibleIndex(), + 319, + "should be scrolled to the bottom" + ); + + Assert.equal(getActualSubject(320 - 1), messagesByDate.at(-1).subject); + Assert.equal(getActualSubject(320 - 2), messagesByDate.at(-2).subject); + Assert.equal(getActualSubject(320 - 3), messagesByDate.at(-3).subject); + + // Select a message and check the selection remains after sorting. + + const targetMessage = messagesByDate[49]; + info(`selecting message "${targetMessage.subject}"`); + threadTree.scrollToIndex(49, true); + await new Promise(resolve => requestAnimationFrame(resolve)); + threadTree.selectedIndex = 49; + verifySelection([49], [targetMessage.subject]); + + await clickHeader(dateHeaderButton, "byDate", "descending"); + verifySelection([319 - 49], [targetMessage.subject], { + where: "last", + }); + + await clickHeader(dateHeaderButton, "byDate", "ascending"); + verifySelection([49], [targetMessage.subject], { where: "first" }); + + const targetIndexBySubject = messagesBySubject.indexOf(targetMessage); + + // Switch columns. + + await clickHeader(subjectHeaderButton, "bySubject", "ascending"); + verifySelection([targetIndexBySubject], [targetMessage.subject]); + + await clickHeader(subjectHeaderButton, "bySubject", "descending"); + verifySelection([319 - targetIndexBySubject], [targetMessage.subject]); + + await clickHeader(subjectHeaderButton, "bySubject", "ascending"); + verifySelection([targetIndexBySubject], [targetMessage.subject]); + + // Switch back again. + + await clickHeader(dateHeaderButton, "byDate", "ascending"); + verifySelection([49], [targetMessage.subject], { where: "first" }); + + // Select multiple messages, two adjacent to each other, one non-adjacent, + // and check the selection remains after sorting. + + const targetMessages = [ + messagesByDate[80], + messagesByDate[81], + messagesByDate[83], + ]; + info( + `selecting messages "${targetMessages.map(m => m.subject).join('", "')}"` + ); + threadTree.scrollToIndex(83, true); + await new Promise(resolve => requestAnimationFrame(resolve)); + threadTree.selectedIndices = [80, 81, 83]; + verifySelection( + [80, 81, 83], + [ + targetMessages[0].subject, + targetMessages[1].subject, + targetMessages[2].subject, + ], + { currentIndex: 83 } + ); + + await clickHeader(dateHeaderButton, "byDate", "descending"); + verifySelection( + [319 - 83, 319 - 81, 319 - 80], + [ + targetMessages[2].subject, + // Rows for these two messages probably don't exist yet. + // targetMessages[1].subject, + // targetMessages[0].subject, + ], + { where: "last" } + ); + + await clickHeader(dateHeaderButton, "byDate", "ascending"); + verifySelection( + [80, 81, 83], + [ + // Rows for these two messages probably don't exist yet. + undefined, // targetMessages[0].subject, + undefined, // targetMessages[1].subject, + targetMessages[2].subject, + ], + { currentIndex: 83, where: "first" } + ); +}); + +async function clickHeader(header, type, order) { + info(`sorting ${type} ${order}`); + const button = header.querySelector("button"); + + let scrollEvents = 0; + let listener = () => scrollEvents++; + + threadTree.addEventListener("scroll", listener); + EventUtils.synthesizeMouseAtCenter(button, {}, about3Pane); + + // Wait long enough that any more scrolling would trigger more events. + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(resolve => setTimeout(resolve, 100)); + threadTree.removeEventListener("scroll", listener); + Assert.lessOrEqual(scrollEvents, 1, "only one scroll event should fire"); + + Assert.equal( + about3Pane.gViewWrapper.primarySortType, + Ci.nsMsgViewSortType[type], + `sort type should be ${type}` + ); + Assert.equal( + about3Pane.gViewWrapper.primarySortOrder, + Ci.nsMsgViewSortOrder[order], + `sort order should be ${order}` + ); + Assert.ok(about3Pane.gViewWrapper.showThreaded, "mode should be threaded"); + + Assert.ok( + button.classList.contains("sorting"), + "header button should have the sorted class" + ); + Assert.equal( + button.classList.contains("ascending"), + order == "ascending", + `header button ${ + order == "ascending" ? "should" : "should not" + } have the ascending class` + ); + Assert.equal( + button.classList.contains("descending"), + order == "descending", + `header button ${ + order == "descending" ? "should" : "should not" + } have the descending class` + ); + + Assert.equal( + threadTree.table.header.querySelectorAll( + ".sorting, .ascending, .descending" + ).length, + 1, + "no other header buttons should have sorting classes" + ); + + if (AppConstants.platform != "macosx") { + await menuHelper.testItems({ + viewSortMenu: {}, + sortByDateMenuitem: { checked: type == "byDate" }, + sortBySubjectMenuitem: { checked: type == "bySubject" }, + sortAscending: { checked: order == "ascending" }, + sortDescending: { checked: order == "descending" }, + sortThreaded: { checked: true }, + sortUnthreaded: {}, + groupBySort: {}, + }); + } +} + +function verifySelection( + selectedIndices, + subjects, + { currentIndex, where } = {} +) { + if (currentIndex === undefined) { + currentIndex = selectedIndices[0]; + } + + Assert.deepEqual( + threadTree.selectedIndices, + selectedIndices, + "selectedIndices" + ); + Assert.equal(threadTree.currentIndex, currentIndex, "currentIndex"); + if (where == "first") { + Assert.equal( + threadTree.getFirstVisibleIndex(), + currentIndex, + "currentIndex should be first" + ); + } else { + Assert.lessOrEqual( + threadTree.getFirstVisibleIndex(), + currentIndex, + "currentIndex should be at or below first" + ); + } + if (where == "last") { + Assert.equal( + threadTree.getLastVisibleIndex(), + currentIndex, + "currentIndex should be last" + ); + } else { + Assert.greaterOrEqual( + threadTree.getLastVisibleIndex(), + currentIndex, + "currentIndex should be at or above last" + ); + } + for (let i = 0; i < subjects.length; i++) { + if (subjects[i]) { + Assert.equal(getActualSubject(selectedIndices[i]), subjects[i]); + } + } +} + +function getCardActualSubject(index) { + let row = threadTree.getRowAtIndex(index); + return row.querySelector(".thread-card-subject-container > .subject") + .textContent; +} + +function getActualSubject(index) { + let row = threadTree.getRowAtIndex(index); + return row.querySelector(".subject-line > span").textContent; +} + +function getActualSubjects() { + return Array.from( + threadTree.table.body.rows, + row => row.querySelector(".subject-line > span").textContent + ); +} |