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/. */
/**
* Tests the account central page displayed when the root folder of a server is
* selected in the folder tree.
*/
const { FeedUtils } = ChromeUtils.importESModule(
);
const { MockExternalProtocolService } = ChromeUtils.importESModule(
);
const tabmail = document.getElementById("tabmail");
const about3Pane = tabmail.currentAbout3Pane;
const accountCentralBrowser = about3Pane.accountCentralBrowser;
// Which buttons should be visible for which account types?
const buttonData = {
accountSettingsButton: true,
readButton: ["pop3"],
nntpSubscriptionButton: ["nntp"],
rssSubscriptionButton: ["rss"],
composeButton: ["pop3"],
searchButton: true,
filterButton: true,
e2eButton: ["pop3"],
setupEmail: true,
setupAddressBook: true,
setupCalendar: true,
setupChat: true,
setupFilelink: true,
setupFeeds: true,
setupNewsgroups: true,
importButton: true,
};
let localServer, pop3Server, nntpServer, rssServer;
add_setup(async function () {
// Force the account hub address book creation until it's the only option.
SpecialPowers.pushPrefEnv({
set: [["mail.accounthub.addressbook.enabled", true]],
});
const localAccount = MailServices.accounts.createLocalMailAccount();
localServer = localAccount.incomingServer;
localServer.prettyName = "Test Local Account";
const pop3Account = MailServices.accounts.createAccount();
pop3Account.addIdentity(MailServices.accounts.createIdentity());
pop3Account.incomingServer = MailServices.accounts.createIncomingServer(
`${pop3Account.key}user`,
"localhost",
"pop3"
);
pop3Server = pop3Account.incomingServer;
pop3Server.prettyName = "Test POP3 Account";
const nntpAccount = MailServices.accounts.createAccount();
nntpAccount.incomingServer = MailServices.accounts.createIncomingServer(
`${nntpAccount.key}user`,
"localhost",
"nntp"
);
nntpServer = nntpAccount.incomingServer;
nntpServer.prettyName = "Test NNTP Account";
const rssAccount = FeedUtils.createRssAccount("Test RSS Account");
rssServer = rssAccount.incomingServer;
MockExternalProtocolService.init();
registerCleanupFunction(function () {
MailServices.accounts.removeAccount(localAccount, false);
MailServices.accounts.removeAccount(pop3Account, false);
MailServices.accounts.removeAccount(nntpAccount, false);
MailServices.accounts.removeAccount(rssAccount, false);
MockExternalProtocolService.cleanup();
});
});
/**
* Wait for a window to open, run a callback on it, and close it.
*
* @param {string} url - The URL of the expected window.
* @param {Function} [callback] - A callback to run once the window is open
* and loaded. The callback takes the window as an argument.
*/
async function promiseWindow(url, callback) {
const win = await BrowserTestUtils.promiseAlertDialogOpen(undefined, url);
await SimpleTest.promiseFocus(win);
await callback?.(win);
await BrowserTestUtils.closeWindow(win);
await SimpleTest.promiseFocus();
}
/**
* Wait for the account hub to open, check it has the right view, and close it.
*
* @param {string} viewName - The name of the expected view.
*/
async function promiseAccountHub(viewName) {
const view = await TestUtils.waitForCondition(() => {
const container = document.querySelector("account-hub-container");
if (!container) {
return false;
}
const dialog = container.shadowRoot.querySelector(
".account-hub-dialog[open]"
);
if (!dialog) {
return false;
}
return dialog.querySelector(".account-hub-view:not([hidden])");
}, "waiting for the account hub to open");
Assert.equal(
view.localName,
viewName,
"account hub should be open at the right view"
);
EventUtils.synthesizeKey("KEY_Escape", {}, window);
await TestUtils.waitForTick();
}
/**
* Wait for a tab to open, run a callback on it, and close it.
*
* @param {string} url - The URL of the expected tab.
* @param {Function} [callback] - A callback to run once the tab is open and
* loaded. The callback takes the tab's window object as an argument.
*/
async function promiseTab(url, callback) {
const {
detail: { tabInfo },
} = await BrowserTestUtils.waitForEvent(window, "TabOpen");
await BrowserTestUtils.browserLoaded(tabInfo.browser);
await TestUtils.waitForTick();
Assert.equal(
tabInfo.browser.currentURI.spec,
url,
"correct page should be loaded in the tab"
);
await callback?.(tabInfo.browser.contentWindow);
tabmail.closeTab(tabInfo);
}
/**
* Wait for the account manager to open, check that it has the right pane
* selected, and close it.
*
* @param {string} accountTreeId - The ID of the expected pane.
*/
async function promiseAccountManager(accountTreeId) {
await promiseTab("about:accountsettings", win => {
Assert.equal(
win.document.querySelector("#accounttree .current").id,
accountTreeId,
"server should be selected in the account tree"
);
});
}
/**
* The common parts of the test that run on all server types.
*
* @param {nsIMsgIncomingServer} server
*/
async function subtest(server) {
// Display the server's root folder and wait for account central to load.
about3Pane.displayFolder(server.rootFolder);
await TestUtils.waitForCondition(
() =>
accountCentralBrowser.contentDocument.readyState == "complete" &&
accountCentralBrowser.currentURI.spec.endsWith(
encodeURIComponent(server.serverURI)
),
"waiting for account central document to load"
);
await TestUtils.waitForTick();
const win = accountCentralBrowser.contentWindow;
const doc = accountCentralBrowser.contentDocument;
// Test the page title.
Assert.equal(
doc.getElementById("accountName").textContent,
server.prettyName,
"page header should match the account name"
);
// Test the buttons that should or should not be visible.
for (const [id, types] of Object.entries(buttonData)) {
const button = doc.getElementById(id);
if (types === true || types.includes(server.type)) {
Assert.ok(BrowserTestUtils.isVisible(button), `${id} should be visible`);
} else {
Assert.ok(BrowserTestUtils.isHidden(button), `${id} should be hidden`);
}
}
// Test the account settings button.
const amPromise = promiseAccountManager(
MailServices.accounts.findAccountForServer(server).key
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("accountSettingsButton"),
{},
win
);
await amPromise;
// Test the search button.
const searchPromise = promiseWindow(
async searchWin => {
const folderPicker =
searchWin.document.getElementById("searchableFolders");
Assert.equal(
folderPicker.value,
server.rootFolder.URI,
"server should be selected in the folder picker"
);
}
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("searchButton"),
{},
win
);
await searchPromise;
// Test the filter button.
const filtersPromise = promiseWindow(
async filtersWin => {
const serverPicker = filtersWin.document.getElementById("serverMenu");
Assert.equal(
serverPicker.value,
server.rootFolder.URI,
"server should be selected in the server picker"
);
}
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("filterButton"),
{},
win
);
await filtersPromise;
}
/**
* Test a mail account. This should behave the same for all types of mail
* account, but in this case a POP3 account is used because that is less likely
* to cause the test to fail for reasons we don't care about (this test opens
* the account's inbox, and that might cause checking for mail).
*/
add_task(async function testMail() {
Services.fog.testResetFOG();
await subtest(pop3Server);
const win = accountCentralBrowser.contentWindow;
const doc = accountCentralBrowser.contentDocument;
// Test the compose button.
const composePromise = promiseWindow(
async composeWin => {
await TestUtils.waitForCondition(() => composeWin.gLoadingComplete);
}
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("composeButton"),
{},
win
);
await composePromise;
// Test the e2e button.
const amPromise = promiseAccountManager(
`${MailServices.accounts.findAccountForServer(pop3Server).key}/am-e2e.xhtml`
);
EventUtils.synthesizeMouseAtCenter(doc.getElementById("e2eButton"), {}, win);
await amPromise;
// Test the read messages button. Do this last, it changes the folder.
const folderPromise = BrowserTestUtils.waitForEvent(
window,
"folderURIChanged"
);
EventUtils.synthesizeMouseAtCenter(doc.getElementById("readButton"), {}, win);
await folderPromise;
Assert.equal(
tabmail.currentTabInfo.folder.URI,
pop3Server.serverURI + "/Inbox"
);
const events = Glean.mail.uiInteraction.testGetValue();
Assert.deepEqual(
events.map(e => e.extra.id),
[
"accountSettingsButton",
"searchButton",
"filterButton",
"composeButton",
"e2eButton",
"readButton",
],
"clicks should be recorded in telemetry"
);
});
/**
* Test a news account.
*/
add_task(async function testNews() {
Services.fog.testResetFOG();
await subtest(nntpServer);
const win = accountCentralBrowser.contentWindow;
const doc = accountCentralBrowser.contentDocument;
// Test the subscribe button.
const subscribePromise = promiseWindow(
async subscribeWin => {
const serverPicker = subscribeWin.document.getElementById("serverMenu");
Assert.equal(
serverPicker.value,
nntpServer.rootFolder.URI,
"server should be selected in the server picker"
);
}
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("nntpSubscriptionButton"),
{},
win
);
await subscribePromise;
await SimpleTest.promiseFocus();
const events = Glean.mail.uiInteraction.testGetValue();
Assert.deepEqual(
events.map(e => e.extra.id),
[
"accountSettingsButton",
"searchButton",
"filterButton",
"nntpSubscriptionButton",
],
"clicks should be recorded in telemetry"
);
});
/**
* Test a feeds account.
*/
add_task(async function testFeeds() {
Services.fog.testResetFOG();
await subtest(rssServer);
const win = accountCentralBrowser.contentWindow;
const doc = accountCentralBrowser.contentDocument;
// Test the subscribe button.
const subscribePromise = promiseWindow(
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("rssSubscriptionButton"),
{},
win
);
await subscribePromise;
await SimpleTest.promiseFocus();
const events = Glean.mail.uiInteraction.testGetValue();
Assert.deepEqual(
events.map(e => e.extra.id),
[
"accountSettingsButton",
"searchButton",
"filterButton",
"rssSubscriptionButton",
],
"clicks should be recorded in telemetry"
);
});
/**
* Test the local folders account. This task also checks the items which are
* common to all account types.
*/
add_task(async function testLocal() {
Services.fog.testResetFOG();
await subtest(localServer);
const win = accountCentralBrowser.contentWindow;
const doc = accountCentralBrowser.contentDocument;
// Test "Set Up" buttons.
const emailPromise = promiseAccountHub("account-hub-email");
EventUtils.synthesizeMouseAtCenter(doc.getElementById("setupEmail"), {}, win);
await emailPromise;
const addressBookPromise = promiseAccountHub("account-hub-address-book");
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("setupAddressBook"),
{},
win
);
await addressBookPromise;
const calendarPromise = promiseWindow(
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("setupCalendar"),
{},
win
);
await calendarPromise;
const chatPromise = promiseWindow(
);
EventUtils.synthesizeMouseAtCenter(doc.getElementById("setupChat"), {}, win);
await chatPromise;
const filelinkPromise = promiseTab("about:preferences#compose");
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("setupFilelink"),
{},
win
);
await filelinkPromise;
const feedsPromise = promiseWindow(
);
EventUtils.synthesizeMouseAtCenter(doc.getElementById("setupFeeds"), {}, win);
await feedsPromise;
const newsgroupsPromise = promiseWindow(
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("setupNewsgroups"),
{},
win
);
await newsgroupsPromise;
const importPromise = promiseTab("about:import#start");
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("importButton"),
{},
win
);
await importPromise;
// Test links.
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("donationLink"),
{},
win
);
Assert.equal(
MockExternalProtocolService.urls.length,
1,
"should have attempted to open exactly 1 URL in a browser"
);
Assert.ok(
MockExternalProtocolService.urls[0].startsWith(
),
"should have attempted to open the right URL"
);
MockExternalProtocolService.urls.length = 0;
for (const [id, url] of [
["supportLink", "https://support.mozilla.org/products/thunderbird"],
["involvedLink", "https://www.thunderbird.net/participate/"],
["developerLink", "https://developer.thunderbird.net/"],
]) {
EventUtils.synthesizeMouseAtCenter(doc.getElementById(id), {}, win);
MockExternalProtocolService.assertHasLoadedURL(url);
}
const events = Glean.mail.uiInteraction.testGetValue();
Assert.deepEqual(
events.map(e => e.extra.id),
[
"accountSettingsButton",
"searchButton",
"filterButton",
"setupEmail",
"setupAddressBook",
"setupCalendar",
"setupChat",
"setupFilelink",
"setupFeeds",
"setupNewsgroups",
"importButton",
"donationLink",
"supportLink",
"involvedLink",
"developerLink",
],
"clicks should be recorded in telemetry"
);
});
/**
* Test account central when the URL doesn't match any server. This version
* shouldn't really ever be shown, but somehow it mysteriously still is.
*/
add_task(async function testFirstRun() {
Services.fog.testResetFOG();
accountCentralBrowser.contentWindow.location =
await TestUtils.waitForCondition(
() =>
accountCentralBrowser.contentDocument.readyState == "complete" &&
accountCentralBrowser.currentURI.spec.endsWith(".xhtml?folderURI="),
"waiting for account central document to load"
);
await TestUtils.waitForTick();
const win = accountCentralBrowser.contentWindow;
const doc = accountCentralBrowser.contentDocument;
// Test the version number displayed.
Assert.equal(
doc.getElementById("version").textContent,
AppConstants.MOZ_APP_VERSION_DISPLAY,
"Thunderbird version number should be correct"
);
// Test the about dialog is opened.
const aboutPromise = promiseWindow(
);
EventUtils.synthesizeMouseAtCenter(
doc.getElementById("releasenotes"),
{},
win
);
await aboutPromise;
const events = Glean.mail.uiInteraction.testGetValue();
Assert.deepEqual(
events.map(e => e.extra.id),
["releasenotes"],
"clicks should be recorded in telemetry"
);
});