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/components/unifiedtoolbar/test/browser | |
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 '')
7 files changed, 879 insertions, 0 deletions
diff --git a/comm/mail/components/unifiedtoolbar/test/browser/browser.ini b/comm/mail/components/unifiedtoolbar/test/browser/browser.ini new file mode 100644 index 0000000000..072a4571ef --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/test/browser/browser.ini @@ -0,0 +1,16 @@ +[DEFAULT] +prefs = + mail.provider.suppress_dialog_on_startup=true + mail.spellcheck.inline=false + mail.spotlight.firstRunDone=true + mail.winsearch.firstRunDone=true + mailnews.database.global.indexer.enabled=false + mailnews.start_page.override_url=about:blank + mailnews.start_page.url=about:blank +subsuite = thunderbird +support-files = files/** + +[browser_customizableItems.js] +[browser_searchBar.js] +[browser_toolbarMigration.js] +[browser_unifiedToolbarTab.js] diff --git a/comm/mail/components/unifiedtoolbar/test/browser/browser_customizableItems.js b/comm/mail/components/unifiedtoolbar/test/browser/browser_customizableItems.js new file mode 100644 index 0000000000..152ade47f3 --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/test/browser/browser_customizableItems.js @@ -0,0 +1,173 @@ +/* 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/. */ + +"use strict"; + +const { + getAvailableItemIdsForSpace, + getDefaultItemIdsForSpace, + registerExtension, + unregisterExtension, +} = ChromeUtils.importESModule("resource:///modules/CustomizableItems.sys.mjs"); + +add_task(async function test_extensionRegisterUnregisterDefault() { + const extensionId = "thunderbird-compact-light@mozilla.org"; + await registerExtension(extensionId); + + const itemId = `ext-${extensionId}`; + ok( + getAvailableItemIdsForSpace("mail").includes(itemId), + "Extension item available in mail space" + ); + ok( + getDefaultItemIdsForSpace("mail").includes(itemId), + "Extension item in mail space by default" + ); + ok( + !getAvailableItemIdsForSpace().includes(itemId), + "Extension item not available in all spaces" + ); + + unregisterExtension(extensionId); + + ok( + !getAvailableItemIdsForSpace("mail").includes(itemId), + "Extension item no longer available in mail space" + ); + ok( + !getDefaultItemIdsForSpace("mail").includes(itemId), + "Extension item not in mail space by default" + ); +}); + +add_task(async function test_extensionRegisterAllSpaces() { + const extensionId = "thunderbird-compact-light@mozilla.org"; + await registerExtension(extensionId, []); + + const itemId = `ext-${extensionId}`; + ok( + getAvailableItemIdsForSpace().includes(itemId), + "Extension item available in all spaces" + ); + ok( + getDefaultItemIdsForSpace("default").includes(itemId), + "Extension item in all spaces by default" + ); + ok( + !getAvailableItemIdsForSpace("mail").includes(itemId), + "Extension item not available in mail space" + ); + ok( + getDefaultItemIdsForSpace("mail").includes(itemId), + "Extension item in mail space by default" + ); + + unregisterExtension(extensionId); + + ok( + !getAvailableItemIdsForSpace().includes(itemId), + "Extension item no longer available in all spaces" + ); + ok( + !getDefaultItemIdsForSpace("default").includes(itemId), + "Extension item not in any space by default" + ); +}); + +add_task(async function test_extensionRegisterMultipleSpaces() { + const extensionId = "thunderbird-compact-light@mozilla.org"; + await registerExtension(extensionId, ["mail", "calendar", "default"]); + + const itemId = `ext-${extensionId}`; + ok( + getAvailableItemIdsForSpace("calendar").includes(itemId), + "Extension item available in calendar space" + ); + ok( + getDefaultItemIdsForSpace("calendar").includes(itemId), + "Extension item in calendar space by default" + ); + ok( + getAvailableItemIdsForSpace("mail").includes(itemId), + "Extension item available in mail space" + ); + ok( + getDefaultItemIdsForSpace("mail").includes(itemId), + "Extension item in mail space by default" + ); + ok( + !getAvailableItemIdsForSpace().includes(itemId), + "Extension item not available in all spaces" + ); + ok( + getAvailableItemIdsForSpace("default").includes(itemId), + "Extension item available in default space" + ); + ok( + getDefaultItemIdsForSpace("default").includes(itemId), + "Extension item in default space" + ); + + unregisterExtension(extensionId); + + ok( + !getAvailableItemIdsForSpace("mail").includes(itemId), + "Extension item no longer available in mail space" + ); + ok( + !getDefaultItemIdsForSpace("mail").includes(itemId), + "Extension item not in mail space by default" + ); + ok( + !getAvailableItemIdsForSpace("calendar").includes(itemId), + "Extension item no longer available in calendar space" + ); + ok( + !getDefaultItemIdsForSpace("calendar").includes(itemId), + "Extension item not in calendar space by default" + ); + ok( + !getAvailableItemIdsForSpace().includes(itemId), + "Extension item not available in all spaces" + ); + ok( + !getAvailableItemIdsForSpace("default").includes(itemId), + "Extension item not available in default space" + ); + ok( + !getDefaultItemIdsForSpace("default").includes(itemId), + "Extension item not in default space" + ); +}); + +add_task(async function test_extensionRegisterStableOrder() { + const extension1Id = "thunderbird-compact-light@mozilla.org"; + const extension2Id = "thunderbird-compact-dark@mozilla.org"; + await registerExtension(extension1Id); + await registerExtension(extension2Id); + + const defaultItems = getDefaultItemIdsForSpace("mail"); + + const firstExtensionId = defaultItems + .find(itemId => itemId.startsWith("ext-")) + .slice(4); + + unregisterExtension(firstExtensionId); + + ok( + !getDefaultItemIdsForSpace("mail").includes(`ext-${firstExtensionId}`), + "Extension that was the first in the default set not in default set" + ); + + await registerExtension(firstExtensionId); + + Assert.deepEqual( + getDefaultItemIdsForSpace("mail"), + defaultItems, + "Default items order stable for extensions" + ); + + unregisterExtension(extension1Id); + unregisterExtension(extension2Id); +}); diff --git a/comm/mail/components/unifiedtoolbar/test/browser/browser_searchBar.js b/comm/mail/components/unifiedtoolbar/test/browser/browser_searchBar.js new file mode 100644 index 0000000000..b88c16f684 --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/test/browser/browser_searchBar.js @@ -0,0 +1,263 @@ +/* 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/. */ + +let tabmail = document.getElementById("tabmail"); +registerCleanupFunction(() => { + tabmail.closeOtherTabs(tabmail.tabInfo[0]); +}); +let browser; +let searchBar; + +const waitForRender = () => { + return new Promise(resolve => { + window.requestAnimationFrame(resolve); + }); +}; + +/* These are shadow-root safe variants of the methods in BrowserTestUtils. */ + +/** + * Checks if a DOM element is hidden. + * + * @param {Element} element + * The element which is to be checked. + * + * @return {boolean} + */ +function is_hidden(element) { + var style = element.ownerGlobal.getComputedStyle(element); + if (style.display == "none") { + return true; + } + if (style.visibility != "visible") { + return true; + } + if (style.display == "-moz-popup") { + return ["hiding", "closed"].includes(element.state); + } + + // Hiding a parent element will hide all its children + if (element.parentNode != element.ownerDocument && element.parentElement) { + return is_hidden(element.parentElement); + } + + return false; +} + +/** + * Checks if a DOM element is visible. + * + * @param {Element} element + * The element which is to be checked. + * + * @return {boolean} + */ +function is_visible(element) { + var style = element.ownerGlobal.getComputedStyle(element); + if (style.display == "none") { + return false; + } + if (style.visibility != "visible") { + return false; + } + if (style.display == "-moz-popup" && element.state != "open") { + return false; + } + + // Hiding a parent element will hide all its children + if (element.parentNode != element.ownerDocument && element.parentElement) { + return is_visible(element.parentElement); + } + + return true; +} + +add_setup(async function () { + let tab = tabmail.openTab("contentTab", { + url: "chrome://mochitests/content/browser/comm/mail/components/unifiedtoolbar/test/browser/files/searchBar.xhtml", + }); + + await BrowserTestUtils.browserLoaded(tab.browser); + tab.browser.focus(); + browser = tab.browser; + searchBar = tab.browser.contentWindow.document.querySelector("search-bar"); +}); + +add_task(async function test_initialState() { + const input = searchBar.shadowRoot.querySelector("input"); + is( + input.getAttribute("aria-label"), + searchBar.getAttribute("label"), + "Label forwarded to aria-label on input" + ); +}); + +add_task(async function test_labelUpdate() { + const input = searchBar.shadowRoot.querySelector("input"); + searchBar.setAttribute("label", "foo"); + await waitForRender(); + is( + input.getAttribute("aria-label"), + "foo", + "Updated label applied to content" + ); +}); + +add_task(async function test_focus() { + const input = searchBar.shadowRoot.querySelector("input"); + searchBar.focus(); + is( + searchBar.shadowRoot.activeElement, + input, + "Input is focused when search bar is focused" + ); +}); + +add_task(async function test_autocompleteEvent() { + const typeAndWaitForAutocomplete = async key => { + const eventPromise = BrowserTestUtils.waitForEvent( + searchBar, + "autocomplete" + ); + await BrowserTestUtils.synthesizeKey(key, {}, browser); + return eventPromise; + }; + searchBar.focus(); + let event = await typeAndWaitForAutocomplete("T"); + is(event.detail, "T", "Autocomplete for T"); + + event = await typeAndWaitForAutocomplete("e"); + is(event.detail, "Te", "Autocomplete for e"); + + event = await typeAndWaitForAutocomplete("KEY_Backspace"); + is(event.detail, "T", "Autocomplete for backspace"); + + await BrowserTestUtils.synthesizeKey("KEY_Backspace", {}, browser); +}); + +add_task(async function test_searchEventFromEnter() { + const input = searchBar.shadowRoot.querySelector("input"); + input.value = "Lorem ipsum"; + searchBar.focus(); + + const eventPromise = BrowserTestUtils.waitForEvent(searchBar, "search"); + await BrowserTestUtils.synthesizeKey("KEY_Enter", {}, browser); + const event = await eventPromise; + + is(event.detail, "Lorem ipsum", "Event contains search query"); + await waitForRender(); + is(input.value, "", "Input was cleared"); +}); + +add_task(async function test_searchEventFromButton() { + const input = searchBar.shadowRoot.querySelector("input"); + input.value = "Lorem ipsum"; + + const eventPromise = BrowserTestUtils.waitForEvent(searchBar, "search"); + searchBar.shadowRoot.querySelector("button").click(); + const event = await eventPromise; + + is(event.detail, "Lorem ipsum", "Event contains search query"); + await waitForRender(); + is(input.value, "", "Input was cleared"); +}); + +add_task(async function test_searchEventPreventDefault() { + const input = searchBar.shadowRoot.querySelector("input"); + input.value = "Lorem ipsum"; + + searchBar.addEventListener( + "search", + event => { + event.preventDefault(); + }, + { + once: true, + passive: false, + } + ); + + const eventPromise = BrowserTestUtils.waitForEvent(searchBar, "search"); + searchBar.shadowRoot.querySelector("button").click(); + await eventPromise; + await waitForRender(); + + is(input.value, "Lorem ipsum"); + + input.value = ""; +}); + +add_task(async function test_placeholderVisibility() { + const placeholder = searchBar.shadowRoot.querySelector("div"); + const input = searchBar.shadowRoot.querySelector("input"); + + input.value = ""; + await waitForRender(); + ok(is_visible(placeholder), "Placeholder is visible initially"); + + input.value = "some input"; + await waitForRender(); + ok(is_hidden(placeholder), "Placeholder is hidden after text is entered"); + + input.value = ""; + await waitForRender(); + ok( + is_visible(placeholder), + "Placeholder is visible again after input is cleared" + ); +}); + +add_task(async function test_placeholderFallbackToLabel() { + const placeholder = searchBar.querySelector("span"); + placeholder.remove(); + + const shadowedPlaceholder = searchBar.shadowRoot.querySelector("div"); + const label = searchBar.getAttribute("label"); + + is( + shadowedPlaceholder.textContent, + label, + "Falls back to label if no placeholder slot contents provided" + ); + + searchBar.setAttribute("label", "Foo bar"); + is( + shadowedPlaceholder.textContent, + "Foo bar", + "Placeholder contents get updated with label attribute" + ); + + searchBar.prepend(placeholder); + searchBar.setAttribute("label", label); +}); + +add_task(async function test_reset() { + const input = searchBar.shadowRoot.querySelector("input"); + const placeholder = searchBar.shadowRoot.querySelector("div"); + input.value = "Lorem ipsum"; + + searchBar.reset(); + + is(input.value, "", "Input empty after reset"); + await waitForRender(); + ok(is_visible(placeholder), "Placeholder visible"); +}); + +add_task(async function test_disabled() { + const input = searchBar.shadowRoot.querySelector("input"); + const button = searchBar.shadowRoot.querySelector("button"); + + ok(!input.disabled, "Input enabled"); + ok(!button.disabled, "Button enabled"); + + searchBar.setAttribute("disabled", true); + + ok(input.disabled, "Disabled propagated to input"); + ok(button.disabled, "Disabled propagated to button"); + + searchBar.removeAttribute("disabled"); + + ok(!input.disabled, "Input enabled again"); + ok(!button.disabled, "Button enabled again"); +}); diff --git a/comm/mail/components/unifiedtoolbar/test/browser/browser_toolbarMigration.js b/comm/mail/components/unifiedtoolbar/test/browser/browser_toolbarMigration.js new file mode 100644 index 0000000000..c2ca1147fd --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/test/browser/browser_toolbarMigration.js @@ -0,0 +1,99 @@ +/* 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/. */ + +"use strict"; + +const { migrateToolbarForSpace } = ChromeUtils.importESModule( + "resource:///modules/ToolbarMigration.sys.mjs" +); +const { getState, storeState } = ChromeUtils.importESModule( + "resource:///modules/CustomizationState.mjs" +); +const { EXTENSION_PREFIX } = ChromeUtils.importESModule( + "resource:///modules/CustomizableItems.sys.mjs" +); +const { getCachedAllowedSpaces, setCachedAllowedSpaces } = ChromeUtils.import( + "resource:///modules/ExtensionToolbarButtons.jsm" +); + +const MESSENGER_WINDOW = "chrome://messenger/content/messenger.xhtml"; +const EXTENSION_ID = "thunderbird-compact-light@mozilla.org"; + +add_setup(() => { + storeState({}); +}); + +add_task(async function test_migrate_extension() { + Services.xulStore.setValue(MESSENGER_WINDOW, "mail-bar3", "currentset", ""); + Services.xulStore.setValue( + MESSENGER_WINDOW, + "mail-bar3", + "defaultset", + "button-getmsg,button-newmsg,separator,button-tag,qfb-show-filter-bar,spring,gloda-search,thunderbird-compact-light_mozilla_org-browserAction-toolbarbutton,button-appmenu" + ); + Services.xulStore.setValue( + MESSENGER_WINDOW, + "mail-bar3", + "extensionset", + "thunderbird-compact-light_mozilla_org-browserAction-toolbarbutton" + ); + const extensionPref = Services.prefs.getStringPref( + "extensions.webextensions.uuids", + "" + ); + const parsedPref = JSON.parse(extensionPref || "{}"); + if (!parsedPref.hasOwnProperty(EXTENSION_ID)) { + parsedPref[EXTENSION_ID] = "foo"; + Services.prefs.setStringPref( + "extensions.webextensions.uuids", + JSON.stringify(parsedPref) + ); + } + + migrateToolbarForSpace("mail"); + + const newState = getState(); + + Assert.deepEqual( + newState.mail, + [ + "get-messages", + "write-message", + "tag-message", + "quick-filter-bar", + "spacer", + `${EXTENSION_PREFIX}${EXTENSION_ID}`, + "spacer", + ], + "Extension button was converted to new ID format" + ); + Assert.ok( + !Services.xulStore.hasValue(MESSENGER_WINDOW, "mail-bar3", "currentset"), + "Old toolbar state is cleared" + ); + Assert.ok( + !Services.xulStore.hasValue(MESSENGER_WINDOW, "mail-bar3", "defaultset"), + "Old toolbar default state is cleared" + ); + Assert.ok( + !Services.xulStore.hasValue(MESSENGER_WINDOW, "mail-bar3", "extensionset"), + "Old toolbar extension state is cleared" + ); + Assert.deepEqual( + Object.fromEntries(getCachedAllowedSpaces()), + { [EXTENSION_ID]: ["mail"] }, + "Extension set migrated to new persistent extension state" + ); + + storeState({}); + setCachedAllowedSpaces(new Map()); + if (extensionPref) { + Services.prefs.setStringPref( + "extensions.webextensions.uuids", + extensionPref + ); + } else { + Services.prefs.clearUserPref("extensions.webextensions.uuids"); + } +}); diff --git a/comm/mail/components/unifiedtoolbar/test/browser/browser_unifiedToolbarTab.js b/comm/mail/components/unifiedtoolbar/test/browser/browser_unifiedToolbarTab.js new file mode 100644 index 0000000000..336199ee51 --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/test/browser/browser_unifiedToolbarTab.js @@ -0,0 +1,285 @@ +/* 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/. */ + +let tabmail = document.getElementById("tabmail"); +registerCleanupFunction(() => { + tabmail.closeOtherTabs(tabmail.tabInfo[0]); +}); +let browser; +let testDocument; + +const waitForRender = () => { + return new Promise(resolve => { + window.requestAnimationFrame(resolve); + }); +}; +const getTabButton = tab => tab.shadowRoot.querySelector("button"); +/** + * Get the relevant elements for the tab at the given index. + * + * @param {number} tabIndex + * @returns {{tab: UnifiedToolbarTab, button: HTMLButtonElement, pane: HTMLElement}} + */ +const getTabElements = tabIndex => { + const tab = testDocument.querySelector( + `unified-toolbar-tab:nth-child(${tabIndex})` + ); + const button = getTabButton(tab); + const pane = tab.pane; + return { tab, button, pane }; +}; + +add_setup(async function () { + let tab = tabmail.openTab("contentTab", { + url: "chrome://mochitests/content/browser/comm/mail/components/unifiedtoolbar/test/browser/files/unifiedToolbarTab.xhtml", + }); + + await BrowserTestUtils.browserLoaded(tab.browser); + tab.browser.focus(); + browser = tab.browser; + testDocument = tab.browser.contentWindow.document; +}); + +add_task(function test_tabElementInitialization() { + const activeTab = testDocument.querySelector("unified-toolbar-tab[selected]"); + is( + activeTab.getAttribute("role"), + "presentation", + "The custom element is just for show" + ); + ok( + !activeTab.hasAttribute("aria-controls"), + "aria-controls removed from custom element" + ); + ok(activeTab.hasAttribute("selected"), "Active tab kept itself selected"); + const tabButton = getTabButton(activeTab); + is(tabButton.getAttribute("role"), "tab", "Active tab is marked as tab"); + is(tabButton.tabIndex, 0, "Active tab is in the focus ring"); + is( + tabButton.getAttribute("aria-selected"), + "true", + "Tab is marked as selected" + ); + ok( + tabButton.hasAttribute("aria-controls"), + "aria-controls got given to button" + ); + + const otherTab = testDocument.querySelector( + "unified-toolbar-tab:not([selected])" + ); + is( + otherTab.getAttribute("role"), + "presentation", + "The custom element is just for show on the other tab" + ); + ok( + !otherTab.hasAttribute("aria-controls"), + "aria-controls removed from the other tab" + ); + ok(!otherTab.hasAttribute("selected"), "Other tab didn't select itself"); + const otherButton = getTabButton(otherTab); + is(otherButton.getAttribute("role"), "tab", "Other tab is marked as tab"); + is(otherButton.tabIndex, -1, "Other tab is not in the focus ring"); + ok( + !otherButton.hasAttribute("aria-selected"), + "Other tab isn't marked as selected" + ); + ok( + otherButton.hasAttribute("aria-controls"), + "aria-controls got given to other button" + ); +}); + +add_task(async function test_paneGetter() { + const tab1 = getTabElements(1); + const tabPane = testDocument.getElementById("tabPane"); + const tab2 = getTabElements(2); + const otherTabPane = testDocument.getElementById("otherTabPane"); + + is( + tab1.button.getAttribute("aria-controls"), + tabPane.id, + "Tab 1 controls tab 1 pane" + ); + is( + tab2.button.getAttribute("aria-controls"), + otherTabPane.id, + "Tab 2 controls tab 2 pane" + ); + + Assert.strictEqual( + tab1.tab.pane, + tabPane, + "Tab 1 pane getter returns #tabPane" + ); + Assert.strictEqual( + tab2.tab.pane, + otherTabPane, + "Tab 2 pane getter returns #otherTabPane" + ); +}); + +add_task(async function test_unselect() { + const tab = getTabElements(1); + + tab.tab.unselect(); + + ok(!tab.button.hasAttribute("aria-selected"), "Tab not marked as selected"); + is(tab.button.tabIndex, -1, "Tab not in focus ring"); + ok(!tab.tab.hasAttribute("selected"), "Tab not marked selected"); + ok(tab.pane.hidden, "Tab pane hidden"); +}); + +add_task(async function test_select() { + const tab1 = getTabElements(1); + const tab2 = getTabElements(2); + + let tabswitchPromise = BrowserTestUtils.waitForEvent( + testDocument.body, + "tabswitch" + ); + tab1.tab.select(); + + await tabswitchPromise; + ok(tab1.tab.hasAttribute("selected"), "Tab 1 selected"); + is( + tab1.button.getAttribute("aria-selected"), + "true", + "Tab 1 marked as selected" + ); + is(tab1.button.tabIndex, 0, "Tab 1 keyboard selectable"); + ok(!tab1.pane.hidden, "Tab pane for tab 1 visible"); + + tabswitchPromise = BrowserTestUtils.waitForEvent(tab2.tab, "tabswitch"); + tab2.tab.select(); + + await tabswitchPromise; + ok(tab2.tab.hasAttribute("selected"), "Tab 2 selected"); + is( + tab2.button.getAttribute("aria-selected"), + "true", + "Tab 2 has a11y selection" + ); + is(tab2.button.tabIndex, 0, "Tab 2 keyboard selectable"); + ok(!tab2.pane.hidden, "Tab pane for tab 2 visible"); + + ok(!tab1.tab.hasAttribute("selected"), "Tab 1 unselected"); + ok(!tab1.button.hasAttribute("aria-selected"), "Tab 1 marked as unselected"); + is(tab1.button.tabIndex, -1, "Tab 1 not in focus ring"); + ok(tab1.pane.hidden, "Tab pane for tab 1 hidden"); +}); + +add_task(async function test_switchingTabWithMouse() { + const tab1 = getTabElements(1); + const tab2 = getTabElements(2); + + tab2.button.click(); + ok(tab2.tab.hasAttribute("selected"), "Other tab is selected"); + is(tab2.button.tabIndex, 0, "Other tab is in focus ring"); + ok(!tab1.tab.hasAttribute("selected"), "First tab is not selected"); + is(tab1.button.tabIndex, -1, "First tab is not in focus ring"); + ok( + BrowserTestUtils.is_visible(tab2.pane), + "Tab pane for selected tab is visible" + ); + ok(BrowserTestUtils.is_hidden(tab1.pane), "Tab pane for first tab is hidden"); + + tab1.button.click(); + ok(tab1.tab.hasAttribute("selected"), "First tab is selected"); + is(tab1.button.tabIndex, 0, "First tab is in focus ring"); + ok(!tab2.tab.hasAttribute("selected"), "Other tab is not selected"); + is(tab2.button.tabIndex, -1, "Other tab is not in focus ring"); + ok( + BrowserTestUtils.is_visible(tab1.pane), + "Tab pane for first tab is visible" + ); + ok(BrowserTestUtils.is_hidden(tab2.pane), "Tab pane for other tab is hidden"); +}); + +add_task(async function test_switchingTabWithKeyboard() { + const tab1 = getTabElements(1); + const tab2 = getTabElements(2); + + tab1.tab.focus(); + is(testDocument.activeElement, tab1.tab, "Initially first tab is active"); + + await BrowserTestUtils.synthesizeKey("KEY_ArrowRight", {}, browser); + is(testDocument.activeElement, tab2.tab, "Second tab is focused"); + is( + tab2.tab.shadowRoot.activeElement, + tab2.button, + "Button within tab is focused" + ); + await BrowserTestUtils.synthesizeKey(" ", {}, browser); + ok(tab2.tab.hasAttribute("selected"), "Other tab is selected"); + is(tab2.button.tabIndex, 0, "Other tab is in focus ring"); + ok(!tab1.tab.hasAttribute("selected"), "First tab is not selected"); + is(tab1.button.tabIndex, -1, "First tab is not in focus ring"); + ok( + BrowserTestUtils.is_visible(tab2.pane), + "Tab pane for selected tab is visible" + ); + ok(BrowserTestUtils.is_hidden(tab1.pane), "Tab pane for first tab is hidden"); + + await BrowserTestUtils.synthesizeKey("KEY_ArrowLeft", {}, browser); + is(testDocument.activeElement, tab1.tab, "Previous tab is selected"); + await BrowserTestUtils.synthesizeKey("KEY_End", {}, browser); + is(testDocument.activeElement, tab2.tab, "Last tab is selected"); + await BrowserTestUtils.synthesizeKey("KEY_Home", {}, browser); + is(testDocument.activeElement, tab1.tab, "First tab is selected"); + await BrowserTestUtils.synthesizeKey("KEY_Enter", {}, browser); + ok(tab1.tab.hasAttribute("selected"), "First tab is selected"); + is(tab1.button.tabIndex, 0, "First tab is in focus ring"); + ok(!tab2.tab.hasAttribute("selected"), "Other tab is not selected"); + is(tab2.button.tabIndex, -1, "Other tab is not in focus ring"); + ok( + BrowserTestUtils.is_visible(tab1.pane), + "Tab pane for first tab is visible" + ); + ok(BrowserTestUtils.is_hidden(tab2.pane), "Tab pane for other tab is hidden"); +}); + +add_task(async function test_switchingTabWithKeyboardRTL() { + testDocument.dir = "rtl"; + await waitForRender(); + const tab1 = getTabElements(1); + const tab2 = getTabElements(2); + + tab1.tab.focus(); + is(testDocument.activeElement, tab1.tab, "Initially first tab is active"); + + await BrowserTestUtils.synthesizeKey("KEY_ArrowLeft", {}, browser); + is(testDocument.activeElement, tab2.tab, "Second tab is selected"); + is( + tab2.tab.shadowRoot.activeElement, + tab2.button, + "Button within tab is focused" + ); + await BrowserTestUtils.synthesizeKey(" ", {}, browser); + ok(tab2.tab.hasAttribute("selected"), "Other tab is selected"); + is(tab2.button.tabIndex, 0, "Other tab is in focus ring"); + ok(!tab1.tab.hasAttribute("selected"), "First tab is not selected"); + is(tab1.button.tabIndex, -1, "First tab is not in focus ring"); + ok( + BrowserTestUtils.is_visible(tab2.pane), + "Tab pane for selected tab is visible" + ); + ok(BrowserTestUtils.is_hidden(tab1.pane), "Tab pane for first tab is hidden"); + + await BrowserTestUtils.synthesizeKey("KEY_ArrowRight", {}, browser); + is(testDocument.activeElement, tab1.tab, "Previous tab is selected"); + await BrowserTestUtils.synthesizeKey("KEY_Enter", {}, browser); + ok(tab1.tab.hasAttribute("selected"), "First tab is selected"); + is(tab1.button.tabIndex, 0, "First tab is in focus ring"); + ok(!tab2.tab.hasAttribute("selected"), "Other tab is not selected"); + is(tab2.button.tabIndex, -1, "Other tab is not in focus ring"); + ok( + BrowserTestUtils.is_visible(tab1.pane), + "Tab pane for first tab is visible" + ); + ok(BrowserTestUtils.is_hidden(tab2.pane), "Tab pane for other tab is hidden"); + + testDocument.dir = "ltr"; +}); diff --git a/comm/mail/components/unifiedtoolbar/test/browser/files/searchBar.xhtml b/comm/mail/components/unifiedtoolbar/test/browser/files/searchBar.xhtml new file mode 100644 index 0000000000..33000135b4 --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/test/browser/files/searchBar.xhtml @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta charset="utf-8" /> + <title>Search bar element test</title> + <script type="module" src="chrome://messenger/content/unifiedtoolbar/search-bar.mjs"></script> + </head> + <body> + <template id="searchBarTemplate"> + <form> + <input type="search" placeholder="" required="required"/> + <div aria-hidden="true"><slot name="placeholder"></slot></div> + <button class="button button-flat icon-button"><slot name="button"></slot></button> + </form> + </template> + <search-bar label="Search"> + <span slot="placeholder">Placeholder</span> + <img slot="button" src="chrome://messenger/skin/icons/new/compact/search.svg" alt="Search"/> + </search-bar> + </body> +</html> diff --git a/comm/mail/components/unifiedtoolbar/test/browser/files/unifiedToolbarTab.xhtml b/comm/mail/components/unifiedtoolbar/test/browser/files/unifiedToolbarTab.xhtml new file mode 100644 index 0000000000..f30f2b7d8b --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/test/browser/files/unifiedToolbarTab.xhtml @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr"> + <head> + <meta charset="utf-8" /> + <title>Search bar element test</title> + <script type="module" src="chrome://messenger/content/unifiedtoolbar/unified-toolbar-tab.mjs"></script> + </head> + <body> + <template id="unifiedToolbarTabTemplate"> + <button role="tab"> + <img alt="" src="" /> + <slot></slot> + </button> + </template> + <div role="tablist"> + <unified-toolbar-tab selected="true" aria-controls="tabPane">Tab Title</unified-toolbar-tab> + <unified-toolbar-tab aria-controls="otherTabPane">Other Tab</unified-toolbar-tab> + </div> + <div id="tabPane" role="tabpanel">Panel 1</div> + <div id="otherTabPane" role="tabpanel" hidden="hidden">Panel 2</div> + </body> +</html> |