diff options
Diffstat (limited to '')
7 files changed, 704 insertions, 0 deletions
diff --git a/browser/base/content/test/sidebar/browser.toml b/browser/base/content/test/sidebar/browser.toml new file mode 100644 index 0000000000..2e7e7c03c4 --- /dev/null +++ b/browser/base/content/test/sidebar/browser.toml @@ -0,0 +1,13 @@ +[DEFAULT] + +["browser_sidebar_adopt.js"] + +["browser_sidebar_app_locale_changed.js"] + +["browser_sidebar_keys.js"] + +["browser_sidebar_move.js"] + +["browser_sidebar_persist.js"] + +["browser_sidebar_switcher.js"] diff --git a/browser/base/content/test/sidebar/browser_sidebar_adopt.js b/browser/base/content/test/sidebar/browser_sidebar_adopt.js new file mode 100644 index 0000000000..344a71cb9b --- /dev/null +++ b/browser/base/content/test/sidebar/browser_sidebar_adopt.js @@ -0,0 +1,74 @@ +/* This test checks that the SidebarFocused event doesn't fire in adopted + * windows when the sidebar gets opened during window opening, to make sure + * that sidebars don't steal focus from the page in this case (Bug 1394207). + * There's another case not covered here that has the same expected behavior - + * during the initial browser startup - but it would be hard to do with a mochitest. */ + +registerCleanupFunction(() => { + SidebarUI.hide(); +}); + +function failIfSidebarFocusedFires() { + ok(false, "This event shouldn't have fired"); +} + +add_setup(function () { + CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar"); + registerCleanupFunction(() => + CustomizableUI.removeWidgetFromArea("sidebar-button") + ); +}); + +add_task(async function testAdoptedTwoWindows() { + // First open a new window, show the sidebar in that window, and close it. + // Then, open another new window and confirm that the sidebar is closed since it is + // being adopted from the main window which doesn't have a shown sidebar. See Bug 1407737. + info("Ensure that sidebar state is adopted only from the opener"); + + let win1 = await BrowserTestUtils.openNewBrowserWindow(); + await win1.SidebarUI.show("viewBookmarksSidebar"); + await BrowserTestUtils.closeWindow(win1); + + let win2 = await BrowserTestUtils.openNewBrowserWindow(); + ok( + !win2.document.getElementById("sidebar-button").hasAttribute("checked"), + "Sidebar button isn't checked" + ); + ok(!win2.SidebarUI.isOpen, "Sidebar is closed"); + await BrowserTestUtils.closeWindow(win2); +}); + +add_task(async function testEventsReceivedInMainWindow() { + info( + "Opening the sidebar and expecting both SidebarShown and SidebarFocused events" + ); + + let initialShown = BrowserTestUtils.waitForEvent(window, "SidebarShown"); + let initialFocus = BrowserTestUtils.waitForEvent(window, "SidebarFocused"); + + await SidebarUI.show("viewBookmarksSidebar"); + await initialShown; + await initialFocus; + + ok(true, "SidebarShown and SidebarFocused events fired on a new window"); +}); + +add_task(async function testEventReceivedInNewWindow() { + info( + "Opening a new window and expecting the SidebarFocused event to not fire" + ); + + let promiseNewWindow = BrowserTestUtils.waitForNewWindow(); + let win = OpenBrowserWindow(); + + let adoptedShown = BrowserTestUtils.waitForEvent(win, "SidebarShown"); + win.addEventListener("SidebarFocused", failIfSidebarFocusedFires); + registerCleanupFunction(async function () { + win.removeEventListener("SidebarFocused", failIfSidebarFocusedFires); + await BrowserTestUtils.closeWindow(win); + }); + + await promiseNewWindow; + await adoptedShown; + ok(true, "SidebarShown event fired on an adopted window"); +}); diff --git a/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js b/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js new file mode 100644 index 0000000000..5b07da9839 --- /dev/null +++ b/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js @@ -0,0 +1,111 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This file tests that the sidebar recreates the contents of the <tree> element + * for live app locale switching. + */ + +add_task(function cleanup() { + registerCleanupFunction(() => { + SidebarUI.hide(); + }); +}); + +/** + * @param {string} sidebarName + */ +async function testLiveReloading(sidebarName) { + info("Showing the sidebar " + sidebarName); + await SidebarUI.show(sidebarName); + + function getTreeChildren() { + const sidebarDoc = + document.querySelector("#sidebar").contentWindow.document; + return sidebarDoc.querySelector(".sidebar-placesTreechildren"); + } + + const childrenBefore = getTreeChildren(); + ok(childrenBefore, "Found the sidebar children"); + is(childrenBefore, getTreeChildren(), "The children start out as equal"); + + info("Simulating an app locale change."); + Services.obs.notifyObservers(null, "intl:app-locales-changed"); + + await TestUtils.waitForCondition( + getTreeChildren, + "Waiting for a new child tree element." + ); + + isnot( + childrenBefore, + getTreeChildren(), + "The tree's contents are re-computed." + ); + + info("Hiding the sidebar"); + SidebarUI.hide(); +} + +add_task(async function test_bookmarks_sidebar() { + await testLiveReloading("viewBookmarksSidebar"); +}); + +add_task(async function test_history_sidebar() { + await testLiveReloading("viewHistorySidebar"); +}); + +add_task(async function test_ext_sidebar_panel_reloaded_on_locale_changes() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + sidebar_action: { + default_panel: "sidebar.html", + }, + }, + useAddonManager: "temporary", + + files: { + "sidebar.html": `<html> + <head> + <meta charset="utf-8"/> + <script src="sidebar.js"></script> + </head> + <body> + A Test Sidebar + </body> + </html>`, + "sidebar.js": function () { + const { browser } = this; + window.onload = () => { + browser.test.sendMessage("sidebar"); + }; + }, + }, + }); + await extension.startup(); + // Test sidebar is opened on install + await extension.awaitMessage("sidebar"); + + // Test sidebar is opened on simulated locale changes. + info("Switch browser to bidi and expect the sidebar panel to be reloaded"); + + await SpecialPowers.pushPrefEnv({ + set: [["intl.l10n.pseudo", "bidi"]], + }); + await extension.awaitMessage("sidebar"); + is( + window.document.documentElement.getAttribute("dir"), + "rtl", + "browser window changed direction to rtl as expected" + ); + + await SpecialPowers.popPrefEnv(); + await extension.awaitMessage("sidebar"); + is( + window.document.documentElement.getAttribute("dir"), + "ltr", + "browser window changed direction to ltr as expected" + ); + + await extension.unload(); +}); diff --git a/browser/base/content/test/sidebar/browser_sidebar_keys.js b/browser/base/content/test/sidebar/browser_sidebar_keys.js new file mode 100644 index 0000000000..f12d1cf5f7 --- /dev/null +++ b/browser/base/content/test/sidebar/browser_sidebar_keys.js @@ -0,0 +1,108 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +async function testSidebarKeyToggle(key, options, expectedSidebarId) { + EventUtils.synthesizeMouseAtCenter(gURLBar.textbox, {}); + let promiseShown = BrowserTestUtils.waitForEvent(window, "SidebarShown"); + EventUtils.synthesizeKey(key, options); + await promiseShown; + Assert.equal( + document.getElementById("sidebar-box").getAttribute("sidebarcommand"), + expectedSidebarId + ); + EventUtils.synthesizeKey(key, options); + Assert.ok(!SidebarUI.isOpen); +} + +add_task(async function test_sidebar_keys() { + registerCleanupFunction(() => SidebarUI.hide()); + + await testSidebarKeyToggle("b", { accelKey: true }, "viewBookmarksSidebar"); + + let options = { accelKey: true, shiftKey: AppConstants.platform == "macosx" }; + await testSidebarKeyToggle("h", options, "viewHistorySidebar"); +}); + +add_task(async function test_sidebar_in_customize_mode() { + // Test bug 1756385 - widgets to appear unchecked in customize mode. Test that + // the sidebar button widget doesn't appear checked, and that the sidebar + // button toggle is inert while in customize mode. + let { CustomizableUI } = ChromeUtils.importESModule( + "resource:///modules/CustomizableUI.sys.mjs" + ); + registerCleanupFunction(() => SidebarUI.hide()); + + let placement = CustomizableUI.getPlacementOfWidget("sidebar-button"); + if (!(placement?.area == CustomizableUI.AREA_NAVBAR)) { + CustomizableUI.addWidgetToArea( + "sidebar-button", + CustomizableUI.AREA_NAVBAR, + 0 + ); + CustomizableUI.ensureWidgetPlacedInWindow("sidebar-button", window); + registerCleanupFunction(function () { + CustomizableUI.removeWidgetFromArea("sidebar-button"); + }); + } + + let widgetIcon = CustomizableUI.getWidget("sidebar-button") + .forWindow(window) + .node?.querySelector(".toolbarbutton-icon"); + // Get the alpha value of the sidebar toggle widget's background + let getBGAlpha = () => + InspectorUtils.colorToRGBA( + getComputedStyle(widgetIcon).getPropertyValue("background-color") + ).a; + + let promiseShown = BrowserTestUtils.waitForEvent(window, "SidebarShown"); + SidebarUI.show("viewBookmarksSidebar"); + await promiseShown; + + Assert.greater( + getBGAlpha(), + 0, + "Sidebar widget background should appear checked" + ); + + // Enter customize mode. This should disable the toggle and make the sidebar + // toggle widget appear unchecked. + let customizationReadyPromise = BrowserTestUtils.waitForEvent( + gNavToolbox, + "customizationready" + ); + gCustomizeMode.enter(); + await customizationReadyPromise; + + Assert.equal( + getBGAlpha(), + 0, + "Sidebar widget background should appear unchecked" + ); + + // Attempt toggle - should fail in customize mode. + await SidebarUI.toggle(); + ok(SidebarUI.isOpen, "Sidebar is still open"); + + // Exit customize mode. This should re-enable the toggle and make the sidebar + // toggle widget appear checked again, since toggle() didn't hide the sidebar. + let afterCustomizationPromise = BrowserTestUtils.waitForEvent( + gNavToolbox, + "aftercustomization" + ); + gCustomizeMode.exit(); + await afterCustomizationPromise; + + Assert.greater( + getBGAlpha(), + 0, + "Sidebar widget background should appear checked again" + ); + + await SidebarUI.toggle(); + ok(!SidebarUI.isOpen, "Sidebar is closed"); + Assert.equal( + getBGAlpha(), + 0, + "Sidebar widget background should appear unchecked" + ); +}); diff --git a/browser/base/content/test/sidebar/browser_sidebar_move.js b/browser/base/content/test/sidebar/browser_sidebar_move.js new file mode 100644 index 0000000000..3de26b7966 --- /dev/null +++ b/browser/base/content/test/sidebar/browser_sidebar_move.js @@ -0,0 +1,72 @@ +registerCleanupFunction(() => { + Services.prefs.clearUserPref("sidebar.position_start"); + SidebarUI.hide(); +}); + +const EXPECTED_START_ORDINALS = [ + ["sidebar-box", 1], + ["sidebar-splitter", 2], + ["appcontent", 3], +]; + +const EXPECTED_END_ORDINALS = [ + ["sidebar-box", 3], + ["sidebar-splitter", 2], + ["appcontent", 1], +]; + +function getBrowserChildrenWithOrdinals() { + let browser = document.getElementById("browser"); + return [...browser.children].map(node => { + return [node.id, node.style.order]; + }); +} + +add_task(async function () { + await SidebarUI.show("viewBookmarksSidebar"); + SidebarUI.showSwitcherPanel(); + + let reversePositionButton = document.getElementById( + "sidebar-reverse-position" + ); + let originalLabel = reversePositionButton.getAttribute("label"); + let box = document.getElementById("sidebar-box"); + + // Default (position: left) + Assert.deepEqual( + getBrowserChildrenWithOrdinals(), + EXPECTED_START_ORDINALS, + "Correct ordinal (start)" + ); + ok(!box.hasAttribute("positionend"), "Positioned start"); + + // Moved to right + SidebarUI.reversePosition(); + SidebarUI.showSwitcherPanel(); + Assert.deepEqual( + getBrowserChildrenWithOrdinals(), + EXPECTED_END_ORDINALS, + "Correct ordinal (end)" + ); + isnot( + reversePositionButton.getAttribute("label"), + originalLabel, + "Label changed" + ); + ok(box.hasAttribute("positionend"), "Positioned end"); + + // Moved to back to left + SidebarUI.reversePosition(); + SidebarUI.showSwitcherPanel(); + Assert.deepEqual( + getBrowserChildrenWithOrdinals(), + EXPECTED_START_ORDINALS, + "Correct ordinal (start)" + ); + ok(!box.hasAttribute("positionend"), "Positioned start"); + is( + reversePositionButton.getAttribute("label"), + originalLabel, + "Label is back to normal" + ); +}); diff --git a/browser/base/content/test/sidebar/browser_sidebar_persist.js b/browser/base/content/test/sidebar/browser_sidebar_persist.js new file mode 100644 index 0000000000..fe67bed9e0 --- /dev/null +++ b/browser/base/content/test/sidebar/browser_sidebar_persist.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function persist_sidebar_width() { + { + // Make the main test window not count as a browser window any longer, + // which allows the persitence code to kick in. + const docEl = document.documentElement; + const oldWinType = docEl.getAttribute("windowtype"); + docEl.setAttribute("windowtype", "navigator:testrunner"); + registerCleanupFunction(() => { + docEl.setAttribute("windowtype", oldWinType); + }); + } + + { + info("Showing new window and setting sidebar box"); + const win = await BrowserTestUtils.openNewBrowserWindow(); + await win.SidebarUI.show("viewBookmarksSidebar"); + win.document.getElementById("sidebar-box").style.width = "100px"; + await BrowserTestUtils.closeWindow(win); + } + + { + info("Showing new window and seeing persisted width"); + const win = await BrowserTestUtils.openNewBrowserWindow(); + await win.SidebarUI.show("viewBookmarksSidebar"); + is( + win.document.getElementById("sidebar-box").style.width, + "100px", + "Width style should be persisted" + ); + await BrowserTestUtils.closeWindow(win); + } +}); diff --git a/browser/base/content/test/sidebar/browser_sidebar_switcher.js b/browser/base/content/test/sidebar/browser_sidebar_switcher.js new file mode 100644 index 0000000000..032c23b029 --- /dev/null +++ b/browser/base/content/test/sidebar/browser_sidebar_switcher.js @@ -0,0 +1,289 @@ +registerCleanupFunction(() => { + SidebarUI.hide(); +}); + +/** + * Helper function that opens a sidebar switcher panel popup menu + * @returns Promise that resolves when the switcher panel popup is shown + * without any action from a user/test + */ +function showSwitcherPanelPromise() { + return new Promise(resolve => { + SidebarUI._switcherPanel.addEventListener( + "popupshown", + () => { + resolve(); + }, + { once: true } + ); + SidebarUI.showSwitcherPanel(); + }); +} + +/** + * Helper function that waits for a sidebar switcher panel's "popupshown" event + * @returns Promise which resolves when the popup menu is opened + */ +async function waitForSwitcherPopupShown() { + return BrowserTestUtils.waitForEvent(SidebarUI._switcherPanel, "popupshown"); +} + +/** + * Helper function that sends a mouse click to a specific menu item or a key + * event to a active menu item of the sidebar switcher menu popup. Provide a + * querySelector parameter when a click behavior is needed. + * @param {String} [querySelector=null] An HTML attribute of the menu item + * to be clicked + * @returns Promise that resolves when both the menu popup is hidden and + * the sidebar itself is focused + */ +function pickSwitcherMenuitem(querySelector = null) { + let sidebarPopup = document.querySelector("#sidebarMenu-popup"); + let hideSwitcherPanelPromise = Promise.all([ + BrowserTestUtils.waitForEvent(window, "SidebarFocused"), + BrowserTestUtils.waitForEvent(sidebarPopup, "popuphidden"), + ]); + if (querySelector) { + document.querySelector(querySelector).click(); + } else { + EventUtils.synthesizeKey("KEY_Enter", {}); + } + return hideSwitcherPanelPromise; +} + +/** + * Helper function to test a key handling of sidebar menu popup items used to + * access a specific sidebar + * @param {String} key Event.key to open the switcher menu popup + * @param {String} sidebarTitle Title of the sidebar that is to be activated + * during the test (capitalized one word versions), + * i.e. "History" or "Tabs" + */ +async function testSidebarMenuKeyToggle(key, sidebarTitle) { + info(`Testing "${key}" key handling of sidebar menu popup items + to access ${sidebarTitle} sidebar`); + + Assert.ok(SidebarUI.isOpen, "Sidebar is open"); + + let sidebarSwitcher = document.querySelector("#sidebar-switcher-target"); + let sidebar = document.getElementById("sidebar"); + let searchBox = sidebar.firstElementChild; + + // If focus is on the search field (i.e. on the History sidebar), + // or if the focus is on the awesomebar (bug 1835899), + // move it to the switcher target: + + if (searchBox && searchBox.matches(":focus")) { + EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true, repeat: 2 }); + } else if (!sidebarSwitcher.matches(":focus")) { + sidebarSwitcher.focus(); + } + + Assert.equal( + document.activeElement, + sidebarSwitcher, + "The sidebar switcher target button is focused" + ); + Assert.ok( + sidebarSwitcher.matches(":focus"), + "The sidebar switcher target button is focused" + ); + Assert.equal( + SidebarUI._switcherPanel.state, + "closed", + "Sidebar menu popup is closed" + ); + + let promisePopupShown = waitForSwitcherPopupShown(); + + // Activate sidebar switcher target to open its sidebar menu popup: + EventUtils.synthesizeKey(key, {}); + + await promisePopupShown; + + Assert.equal( + SidebarUI._switcherPanel.state, + "open", + "Sidebar menu popup is open" + ); + + info("Testing keyboard navigation between sidebar menu popup controls"); + + let arrowDown = async (menuitemId, msg) => { + let menuItemActive = BrowserTestUtils.waitForEvent( + SidebarUI._switcherPanel, + "DOMMenuItemActive" + ); + EventUtils.synthesizeKey("KEY_ArrowDown", {}); + await menuItemActive; + Assert.ok( + document.getElementById(menuitemId).hasAttribute("_moz-menuactive"), + msg + ); + }; + + // Move to the first sidebar switcher option: + await arrowDown( + "sidebar-switcher-bookmarks", + "The 1st sidebar menu item (Bookmarks) is active" + ); + + // Move to the next sidebar switcher option: + await arrowDown( + "sidebar-switcher-history", + "The 2nd sidebar menu item (History) is active" + ); + + if (sidebarTitle === "Tabs") { + await arrowDown( + "sidebar-switcher-tabs", + "The 3rd sidebar menu item (Synced Tabs) is active" + ); + } + + // Activate the tested sidebar switcher option to open the tested sidebar: + let sidebarShown = BrowserTestUtils.waitForEvent(window, "SidebarShown"); + await pickSwitcherMenuitem(/* querySelector = */ null); + await sidebarShown; + + info("Testing keyboard navigation when a sidebar menu popup is closed"); + + Assert.equal( + SidebarUI._switcherPanel.state, + "closed", + "Sidebar menu popup is closed" + ); + // Test the sidebar panel is updated + Assert.equal( + SidebarUI._box.getAttribute("sidebarcommand"), + `view${sidebarTitle}Sidebar` /* e.g. "viewHistorySidebar" */, + `${sidebarTitle} sidebar loaded` + ); + Assert.equal( + SidebarUI.currentID, + `view${sidebarTitle}Sidebar` /* e.g. "viewHistorySidebar" */, + `${sidebarTitle}'s current ID is updated to a target view` + ); +} + +add_task(async function markup() { + // If a sidebar is already open, close it. + if (!document.getElementById("sidebar-box").hidden) { + Assert.ok( + false, + "Unexpected sidebar found - a previous test failed to cleanup correctly" + ); + SidebarUI.hide(); + } + + let sidebarPopup = document.querySelector("#sidebarMenu-popup"); + let sidebarSwitcher = document.querySelector("#sidebar-switcher-target"); + let sidebarTitle = document.querySelector("#sidebar-title"); + + info("Test default markup of the sidebar switcher control"); + + Assert.equal( + sidebarSwitcher.tagName, + "toolbarbutton", + "Sidebar switcher target control is a toolbarbutton" + ); + Assert.equal( + sidebarSwitcher.children[1], + sidebarTitle, + "Sidebar switcher target control has a child label element" + ); + Assert.equal( + sidebarTitle.tagName, + "label", + "Sidebar switcher target control has a label element (that is expected to provide its accessible name" + ); + Assert.equal( + sidebarSwitcher.getAttribute("aria-expanded"), + "false", + "Sidebar switcher button is collapsed by default" + ); + + info("Test dynamic changes in the markup of the sidebar switcher control"); + + await SidebarUI.show("viewBookmarksSidebar"); + await showSwitcherPanelPromise(); + + Assert.equal( + sidebarSwitcher.getAttribute("aria-expanded"), + "true", + "Sidebar switcher button is expanded when a sidebar menu is shown" + ); + + let waitForPopupHidden = BrowserTestUtils.waitForEvent( + sidebarPopup, + "popuphidden" + ); + + // Close on Escape anywhere + EventUtils.synthesizeKey("KEY_Escape", {}); + await waitForPopupHidden; + + Assert.equal( + sidebarSwitcher.getAttribute("aria-expanded"), + "false", + "Sidebar switcher button is collapsed when a sidebar menu is dismissed" + ); + + SidebarUI.hide(); +}); + +add_task(async function keynav() { + // If a sidebar is already open, close it. + if (SidebarUI.isOpen) { + Assert.ok( + false, + "Unexpected sidebar found - a previous test failed to cleanup correctly" + ); + SidebarUI.hide(); + } + + await SidebarUI.show("viewBookmarksSidebar"); + + await testSidebarMenuKeyToggle("KEY_Enter", "History"); + await testSidebarMenuKeyToggle(" ", "Tabs"); + + SidebarUI.hide(); +}); + +add_task(async function mouse() { + // If a sidebar is already open, close it. + if (!document.getElementById("sidebar-box").hidden) { + Assert.ok( + false, + "Unexpected sidebar found - a previous test failed to cleanup correctly" + ); + SidebarUI.hide(); + } + + let sidebar = document.querySelector("#sidebar-box"); + await SidebarUI.show("viewBookmarksSidebar"); + + await showSwitcherPanelPromise(); + await pickSwitcherMenuitem("#sidebar-switcher-history"); + Assert.equal( + sidebar.getAttribute("sidebarcommand"), + "viewHistorySidebar", + "History sidebar loaded" + ); + + await showSwitcherPanelPromise(); + await pickSwitcherMenuitem("#sidebar-switcher-tabs"); + Assert.equal( + sidebar.getAttribute("sidebarcommand"), + "viewTabsSidebar", + "Tabs sidebar loaded" + ); + + await showSwitcherPanelPromise(); + await pickSwitcherMenuitem("#sidebar-switcher-bookmarks"); + Assert.equal( + sidebar.getAttribute("sidebarcommand"), + "viewBookmarksSidebar", + "Bookmarks sidebar loaded" + ); +}); |