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/test/browser/tabmail/browser_closing.js | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.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 'comm/mail/test/browser/tabmail/browser_closing.js')
-rw-r--r-- | comm/mail/test/browser/tabmail/browser_closing.js | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/comm/mail/test/browser/tabmail/browser_closing.js b/comm/mail/test/browser/tabmail/browser_closing.js new file mode 100644 index 0000000000..b0caa65bc0 --- /dev/null +++ b/comm/mail/test/browser/tabmail/browser_closing.js @@ -0,0 +1,407 @@ +/* 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 tabmail behaviour when tabs close. + */ + +"use strict"; + +var { + assert_selected_tab, + be_in_folder, + collapse_all_threads, + create_folder, + make_display_threaded, + make_message_sets_in_folders, + mc, + open_selected_message_in_new_tab, + open_selected_messages, + select_click_row, + switch_tab, +} = ChromeUtils.import( + "resource://testing-common/mozmill/FolderDisplayHelpers.jsm" +); + +var gFolder; + +var MSGS_PER_THREAD = 3; + +add_setup(async function () { + gFolder = await create_folder("test-tabmail-closing folder"); + await make_message_sets_in_folders( + [gFolder], + [{ msgsPerThread: MSGS_PER_THREAD }] + ); +}); + +/** + * Test that if we open up a message in a tab from the inbox tab, that + * if we immediately close that tab, we switch back to the inbox tab. + */ +add_task(async function test_closed_single_message_tab_returns_to_inbox() { + await be_in_folder(gFolder); + make_display_threaded(); + let inboxTab = mc.window.document.getElementById("tabmail").currentTabInfo; + + select_click_row(0); + // Open a message in a new tab... + await open_selected_message_in_new_tab(false); + + // Open a second message in a new tab... + await switch_tab(0); + select_click_row(1); + await open_selected_message_in_new_tab(false); + + // Close the second tab + mc.window.document.getElementById("tabmail").closeTab(2); + + // We should have gone back to the inbox tab + assert_selected_tab(inboxTab); + + // Close the first tab + mc.window.document.getElementById("tabmail").closeTab(1); +}); + +/** + * Test that if we open up some message tabs from the inbox tab, and then + * switch around in those tabs, closing the tabs doesn't immediately jump + * you back to the inbox tab. + */ +add_task(async function test_does_not_go_to_opener_if_switched() { + await be_in_folder(gFolder); + make_display_threaded(); + + select_click_row(0); + // Open a message in a new tab... + await open_selected_message_in_new_tab(false); + + // Open a second message in a new tab... + await switch_tab(0); + select_click_row(1); + await open_selected_message_in_new_tab(false); + + // Switch to the first tab + await switch_tab(1); + let firstTab = mc.window.document.getElementById("tabmail").currentTabInfo; + + // Switch back to the second tab + await switch_tab(2); + + // Close the second tab + mc.window.document.getElementById("tabmail").closeTab(2); + + // We should have gone back to the second tab + assert_selected_tab(firstTab); + + // Close the first tab + mc.window.document.getElementById("tabmail").closeTab(1); +}); + +/** + * Test that if we open a whole thread up in message tabs, closing + * the last message tab takes us to the second last message tab as opposed + * to the inbox tab. + */ +add_task(async function test_opening_thread_in_tabs_closing_behaviour() { + await be_in_folder(gFolder); + make_display_threaded(); + collapse_all_threads(); + + // Open a thread as a series of message tabs. + select_click_row(0); + open_selected_messages(mc); + + // At this point, the last message tab should be selected already. We + // close that tab, and the second last message tab should be selected. + // We should close that tab, and the third last tab should be selected, + // etc. + for (let i = MSGS_PER_THREAD; i > 0; --i) { + let previousTab = mc.window.document + .getElementById("tabmail") + .tabContainer.getItemAtIndex(i - 1); + mc.window.document.getElementById("tabmail").closeTab(i); + Assert.equal( + previousTab, + mc.window.document.getElementById("tabmail").tabContainer.selectedItem, + "Expected tab at index " + (i - 1) + " to be selected." + ); + } +}).skip(); + +/** + * @typedef {object} TestTab + * @property {Element} node - The tab's DOM node. + * @property {number} index - The tab's index. + * @property {object} info - The tabInfo for this tab, as used in #tabmail. + */ + +/** + * Open some message tabs in the background from the folder tab. + * + * @param {number} numAdd - The number of tabs to add. + * + * @param {TestTab[]} An array of tab objects corresponding to all the open + * tabs. + */ +async function openTabs(numAdd) { + await be_in_folder(gFolder); + select_click_row(0); + for (let i = 0; i < numAdd; i++) { + await open_selected_message_in_new_tab(true); + } + let tabs = mc.window.document + .getElementById("tabmail") + .tabInfo.map((info, index) => { + return { + info, + index, + node: info.tabNode, + }; + }); + Assert.equal(tabs.length, numAdd + 1, "Have expected number of tabs"); + return tabs; +} + +/** + * Assert that a tab is closed. + * + * @param {TestTab} fromTab - The tab to close from. + * @param {Function} closeMethod - The (async) method to call on fromTab.node in + * order to perform the tab close. + * @param {TestTab} switchToTab - The tab we expect to switch to after closing + * tab. + * @param {TestTab[]} [closingTabs] - The tabs we expect to close after calling + * the closeMethod. This is just fromTab by default. + */ +async function assertClose(fromTab, closeMethod, switchToTab, closingTabs) { + let desc; + if (closingTabs) { + let closingIndices = closingTabs.map(t => t.index).join(","); + desc = `closing tab #${closingIndices} using tab #${fromTab.index}`; + } else { + closingTabs = [fromTab]; + desc = `closing tab #${fromTab.index}`; + } + let numTabsBefore = + mc.window.document.getElementById("tabmail").tabInfo.length; + for (let tab of closingTabs) { + Assert.ok( + tab.node.parentNode, + `tab #${tab.index} should be in the DOM tree before ${desc}` + ); + } + fromTab.node.scrollIntoView(); + await closeMethod(fromTab.node); + for (let tab of closingTabs) { + Assert.ok( + !tab.node.parentNode, + `tab #${tab.index} should be removed from the DOM tree after ${desc}` + ); + } + Assert.equal( + mc.window.document.getElementById("tabmail").tabInfo.length, + numTabsBefore - closingTabs.length, + `Number of tabs after ${desc}` + ); + assert_selected_tab( + switchToTab.info, + `tab #${switchToTab.index} is selected after ${desc}` + ); +} + +/** + * Close a tab using its close button. + * + * @param {Element} tab - The tab to close. + */ +function closeWithButton(tab) { + EventUtils.synthesizeMouseAtCenter( + tab.querySelector(".tab-close-button"), + {}, + tab.ownerGlobal + ); +} + +/** + * Close a tab using a middle mouse click. + * + * @param {Element} tab - The tab to close. + */ +function closeWithMiddleClick(tab) { + EventUtils.synthesizeMouseAtCenter(tab, { button: 1 }, tab.ownerGlobal); +} + +/** + * Close the currently selected tab. + * + * @param {Element} tab - The tab to close. + */ +function closeWithKeyboard(tab) { + if (AppConstants.platform == "macosx") { + EventUtils.synthesizeKey("w", { accelKey: true }, tab.ownerGlobal); + } else { + EventUtils.synthesizeKey("w", { ctrlKey: true }, tab.ownerGlobal); + } +} + +/** + * Open the context menu of a tab. + * + * @param {Element} tab - The tab to open the context menu of. + */ +async function openContextMenu(tab) { + let win = tab.ownerGlobal; + let contextMenu = win.document.getElementById("tabContextMenu"); + let shownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + EventUtils.synthesizeMouseAtCenter( + tab, + { type: "contextmenu", button: 2 }, + win + ); + await shownPromise; +} + +/** + * Close the context menu, without selecting anything. + * + * @param {Element} tab - The tab to close the context menu of. + */ +async function closeContextMenu(tab) { + let win = tab.ownerGlobal; + let contextMenu = win.document.getElementById("tabContextMenu"); + let hiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden"); + contextMenu.hidePopup(); + await hiddenPromise; +} + +/** + * Open a tab's context menu and select an item. + * + * @param {Element} tab - The tab to open the context menu on. + * @param {string} itemId - The id of the menu item to select. + */ +async function selectFromContextMenu(tab, itemId) { + let doc = tab.ownerDocument; + let contextMenu = doc.getElementById("tabContextMenu"); + let item = doc.getElementById(itemId); + await openContextMenu(tab); + let hiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden"); + contextMenu.activateItem(item); + await hiddenPromise; +} + +/** + * Close a tab using its context menu. + * + * @param {Element} tab - The tab to close. + */ +async function closeWithContextMenu(tab) { + await selectFromContextMenu(tab, "tabContextMenuClose"); +} + +/** + * Close all other tabs using a tab's context menu. + * + * @param {Element} tab - The tab to not close. + */ +async function closeOtherTabsWithContextMenu(tab) { + await selectFromContextMenu(tab, "tabContextMenuCloseOtherTabs"); +} + +/** + * Test closing unselected tabs with the mouse or keyboard. + */ +add_task(async function test_close_unselected_tab_methods() { + let tabs = await openTabs(3); + + // Can't close the first tab. + Assert.ok( + BrowserTestUtils.is_hidden(tabs[0].node.querySelector(".tab-close-button")), + "Close button should be hidden for the first tab" + ); + // Middle click does nothing. + closeWithMiddleClick(tabs[0].node); + assert_selected_tab(tabs[0].info); + // Keyboard shortcut does nothing. + closeWithKeyboard(tabs[0].node); + assert_selected_tab(tabs[0].info); + // Context close item is disabled. + await openContextMenu(tabs[0].node); + Assert.ok( + mc.window.document.getElementById("tabContextMenuClose").disabled, + "Close context menu item should be disabled for the first tab" + ); + await closeContextMenu(tabs[0].node); + + // Close unselected tabs. The selected tab should stay the same. + await assertClose(tabs[3], closeWithButton, tabs[0]); + await assertClose(tabs[1], closeWithMiddleClick, tabs[0]); + await assertClose(tabs[2], closeWithContextMenu, tabs[0]); + // Keyboard shortcut cannot be used to close an unselected tab. +}); + +/** + * Test closing selected tabs with the mouse or keyboard. + */ +add_task(async function test_close_selected_tab_methods() { + let tabs = await openTabs(4); + + // Select tab by clicking it. + EventUtils.synthesizeMouseAtCenter(tabs[4].node, {}, mc.window); + assert_selected_tab(tabs[4].info); + await assertClose(tabs[4], closeWithButton, tabs[3]); + + // Select tab #2 by clicking tab #3 and using the shortcut to go back. + EventUtils.synthesizeMouseAtCenter(tabs[3].node, {}, mc.window); + assert_selected_tab(tabs[3].info); + EventUtils.synthesizeKey( + "VK_TAB", + { ctrlKey: true, shiftKey: true }, + mc.window + ); + assert_selected_tab(tabs[2].info); + await assertClose(tabs[2], closeWithKeyboard, tabs[3]); + + // Note: Current open tabs is: #0, #1, #2, #3. + + // Select tab #1 by using the shortcut to go forward from tab #0. + EventUtils.synthesizeMouseAtCenter(tabs[0].node, {}, mc.window); + assert_selected_tab(tabs[0].info); + EventUtils.synthesizeKey("VK_TAB", { ctrlKey: true }, mc.window); + assert_selected_tab(tabs[1].info); + await assertClose(tabs[1], closeWithMiddleClick, tabs[3]); + + // Note: Current open tabs is: #0, #3. + // Close tabs #3 using the context menu. + await assertClose(tabs[3], closeWithContextMenu, tabs[0]); +}); + +/** + * Test closing other tabs with the context menu. + */ +add_task(async function test_close_other_tabs() { + let tabs = await openTabs(3); + + EventUtils.synthesizeMouseAtCenter(tabs[3].node, {}, mc.window); + assert_selected_tab(tabs[3].info); + // Close tabs #1 and #2 using the context menu of #3. + await assertClose(tabs[3], closeOtherTabsWithContextMenu, tabs[3], [ + tabs[1], + tabs[2], + ]); + + // Note: Current open tabs is: #0 #3. + // The tab #3 closeOtherItem is now disabled since only tab #0 is left, which + // cannot be closed. + await openContextMenu(tabs[3].node); + Assert.ok( + mc.window.document.getElementById("tabContextMenuCloseOtherTabs").disabled, + "Close context menu item should be disabled for the first tab" + ); + await closeContextMenu(tabs[3].node); + + // But we can close tab #3 using tab #0 context menu. + await assertClose(tabs[0], closeOtherTabsWithContextMenu, tabs[0], [tabs[3]]); +}); |