Revision control

Copy as Markdown

Other Tools

/* 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 the spaces toolbar features.
*/
/* globals gSpacesToolbar */
var folderA;
var folderB;
var testAccount;
add_setup(function () {
// Set up two folders.
testAccount = MailServices.accounts.createLocalMailAccount();
const rootFolder = testAccount.incomingServer.rootFolder.QueryInterface(
Ci.nsIMsgLocalMailFolder
);
folderA = rootFolder.createLocalSubfolder("spacesToolbarA");
folderB = rootFolder.createLocalSubfolder("spacesToolbarB");
});
registerCleanupFunction(async () => {
window.MailServices.accounts.removeAccount(testAccount, true);
// Close all opened tabs.
const tabmail = document.getElementById("tabmail");
tabmail.closeOtherTabs(tabmail.tabInfo[0]);
// Reset the spaces toolbar to its default visible state.
window.gSpacesToolbar.toggleToolbar(false);
// Reset the menubar visibility.
const menubar = document.getElementById("toolbar-menubar");
menubar.removeAttribute("autohide");
menubar.removeAttribute("inactive");
await new Promise(resolve => requestAnimationFrame(resolve));
});
async function assertMailShown(win = window) {
await TestUtils.waitForCondition(
() =>
win.document.getElementById("tabmail").currentTabInfo.mode.name ==
"mail3PaneTab",
"The mail tab should be visible"
);
}
async function assertAddressBookShown(win = window) {
await TestUtils.waitForCondition(() => {
const panel = win.document.querySelector(
// addressBookTabWrapper0, addressBookTabWrapper1, etc
"#tabpanelcontainer > [id^=addressBookTabWrapper][selected]"
);
if (!panel) {
return false;
}
const browser = panel.querySelector("[id^=addressBookTabBrowser]");
return browser.contentDocument.readyState == "complete";
}, "The address book tab should be visible and loaded");
}
async function assertChatShown(win = window) {
await TestUtils.waitForCondition(
() => win.document.getElementById("chatTabPanel").hasAttribute("selected"),
"The chat tab should be visible"
);
}
async function assertCalendarShown(win = window) {
await TestUtils.waitForCondition(() => {
return (
win.document
.getElementById("calendarTabPanel")
.hasAttribute("selected") &&
!win.document.getElementById("calendar-view-box").collapsed
);
}, "The calendar view should be visible");
}
async function assertTasksShown(win = window) {
await TestUtils.waitForCondition(() => {
return (
win.document
.getElementById("calendarTabPanel")
.hasAttribute("selected") &&
!win.document.getElementById("calendar-task-box").collapsed
);
}, "The task view should be visible");
}
async function assertSettingsShown(win = window) {
await TestUtils.waitForCondition(() => {
const panel = win.document.querySelector(
// preferencesTabWrapper0, preferencesTabWrapper1, etc
"#tabpanelcontainer > [id^=preferencesTabWrapper][selected]"
);
if (!panel) {
return false;
}
const browser = panel.querySelector("[id^=preferencesTabBrowser]");
return browser.contentDocument.readyState == "complete";
}, "The settings tab should be visible and loaded");
}
async function assertContentShown(url, win = window) {
await TestUtils.waitForCondition(() => {
const panel = win.document.querySelector(
// contentTabWrapper0, contentTabWrapper1, etc
"#tabpanelcontainer > [id^=contentTabWrapper][selected]"
);
if (!panel) {
return false;
}
const doc = panel.querySelector("[id^=contentTabBrowser]").contentDocument;
return doc.URL == url && doc.readyState == "complete";
}, `The selected content tab should show ${url}`);
}
async function sub_test_cycle_through_primary_tabs() {
// We can't really cycle through all buttons and tabs with a simple for loop
// since some tabs are actual collapsing views and other tabs are separate
// pages. We can improve this once the new 3pane tab is actually a standalone
// tab.
// Switch to address book.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("addressBookButton"),
{},
window
);
await assertAddressBookShown();
// Switch to calendar.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("calendarButton"),
{},
window
);
await assertCalendarShown();
// Switch to Mail.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("mailButton"),
{},
window
);
await assertMailShown();
// Switch to Tasks.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("tasksButton"),
{},
window
);
await assertTasksShown();
// Switch to chat.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("chatButton"),
{},
window
);
await assertChatShown();
// Switch to Settings.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("settingsButton"),
{},
window
);
await assertSettingsShown();
// Switch to Mail.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("mailButton"),
{},
window
);
await assertMailShown();
window.tabmail.closeOtherTabs(window.tabmail.tabInfo[0]);
}
add_task(async function testSpacesToolbarVisibility() {
const spacesToolbar = document.getElementById("spacesToolbar");
const toggleButton = document.getElementById("spacesToolbarReveal");
const pinnedButton = document.getElementById("spacesPinnedButton");
Assert.ok(spacesToolbar, "The spaces toolbar exists");
const assertVisibility = async function (isHidden, msg) {
await TestUtils.waitForCondition(
() => spacesToolbar.hidden == !isHidden,
`The spaces toolbar should be ${!isHidden ? "visible" : "hidden"}: ${msg}`
);
await TestUtils.waitForCondition(
() => toggleButton.hidden == isHidden,
`The toggle button should be ${isHidden ? "hidden" : "visible"}: ${msg}`
);
await TestUtils.waitForCondition(
() => pinnedButton.hidden == isHidden,
`The pinned button should be ${isHidden ? "hidden" : "visible"}: ${msg}`
);
};
async function toggleVisibilityWithAppMenu(expectChecked) {
const appMenu = document.getElementById("appMenu-popup");
const menuShownPromise = BrowserTestUtils.waitForEvent(
appMenu,
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(
document.getElementById("button-appmenu"),
{},
window
);
await menuShownPromise;
const viewShownPromise = BrowserTestUtils.waitForEvent(
appMenu.querySelector("#appMenu-viewView"),
"ViewShown"
);
EventUtils.synthesizeMouseAtCenter(
appMenu.querySelector("#appmenu_View"),
{},
window
);
await viewShownPromise;
const toolbarShownPromise = BrowserTestUtils.waitForEvent(
appMenu.querySelector("#appMenu-toolbarsView"),
"ViewShown"
);
EventUtils.synthesizeMouseAtCenter(
appMenu.querySelector("#appmenu_Toolbars"),
{},
window
);
await toolbarShownPromise;
const appMenuButton = document.getElementById("appmenu_spacesToolbar");
Assert.equal(
appMenuButton.checked,
expectChecked,
`The app menu item should ${expectChecked ? "not " : ""}be checked`
);
EventUtils.synthesizeMouseAtCenter(appMenuButton, {}, window);
// Close the appmenu.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("button-appmenu"),
{},
window
);
}
await assertVisibility(true, "on initial load");
// Collapse with a mouse click.
let activeElement = document.activeElement;
const collapseButton = document.getElementById("collapseButton");
EventUtils.synthesizeMouseAtCenter(collapseButton, {}, window);
await assertVisibility(false, "after clicking collapse button");
await toggleVisibilityWithAppMenu(false);
await assertVisibility(true, "after revealing with the app menu");
// We already clicked the collapse button, so it should already be the
// focusButton for the gSpacesToolbar, and thus focusable.
collapseButton.focus();
Assert.ok(
collapseButton.matches(":focus"),
"Collapse button should be focusable"
);
// Hide the spaces toolbar using the collapse button, which already has focus.
EventUtils.synthesizeKey(" ", {}, window);
await assertVisibility(false, "after closing with space key press");
Assert.ok(
pinnedButton.matches(":focus"),
"Pinned button should be focused after closing with a key press"
);
// Show using the pinned button menu.
const pinnedMenu = document.getElementById("spacesButtonMenuPopup");
const pinnedMenuShown = BrowserTestUtils.waitForEvent(
pinnedMenu,
"popupshown"
);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await pinnedMenuShown;
pinnedMenu.activateItem(document.getElementById("spacesPopupButtonReveal"));
await assertVisibility(true, "after opening with pinned menu");
Assert.ok(
collapseButton.matches(":focus"),
"Collapse button should be focused again after showing with the pinned menu"
);
// Move focus to the mail button.
const mailButton = document.getElementById("mailButton");
// Loop around from the collapse button to the mailButton.
EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
Assert.ok(
mailButton.matches(":focus"),
"Mail button should become focused after pressing key down"
);
Assert.ok(
spacesToolbar.matches(":focus-within"),
"Spaces toolbar should contain the focus"
);
// Now move focus elsewhere.
EventUtils.synthesizeKey("KEY_Tab", {}, window);
activeElement = document.activeElement;
Assert.ok(
!mailButton.matches(":focus"),
"Mail button should no longer be focused"
);
Assert.ok(
!spacesToolbar.matches(":focus-within"),
"Spaces toolbar should no longer contain the focus"
);
// Hide the spaces toolbar using the app menu.
await toggleVisibilityWithAppMenu(true);
await assertVisibility(false, "after hiding with the app menu");
// macOS by default doesn't move the focus when clicking on toolbar buttons.
if (AppConstants.platform != "macosx") {
Assert.notEqual(
document.activeElement,
activeElement,
"The focus moved from the previous element"
);
// Focus should be on the main app menu since we used the mouse to toggle the
// spaces toolbar.
Assert.equal(
document.activeElement,
document.getElementById("button-appmenu"),
"Active element is on the app menu"
);
} else {
Assert.equal(
document.activeElement,
activeElement,
"The focus didn't move from the previous element"
);
}
// Now click the status bar toggle button to reveal the toolbar again.
toggleButton.focus();
Assert.ok(
toggleButton.matches(":focus"),
"Toggle button should be focusable"
);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await assertVisibility(true, "after showing with the toggle button");
// Focus is restored to the mailButton.
Assert.ok(
mailButton.matches(":focus"),
"Mail button should become focused again"
);
// Clicked buttons open or move to the correct tab, starting with just one tab
// open.
await sub_test_cycle_through_primary_tabs();
});
add_task(async function testSpacesToolbarContextMenu() {
const tabmail = document.getElementById("tabmail");
const firstMailTabInfo = tabmail.currentTabInfo;
firstMailTabInfo.folder = folderB;
// Fetch context menu elements.
const contextMenu = document.getElementById("spacesContextMenu");
const newTabItem = document.getElementById("spacesContextNewTabItem");
const newWindowItem = document.getElementById("spacesContextNewWindowItem");
const settingsMenu = document.getElementById("settingsContextMenu");
const settingsItem = document.getElementById(
"settingsContextOpenSettingsItem"
);
const accountItem = document.getElementById(
"settingsContextOpenAccountSettingsItem"
);
const addonsItem = document.getElementById("settingsContextOpenAddonsItem");
/**
* Open the context menu, test its state, select an action and wait for it to
* close.
*
* @param {object} input - Input data.
* @param {Element} input.button - The button whose context menu should be
* opened.
* @param {Element} [input.item] - The context menu item to select. Either
* this or switchItem must be given.
* @param {number} [input.switchItem] - The nth switch-to-tab item to select.
* @param {object} expect - The expected state of the context menu when
* opened.
* @param {boolean} [expect.settings=false] - Whether we expect the settings
* context menu. If this is true, the other values are ignored.
* @param {boolean} [expect.newTab=false] - Whether we expect the "Open in new
* tab" item to be visible.
* @param {boolean} [expect.newWindow=false] - Whether we expect the "Open in
* new window" item to be visible.
* @param {number} [expect.numSwitch=0] - The expected number of switch-to-tab
* items.
* @param {string} msg - A message to use in tests.
*/
async function useContextMenu(input, expect, msg) {
const menu = expect.settings ? settingsMenu : contextMenu;
const shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
EventUtils.synthesizeMouseAtCenter(
input.button,
{ type: "contextmenu" },
window
);
await shownPromise;
let item = input.item;
if (!expect.settings) {
Assert.equal(
BrowserTestUtils.isVisible(newTabItem),
expect.newTab || false,
`Open in new tab item visibility: ${msg}`
);
Assert.equal(
BrowserTestUtils.isVisible(newWindowItem),
expect.newWindow || false,
`Open in new window item visibility: ${msg}`
);
const switchItems = menu.querySelectorAll(".switch-to-tab");
Assert.equal(
switchItems.length,
expect.numSwitch || 0,
`Should have the expected number of switch items: ${msg}`
);
if (!item) {
item = switchItems[input.switchItem];
}
}
const hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden");
menu.activateItem(item);
await hiddenPromise;
}
const tabScroll = document.getElementById("tabmail-arrowscrollbox").scrollbox;
/**
* Ensure the tab is scrolled into view.
*
* @param {MozTabmailTab} - The tab to scroll into view.
*/
async function scrollToTab(tab) {
function tabInView() {
const tabRect = tab.getBoundingClientRect();
const scrollRect = tabScroll.getBoundingClientRect();
return (
tabRect.left >= scrollRect.left && tabRect.right <= scrollRect.right
);
}
if (tabInView()) {
info(`Tab ${tab.label} already in view`);
return;
}
tab.scrollIntoView();
await TestUtils.waitForCondition(
tabInView,
"Tab should be scrolled into view: " + tab.label
);
info(`Tab ${tab.label} was scrolled into view`);
}
let numTabs = 0;
/**
* Wait for and return the latest tab.
*
* This should be called every time a tab is created so the test can keep
* track of the expected number of tabs.
*
* @returns {MozTabmailTab} - The last tab.
*/
async function waitForNewTab() {
numTabs++;
let tabs;
await TestUtils.waitForCondition(() => {
tabs = document.querySelectorAll("tab.tabmail-tab");
return tabs.length == numTabs;
}, `Waiting for ${numTabs} tabs`);
return tabs[numTabs - 1];
}
/**
* Close a tab and wait for it to close.
*
* This should be used alongside waitForNewTab so the test can keep track of
* the expected number of tabs.
*
* @param {MozTabmailTab} - The tab to close.
*/
async function closeTab(tab) {
numTabs--;
await scrollToTab(tab);
EventUtils.synthesizeMouseAtCenter(
tab.querySelector(".tab-close-button"),
{},
window
);
await TestUtils.waitForCondition(
() => document.querySelectorAll("tab.tabmail-tab").length == numTabs,
"Waiting for tab to close"
);
}
const toolbar = document.getElementById("spacesToolbar");
/**
* Verify the current tab and space match.
*
* @param {MozTabmailTab} tab - The expected tab.
* @param {Element} spaceButton - The expected button to be shown as the
* current space in the spaces toolbar.
* @param {string} msg - A message to use in tests.
*/
async function assertTab(tab, spaceButton, msg) {
await TestUtils.waitForCondition(
() => tab.selected,
`Tab should be selected: ${msg}`
);
const current = toolbar.querySelectorAll("button.current");
Assert.equal(current.length, 1, `Should have one current space: ${msg}`);
Assert.equal(
current[0],
spaceButton,
`Current button ${current[0].id} should match: ${msg}`
);
}
/**
* Click on a tab and verify we have switched tabs and spaces.
*
* @param {MozTabmailTab} tab - The tab to click.
* @param {Element} spaceButton - The expected button to be shown as the
* current space after clicking the tab.
* @param {string} msg - A message to use in tests.
*/
async function switchTab(tab, spaceButton, msg) {
await scrollToTab(tab);
EventUtils.synthesizeMouseAtCenter(tab, {}, window);
await assertTab(tab, spaceButton, msg);
}
// -- Test initial tab --
const mailButton = document.getElementById("mailButton");
const firstTab = await waitForNewTab();
await assertTab(firstTab, mailButton, "First tab is mail tab");
await assertMailShown();
// -- Test spaces that only open one tab --
let calendarTab;
const calendarButton = document.getElementById("calendarButton");
for (const { name, button, assertShown } of [
{
name: "address book",
button: document.getElementById("addressBookButton"),
assertShown: assertAddressBookShown,
},
{
name: "calendar",
button: calendarButton,
assertShown: assertCalendarShown,
},
{
name: "tasks",
button: document.getElementById("tasksButton"),
assertShown: assertTasksShown,
},
{
name: "chat",
button: document.getElementById("chatButton"),
assertShown: assertChatShown,
},
]) {
info(`Testing ${name} space`);
// Only have option to open in new tab.
await useContextMenu(
{ button, item: newTabItem },
{ newTab: true },
`Opening ${name} tab`
);
const newTab = await waitForNewTab();
if (name == "calendar") {
calendarTab = newTab;
}
await assertTab(newTab, button, `Opened ${name} tab`);
await assertShown();
// Only have option to switch tabs.
// Doing this from the same tab does nothing.
await useContextMenu(
{ button, switchItem: 0 },
{ numSwitch: 1 },
`When ${name} tab is open`
);
// Wait one tick to allow tabs to potentially change.
await TestUtils.waitForTick();
// However, the same tab should remain shown.
await assertShown();
// Switch to first tab and back.
await switchTab(firstTab, mailButton, `${name} to first tab`);
await assertMailShown();
await useContextMenu(
{ button, switchItem: 0 },
{ numSwitch: 1 },
`Switching from first tab to ${name}`
);
await assertTab(newTab, button, `Switched from first tab to ${name}`);
await assertShown();
}
// -- Test opening mail space in a new tab --
// Open new mail tabs whilst we are still in a non-mail tab.
await useContextMenu(
{ button: mailButton, item: newTabItem },
{ newWindow: true, newTab: true, numSwitch: 1 },
"Opening the second mail tab"
);
const secondMailTab = await waitForNewTab();
await assertTab(secondMailTab, mailButton, "Opened second mail tab");
await assertMailShown();
// Displayed folder should be the same as in the first mail tab.
const [, secondMailTabInfo] =
tabmail._getTabContextForTabbyThing(secondMailTab);
await TestUtils.waitForCondition(
() => secondMailTabInfo.folder?.URI == folderB.URI,
"Should display folder B in the second mail tab"
);
secondMailTabInfo.folder = folderA;
// Open a new mail tab whilst in a mail tab.
await useContextMenu(
{ button: mailButton, item: newTabItem },
{ newWindow: true, newTab: true, numSwitch: 2 },
"Opening the third mail tab"
);
const thirdMailTab = await waitForNewTab();
await assertTab(thirdMailTab, mailButton, "Opened third mail tab");
await assertMailShown();
// Displayed folder should be the same as in the mail tab that was in view
// when the context menu was opened, rather than the folder in the first tab.
const [, thirdMailTabInfo] =
tabmail._getTabContextForTabbyThing(thirdMailTab);
await TestUtils.waitForCondition(
() => thirdMailTabInfo.folder?.URI == folderA.URI,
"Should display folder A in the third mail tab"
);
// -- Test switching between the multiple mail tabs --
await useContextMenu(
{ button: mailButton, switchItem: 1 },
{ newWindow: true, newTab: true, numSwitch: 3 },
"Switching to second mail tab"
);
await assertTab(secondMailTab, mailButton, "Switch to second mail tab");
await assertMailShown();
await useContextMenu(
{ button: mailButton, switchItem: 0 },
{ newWindow: true, newTab: true, numSwitch: 3 },
"Switching to first mail tab"
);
await assertTab(firstTab, mailButton, "Switch to first mail tab");
await assertMailShown();
await switchTab(calendarTab, calendarButton, "First mail to calendar tab");
await useContextMenu(
{ button: mailButton, switchItem: 2 },
{ newWindow: true, newTab: true, numSwitch: 3 },
"Switching to third mail tab"
);
await assertTab(thirdMailTab, mailButton, "Switch to third mail tab");
await assertMailShown();
// -- Test the mail button with multiple mail tabs --
// Clicking the mail button whilst in the mail space does nothing.
// Specifically, we do not want it to take us to the first tab.
EventUtils.synthesizeMouseAtCenter(mailButton, {}, window);
// Wait one cycle to see if the tab would change.
await TestUtils.waitForTick();
await assertTab(thirdMailTab, mailButton, "Remain in third tab");
await assertMailShown();
Assert.equal(
thirdMailTabInfo.folder.URI,
folderA.URI,
"Still display folder A in the third mail tab"
);
// Clicking the mail button whilst in a different space takes us to the first
// mail tab.
await switchTab(calendarTab, calendarButton, "Third mail to calendar tab");
EventUtils.synthesizeMouseAtCenter(mailButton, {}, window);
await assertTab(firstTab, mailButton, "Switch to the first mail tab");
await assertMailShown();
Assert.equal(
firstMailTabInfo.folder.URI,
folderB.URI,
"Still display folder B in the first mail tab"
);
// -- Test opening the mail space in a new window --
const windowPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
await useContextMenu(
{ button: mailButton, item: newWindowItem },
{ newWindow: true, newTab: true, numSwitch: 3 },
"Opening mail tab in new window"
);
const newMailWindow = await windowPromise;
const newTabmail = newMailWindow.document.getElementById("tabmail");
// Expect the same folder as the previously focused tab.
await TestUtils.waitForCondition(
() => newTabmail.currentTabInfo.folder?.URI == folderB.URI,
"Waiting for folder B to be displayed in the new window"
);
Assert.equal(
newMailWindow.document.querySelectorAll("tab.tabmail-tab").length,
1,
"Should only have one tab in the new window"
);
await assertMailShown(newMailWindow);
// -- Test opening different tabs that belong to the settings space --
const settingsButton = document.getElementById("settingsButton");
await useContextMenu(
{ button: settingsButton, item: accountItem },
{ settings: true },
"Opening account settings"
);
const accountTab = await waitForNewTab();
// Shown as part of the settings space.
await assertTab(accountTab, settingsButton, "Opened account settings tab");
await assertContentShown("about:accountsettings");
await useContextMenu(
{ button: settingsButton, item: settingsItem },
{ settings: true },
"Opening settings"
);
let settingsTab = await waitForNewTab();
// Shown as part of the settings space.
await assertTab(settingsTab, settingsButton, "Opened settings tab");
await assertSettingsShown();
await useContextMenu(
{ button: settingsButton, item: addonsItem },
{ settings: true },
"Opening add-ons"
);
const addonsTab = await waitForNewTab();
// Shown as part of the settings space.
await assertTab(addonsTab, settingsButton, "Opened add-ons tab");
await assertContentShown("about:addons");
// -- Test the settings button with multiple settings tabs --
// Clicking the settings button whilst in the settings space does nothing.
EventUtils.synthesizeMouseAtCenter(settingsButton, {}, window);
// Wait one cycle to see if the tab would change.
await TestUtils.waitForTick();
await assertTab(addonsTab, settingsButton, "Remain in add-ons tab");
await assertContentShown("about:addons");
// Clicking the settings button whilst in a different space takes us to the
// settings tab, rather than the first tab, since this is the primary tab for
// the space.
await switchTab(calendarTab, calendarButton, "Add-ons to calendar tab");
EventUtils.synthesizeMouseAtCenter(settingsButton, {}, window);
await assertTab(settingsTab, settingsButton, "Switch to the settings tab");
await assertSettingsShown();
// Clicking the settings button whilst in a different space and no settings
// tab will open a new settings tab, rather than switch to another tab in the
// settings space because they are not the primary tab for the space.
await closeTab(settingsTab);
await switchTab(calendarTab, calendarButton, "Settings to calendar tab");
EventUtils.synthesizeMouseAtCenter(settingsButton, {}, window);
settingsTab = await waitForNewTab();
await assertTab(settingsTab, settingsButton, "Re-opened settings tab");
await assertSettingsShown();
// -- Test opening different settings tabs when they already exist --
await useContextMenu(
{ button: settingsButton, item: addonsItem },
{ settings: true },
"Switching to add-ons"
);
await assertTab(addonsTab, settingsButton, "Switched to add-ons");
await assertContentShown("about:addons");
await useContextMenu(
{ button: settingsButton, item: accountItem },
{ settings: true },
"Switching to account settings"
);
await assertTab(accountTab, settingsButton, "Switched to account settings");
await assertContentShown("about:accountsettings");
await useContextMenu(
{ button: settingsButton, item: settingsItem },
{ settings: true },
"Switching to settings"
);
await assertTab(settingsTab, settingsButton, "Switched to settings");
await assertSettingsShown();
// -- Test clicking the spaces buttons when all the tabs are already open.
await sub_test_cycle_through_primary_tabs();
// Tidy up the opened window.
// FIXME: Closing the window earlier in the test causes a test failure on the
// osx build on the try server.
await BrowserTestUtils.closeWindow(newMailWindow);
});
add_task(async function testSpacesToolbarMenubar() {
document.getElementById("toolbar-menubar").removeAttribute("autohide");
const spacesToolbar = document.getElementById("spacesToolbar");
EventUtils.synthesizeMouseAtCenter(
document.getElementById("collapseButton"),
{},
window
);
Assert.ok(spacesToolbar.hidden, "The spaces toolbar is hidden");
Assert.ok(
!document.getElementById("spacesToolbarReveal").hidden,
"The status bar toggle button is visible"
);
// Test the menubar button.
const viewShownPromise = BrowserTestUtils.waitForEvent(
document.getElementById("menu_View_Popup"),
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(
document.getElementById("menu_View"),
{},
window
);
await viewShownPromise;
const toolbarsShownPromise = BrowserTestUtils.waitForEvent(
document.getElementById("view_toolbars_popup"),
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(
document.getElementById("menu_Toolbars"),
{},
window
);
await toolbarsShownPromise;
const menuButton = document.getElementById("viewToolbarsPopupSpacesToolbar");
Assert.ok(
menuButton.getAttribute("checked") != "true",
"The menu item is not checked"
);
const viewHiddenPromise = BrowserTestUtils.waitForEvent(
document.getElementById("menu_View_Popup"),
"popuphidden"
);
EventUtils.synthesizeMouseAtCenter(menuButton, {}, window);
await viewHiddenPromise;
Assert.ok(
menuButton.getAttribute("checked") == "true",
"The menu item is checked"
);
}).skip(AppConstants.platform == "macosx"); // Can't click menu bar on Mac.
add_task(async function testSpacesToolbarOSX() {
const size = document
.getElementById("spacesToolbar")
.getBoundingClientRect().width;
// By default, macOS shouldn't need any custom styling.
Assert.ok(
!document.getElementById("titlebar").hasAttribute("style"),
"The custom styling was cleared from all toolbars"
);
const styleAppliedPromise = BrowserTestUtils.waitForCondition(
() =>
document.getElementById("tabmail-tabs").getAttribute("style") ==
`margin-inline-start: ${size}px;`,
"The correct style was applied to the tabmail"
);
// Force full screen.
window.fullScreen = true;
await new Promise(resolve => requestAnimationFrame(resolve));
await styleAppliedPromise;
const styleRemovedPromise = BrowserTestUtils.waitForCondition(
() => !document.getElementById("tabmail-tabs").hasAttribute("style"),
"The custom styling was cleared from all toolbars"
);
// Restore original window size.
window.fullScreen = false;
await new Promise(resolve => requestAnimationFrame(resolve));
await styleRemovedPromise;
}).skip(AppConstants.platform != "macosx");
add_task(async function testSpacesToolbarClearedAlignment() {
// Hide the spaces toolbar to check if the style it's cleared.
window.gSpacesToolbar.toggleToolbar(true);
Assert.ok(
!document.getElementById("titlebar").hasAttribute("style") &&
!document.getElementById("navigation-toolbox").hasAttribute("style"),
"The custom styling was cleared from all toolbars"
);
});
add_task(async function testSpacesToolbarExtension() {
window.gSpacesToolbar.toggleToolbar(false);
for (let i = 0; i < 6; i++) {
await window.gSpacesToolbar.createToolbarButton(`testButton${i}`, {
title: `Title ${i}`,
iconStyles: new Map([
[
"--webextension-toolbar-image",
],
]),
});
const button = document.getElementById(`testButton${i}`);
Assert.ok(button);
Assert.equal(button.title, `Title ${i}`);
const img = button.querySelector("img");
Assert.equal(
img.style.getPropertyValue("--webextension-toolbar-image"),
`Button image should have the correct icon.`
);
const menuitem = document.getElementById(`testButton${i}-menuitem`);
Assert.ok(menuitem);
Assert.equal(menuitem.label, `Title ${i}`);
Assert.equal(
menuitem.style.getPropertyValue("--webextension-toolbar-image"),
`Menuitem should have the correct icon.`
);
const space = window.gSpacesToolbar.spaces.find(
space => space.name == `testButton${i}`
);
Assert.ok(space);
Assert.equal(
space.url,
"Added url should be correct."
);
}
for (let i = 0; i < 6; i++) {
await window.gSpacesToolbar.updateToolbarButton(`testButton${i}`, {
title: `Modified Title ${i}`,
url: `https://test.invalid/${i + 1}`,
iconStyles: new Map([
[
"--webextension-toolbar-image",
],
]),
});
const button = document.getElementById(`testButton${i}`);
Assert.ok(button);
Assert.equal(button.title, `Modified Title ${i}`);
const img = button.querySelector("img");
Assert.equal(
img.style.getPropertyValue("--webextension-toolbar-image"),
`Button image should have the correct icon.`
);
const menuitem = document.getElementById(`testButton${i}-menuitem`);
Assert.ok(menuitem);
Assert.equal(
menuitem.label,
`Modified Title ${i}`,
"Updated title should be correct."
);
Assert.equal(
menuitem.style.getPropertyValue("--webextension-toolbar-image"),
`Menuitem should have the correct icon.`
);
const space = window.gSpacesToolbar.spaces.find(
space => space.name == `testButton${i}`
);
Assert.ok(space);
Assert.equal(
space.url,
"Updated url should be correct."
);
}
const overflowButton = document.getElementById(
"spacesToolbarAddonsOverflowButton"
);
const originalHeight = window.outerHeight;
// Set a ridiculous tiny height to be sure all add-on buttons are hidden.
window.resizeTo(window.outerWidth, 300);
await new Promise(resolve => requestAnimationFrame(resolve));
await BrowserTestUtils.waitForCondition(
() => !overflowButton.hidden,
"The overflow button is visible"
);
const overflowPopup = document.getElementById("spacesToolbarAddonsPopup");
const popupshown = BrowserTestUtils.waitForEvent(overflowPopup, "popupshown");
overflowButton.click();
await popupshown;
Assert.ok(overflowPopup.hasChildNodes());
const popuphidden = BrowserTestUtils.waitForEvent(
overflowPopup,
"popuphidden"
);
// Restore the original height.
window.resizeTo(window.outerWidth, originalHeight);
await new Promise(resolve => requestAnimationFrame(resolve));
await popuphidden;
await BrowserTestUtils.waitForCondition(
() => overflowButton.hidden,
"The overflow button is hidden"
);
// Remove all previously added toolbar buttons and make sure all previously
// generate elements are properly cleared.
for (let i = 0; i < 6; i++) {
await window.gSpacesToolbar.removeToolbarButton(`testButton${i}`);
const space = window.gSpacesToolbar.spaces.find(
space => space.name == `testButton${i}`
);
Assert.ok(!space);
const button = document.getElementById(`testButton${i}`);
Assert.ok(!button);
const menuitem = document.getElementById(`testButton${i}-menuitem`);
Assert.ok(!menuitem);
}
});
add_task(function testPinnedSpacesBadge() {
window.gSpacesToolbar.toggleToolbar(true);
const spacesPinnedButton = document.getElementById("spacesPinnedButton");
const spacesPopupButtonChat = document.getElementById(
"spacesPopupButtonChat"
);
window.gSpacesToolbar.updatePinnedBadgeState();
Assert.ok(
!spacesPinnedButton.classList.contains("has-badge"),
"Pinned button does not indicate badged items without any"
);
spacesPopupButtonChat.classList.add("has-badge");
window.gSpacesToolbar.updatePinnedBadgeState();
Assert.ok(
spacesPinnedButton.classList.contains("has-badge"),
"Pinned button indicates it has badged items"
);
spacesPopupButtonChat.classList.remove("has-badge");
window.gSpacesToolbar.updatePinnedBadgeState();
Assert.ok(
!spacesPinnedButton.classList.contains("has-badge"),
"Badge state is reset from pinned button"
);
});
add_task(async function testSpacesToolbarFocusRing() {
// Make sure the spaces toolbar is visible.
window.gSpacesToolbar.toggleToolbar(false);
// Move the focus ring on the mail toolbar button.
document.getElementById("mailButton").focus();
// Collect an array of all currently visible buttons.
const buttons = [
...document.querySelectorAll(".spaces-toolbar-button:not([hidden])"),
];
// Simulate the Arrow Down keypress to make sure the correct button gets the
// focus.
for (let i = 1; i < buttons.length; i++) {
const previousElement = document.activeElement;
EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
Assert.equal(
document.activeElement.id,
buttons[i].id,
"The next button is focused"
);
Assert.ok(
document.activeElement.tabIndex == 0 && previousElement.tabIndex == -1,
"The roving tab index was updated"
);
}
// Do the same with the Arrow Up key press but reversing the array.
buttons.reverse();
for (let i = 1; i < buttons.length; i++) {
const previousElement = document.activeElement;
EventUtils.synthesizeKey("KEY_ArrowUp", {}, window);
Assert.equal(
document.activeElement.id,
buttons[i].id,
"The previous button is focused"
);
Assert.ok(
document.activeElement.tabIndex == 0 && previousElement.tabIndex == -1,
"The roving tab index was updated"
);
}
// Pressing the END key should move the focus down to the last available
// button.
EventUtils.synthesizeKey("KEY_End", {}, window);
Assert.equal(
document.activeElement.id,
"collapseButton",
"The last button is focused"
);
// Pressing the HOME key should move the focus up to the first available
// button.
EventUtils.synthesizeKey("KEY_Home", {}, window);
Assert.equal(
document.activeElement.id,
"mailButton",
"The first button is focused"
);
// Focus follows the mouse click.
EventUtils.synthesizeMouseAtCenter(
document.getElementById("calendarButton"),
{},
window
);
Assert.equal(
document.activeElement.id,
"calendarButton",
"Focus should move to the clicked calendar button"
);
// Now press a key to make sure roving index was updated with the click.
EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
Assert.equal(
document.activeElement.id,
"tasksButton",
"Focus should move to the tasks button"
);
});