summaryrefslogtreecommitdiffstats
path: root/comm/mail/test/browser/account
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/test/browser/account')
-rw-r--r--comm/mail/test/browser/account/browser-clear.ini21
-rw-r--r--comm/mail/test/browser/account/browser.ini67
-rw-r--r--comm/mail/test/browser/account/browser_abWhitelist.js164
-rw-r--r--comm/mail/test/browser/account/browser_accountHub.js54
-rw-r--r--comm/mail/test/browser/account/browser_accountOrder.js91
-rw-r--r--comm/mail/test/browser/account/browser_accountSetupTab.js96
-rw-r--r--comm/mail/test/browser/account/browser_accountTelemetry.js278
-rw-r--r--comm/mail/test/browser/account/browser_actions.js226
-rw-r--r--comm/mail/test/browser/account/browser_archiveOptions.js200
-rw-r--r--comm/mail/test/browser/account/browser_deletion.js108
-rw-r--r--comm/mail/test/browser/account/browser_mailAccountSetupWizard.js931
-rw-r--r--comm/mail/test/browser/account/browser_manageIdentities.js325
-rw-r--r--comm/mail/test/browser/account/browser_portSetting.js87
-rw-r--r--comm/mail/test/browser/account/browser_retestConfig.js110
-rw-r--r--comm/mail/test/browser/account/browser_settingsInfrastructure.js488
-rw-r--r--comm/mail/test/browser/account/browser_tree.js208
-rw-r--r--comm/mail/test/browser/account/browser_values.js401
-rw-r--r--comm/mail/test/browser/account/head.js78
-rw-r--r--comm/mail/test/browser/account/xml/example-imap.com25
-rw-r--r--comm/mail/test/browser/account/xml/example-imap.com^headers^1
-rw-r--r--comm/mail/test/browser/account/xml/example.com24
-rw-r--r--comm/mail/test/browser/account/xml/example.com^headers^1
-rw-r--r--comm/mail/test/browser/account/xml/momo.invalid29
-rw-r--r--comm/mail/test/browser/account/xml/momo.invalid^headers^1
24 files changed, 4014 insertions, 0 deletions
diff --git a/comm/mail/test/browser/account/browser-clear.ini b/comm/mail/test/browser/account/browser-clear.ini
new file mode 100644
index 0000000000..69678d22f5
--- /dev/null
+++ b/comm/mail/test/browser/account/browser-clear.ini
@@ -0,0 +1,21 @@
+[DEFAULT]
+prefs =
+ mail.account.account1.server=
+ mail.account.account2.identities=
+ mail.account.account2.server=
+ mail.accountmanager.accounts=
+ mail.accountmanager.defaultaccount=
+ mail.accountmanager.localfoldersserver=
+ mail.spotlight.firstRunDone=true
+ mail.startup.enabledMailCheckOnce=true
+ mail.winsearch.firstRunDone=true
+ mailnews.auto_config.addons_url=about:blank
+ mailnews.auto_config_url=about:blank
+ mailnews.start_page.override_url=about:blank
+ mailnews.start_page.url=about:blank
+ datareporting.policy.dataSubmissionPolicyBypassNotification=true
+ chat.otr.enable=false
+skip-if = debug
+subsuite = thunderbird
+
+[browser_accountSetupTab.js]
diff --git a/comm/mail/test/browser/account/browser.ini b/comm/mail/test/browser/account/browser.ini
new file mode 100644
index 0000000000..bd5c4b5652
--- /dev/null
+++ b/comm/mail/test/browser/account/browser.ini
@@ -0,0 +1,67 @@
+[DEFAULT]
+head = head.js
+prefs =
+ calendar.debug.log=true
+ carddav.setup.loglevel=Debug
+ carddav.sync.loglevel=Debug
+ chat.otr.enable=false
+ datareporting.policy.dataSubmissionPolicyBypassNotification=true
+ mail.account.account1.server=server1
+ mail.account.account2.identities=id1,id2
+ mail.account.account2.server=server2
+ mail.accountmanager.accounts=account1,account2
+ mail.accountmanager.defaultaccount=account2
+ mail.accountmanager.localfoldersserver=server1
+ mail.identity.id1.fullName=Tinderbox
+ mail.identity.id1.htmlSigFormat=false
+ mail.identity.id1.smtpServer=smtp1
+ mail.identity.id1.useremail=tinderbox@foo.invalid
+ mail.identity.id1.valid=true
+ mail.identity.id2.fullName=Tinderboxpushlog
+ mail.identity.id2.htmlSigFormat=true
+ mail.identity.id2.smtpServer=smtp1
+ mail.identity.id2.useremail=tinderboxpushlog@foo.invalid
+ mail.identity.id2.valid=true
+ mail.provider.suppress_dialog_on_startup=true
+ mail.server.server1.type=none
+ mail.server.server1.userName=nobody
+ mail.server.server2.check_new_mail=false
+ mail.server.server2.directory-rel=[ProfD]Mail/tinderbox
+ mail.server.server2.download_on_biff=true
+ mail.server.server2.hostname=tinderbox123
+ mail.server.server2.login_at_startup=false
+ mail.server.server2.name=tinderbox@foo.invalid
+ mail.server.server2.type=pop3
+ mail.server.server2.userName=tinderbox
+ mail.server.server2.whiteListAbURI=
+ mail.shell.checkDefaultClient=false
+ mail.smtp.defaultserver=smtp1
+ mail.smtpserver.smtp1.hostname=tinderbox123
+ mail.smtpserver.smtp1.username=tinderbox
+ mail.smtpservers=smtp1
+ mail.spotlight.firstRunDone=true
+ mail.startup.enabledMailCheckOnce=true
+ mail.winsearch.firstRunDone=true
+ mailnews.auto_config.addons_url=about:blank
+ mailnews.auto_config_url=about:blank
+ mailnews.start_page.override_url=about:blank
+ mailnews.start_page.url=about:blank
+skip-if = debug
+subsuite = thunderbird
+support-files = xml/**
+
+[browser_abWhitelist.js]
+[browser_accountHub.js]
+[browser_accountOrder.js]
+[browser_accountTelemetry.js]
+[browser_actions.js]
+[browser_archiveOptions.js]
+[browser_deletion.js]
+[browser_mailAccountSetupWizard.js]
+[browser_manageIdentities.js]
+[browser_portSetting.js]
+skip-if = os == "win"
+[browser_retestConfig.js]
+[browser_settingsInfrastructure.js]
+[browser_tree.js]
+[browser_values.js]
diff --git a/comm/mail/test/browser/account/browser_abWhitelist.js b/comm/mail/test/browser/account/browser_abWhitelist.js
new file mode 100644
index 0000000000..be81e317a5
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_abWhitelist.js
@@ -0,0 +1,164 @@
+/* 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";
+
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var { click_account_tree_row, get_account_tree_row, open_advanced_settings } =
+ ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+ );
+var { FAKE_SERVER_HOSTNAME } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var gOldWhiteList = null;
+var gKeyString = null;
+
+var gAccount = null;
+
+add_setup(function () {
+ let server = MailServices.accounts.findServer(
+ "tinderbox",
+ FAKE_SERVER_HOSTNAME,
+ "pop3"
+ );
+ gAccount = MailServices.accounts.FindAccountForServer(server);
+ let serverKey = server.key;
+
+ gKeyString = "mail.server." + serverKey + ".whiteListAbURI";
+ gOldWhiteList = Services.prefs.getCharPref(gKeyString);
+ Services.prefs.setCharPref(gKeyString, "");
+});
+
+registerCleanupFunction(function () {
+ Services.prefs.setCharPref(gKeyString, gOldWhiteList);
+});
+
+/**
+ * First, test that when we initially load the account manager, that
+ * we're not whitelisting any address books. Then, we'll check all
+ * address books and save.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_whitelist_init_and_save(tab) {
+ // Ok, the advanced settings window is open. Let's choose
+ // the junkmail settings.
+ let accountRow = get_account_tree_row(gAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ let doc =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // At this point, we shouldn't have anything checked, but we should have
+ // the two default address books (Personal and Collected) displayed
+ let list = doc.getElementById("whiteListAbURI");
+ Assert.equal(
+ 2,
+ list.getRowCount(),
+ "There was an unexpected number of address books"
+ );
+
+ // Now we'll check both address books
+ for (let i = 0; i < list.getRowCount(); i++) {
+ let abNode = list.getItemAtIndex(i);
+ EventUtils.synthesizeMouseAtCenter(
+ abNode.firstElementChild,
+ { clickCount: 1 },
+ abNode.firstElementChild.ownerGlobal
+ );
+ }
+}
+
+/**
+ * Next, we'll make sure that the address books we checked in
+ * subtest_check_whitelist_init_and_save were properly saved.
+ * Then, we'll clear the address books and save.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_whitelist_load_and_clear(tab) {
+ let accountRow = get_account_tree_row(gAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ let doc =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+ let list = doc.getElementById("whiteListAbURI");
+ let whiteListURIs = Services.prefs.getCharPref(gKeyString).split(" ");
+
+ for (let i = 0; i < list.getRowCount(); i++) {
+ let abNode = list.getItemAtIndex(i);
+ Assert.equal(
+ true,
+ abNode.firstElementChild.checked,
+ "Should have been checked"
+ );
+ // Also ensure that the address book URI was properly saved in the
+ // prefs
+ Assert.ok(whiteListURIs.includes(abNode.getAttribute("value")));
+ // Now un-check that address book
+ EventUtils.synthesizeMouseAtCenter(
+ abNode.firstElementChild,
+ { clickCount: 1 },
+ abNode.firstElementChild.ownerGlobal
+ );
+ }
+}
+
+/**
+ * Finally, we'll make sure that the address books we cleared
+ * were actually cleared.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_whitelist_load_cleared(tab) {
+ let accountRow = get_account_tree_row(gAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ let doc =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+ let list = doc.getElementById("whiteListAbURI");
+ let whiteListURIs = "";
+
+ try {
+ whiteListURIs = Services.prefs.getCharPref(gKeyString);
+ // We should have failed here, because the pref should have been cleared
+ // out.
+ throw Error(
+ "The whitelist preference for this server wasn't properly cleared."
+ );
+ } catch (e) {}
+
+ for (let i = 0; i < list.getRowCount(); i++) {
+ let abNode = list.getItemAtIndex(i);
+ Assert.equal(
+ false,
+ abNode.firstElementChild.checked,
+ "Should not have been checked"
+ );
+ // Also ensure that the address book URI was properly cleared in the
+ // prefs
+ Assert.ok(!whiteListURIs.includes(abNode.getAttribute("value")));
+ }
+}
+
+add_task(async function test_address_book_whitelist() {
+ await open_advanced_settings(subtest_check_whitelist_init_and_save);
+ await open_advanced_settings(subtest_check_whitelist_load_and_clear);
+ await open_advanced_settings(subtest_check_whitelist_load_cleared);
+});
diff --git a/comm/mail/test/browser/account/browser_accountHub.js b/comm/mail/test/browser/account/browser_accountHub.js
new file mode 100644
index 0000000000..8fc459a666
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_accountHub.js
@@ -0,0 +1,54 @@
+/* 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";
+
+// TODO: Defer this for when the account hub replaces the account setup tab.
+// add_task(async function test_account_hub_opening_at_startup() {});
+
+add_task(async function test_account_hub_opening() {
+ // TODO: Use an actual button once it's implemented in the UI.
+ // Open the dialog.
+ await window.openAccountHub();
+
+ const hub = document.querySelector("account-hub-container");
+ await TestUtils.waitForCondition(
+ () => hub.modal,
+ "The dialog element was created"
+ );
+
+ const dialog = hub.shadowRoot.querySelector(".account-hub-dialog");
+ Assert.ok(dialog.open, "The dialog element was opened");
+
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, window);
+ await TestUtils.waitForCondition(
+ () => !dialog.open,
+ "The dialog element was closed"
+ );
+
+ // Open the dialog again.
+ await window.openAccountHub();
+ Assert.ok(dialog.open, "The dialog element was opened");
+
+ // We already have a tinderbox account, so the default header should be
+ // visible and the welcome header should be hidden.
+ Assert.ok(
+ !hub.shadowRoot.querySelector("#defaultHeader").hidden,
+ "The #defaultHeader is visible"
+ );
+ Assert.ok(
+ hub.shadowRoot.querySelector("#welcomeHeader").hidden,
+ "The #welcomeHeader is hidden"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(
+ hub.shadowRoot.querySelector("#closeButton"),
+ {},
+ window
+ );
+ await TestUtils.waitForCondition(
+ () => !dialog.open,
+ "The dialog element was closed"
+ );
+});
diff --git a/comm/mail/test/browser/account/browser_accountOrder.js b/comm/mail/test/browser/account/browser_accountOrder.js
new file mode 100644
index 0000000000..44ec226388
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_accountOrder.js
@@ -0,0 +1,91 @@
+/* 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/. */
+
+/**
+ * This test checks proper operation of the account ordering functionality in the Account manager.
+ */
+
+"use strict";
+
+var { click_account_tree_row, get_account_tree_row, open_advanced_settings } =
+ ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+ );
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var gPopAccount, gOriginalAccountCount;
+
+add_setup(function () {
+ // There may be pre-existing accounts from other tests.
+ gOriginalAccountCount = MailServices.accounts.allServers.length;
+
+ // Create a POP server
+ let popServer = MailServices.accounts
+ .createIncomingServer("nobody", "foo.invalid", "pop3")
+ .QueryInterface(Ci.nsIPop3IncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@foo.invalid";
+
+ gPopAccount = MailServices.accounts.createAccount();
+ gPopAccount.incomingServer = popServer;
+ gPopAccount.addIdentity(identity);
+
+ // Now there should be one more account.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ gOriginalAccountCount + 1
+ );
+});
+
+registerCleanupFunction(function () {
+ if (gPopAccount) {
+ // Remove our test account to leave the profile clean.
+ MailServices.accounts.removeAccount(gPopAccount);
+ gPopAccount = null;
+ }
+ // There should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, gOriginalAccountCount);
+});
+
+add_task(async function test_account_open_state() {
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_order(tab);
+ });
+});
+
+/**
+ * Check the order of the accounts.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+async function subtest_check_account_order(tab) {
+ let accountRow = get_account_tree_row(gPopAccount.key, null, tab);
+ click_account_tree_row(tab, accountRow);
+
+ let prevAccountList = MailServices.accounts.accounts.map(
+ account => account.key
+ );
+
+ // Moving the account up to reorder.
+ EventUtils.synthesizeKey("VK_UP", { altKey: true });
+ await new Promise(resolve => setTimeout(resolve));
+ let curAccountList = MailServices.accounts.accounts.map(
+ account => account.key
+ );
+ Assert.notEqual(curAccountList.join(), prevAccountList.join());
+
+ // Moving the account down, back to the starting position.
+ EventUtils.synthesizeKey("VK_DOWN", { altKey: true });
+ await new Promise(resolve => setTimeout(resolve));
+ curAccountList = MailServices.accounts.accounts.map(account => account.key);
+ Assert.equal(curAccountList.join(), prevAccountList.join());
+}
diff --git a/comm/mail/test/browser/account/browser_accountSetupTab.js b/comm/mail/test/browser/account/browser_accountSetupTab.js
new file mode 100644
index 0000000000..4fe6b74869
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_accountSetupTab.js
@@ -0,0 +1,96 @@
+/* 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";
+
+var { openAccountSetup } = ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+);
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+/**
+ * Test the ability of dismissing the account setup without triggering the
+ * generation of a local folders account nor the update of the mail UI.
+ */
+add_task(async function test_use_thunderbird_without_email() {
+ // Delete all accounts to start clean.
+ for (let account of MailServices.accounts.accounts) {
+ MailServices.accounts.removeAccount(account, true);
+ }
+
+ // Confirm that we don't have any account in our test run.
+ Assert.equal(
+ MailServices.accounts.accounts.length,
+ 0,
+ "No account currently configured"
+ );
+
+ let spacesToolbar = document.getElementById("spacesToolbar");
+ Assert.ok(spacesToolbar, "The spaces toolbar exists");
+
+ let spacesVisiblePromise = BrowserTestUtils.waitForCondition(
+ () => !spacesToolbar.hidden,
+ "The spaces toolbar is visible"
+ );
+
+ // Get the current tab, which should be the account setup tab.
+ let tab = mc.window.document.getElementById("tabmail").selectedTab;
+ Assert.equal(tab.browser.currentURI?.spec, "about:accountsetup");
+
+ let tabDocument = tab.browser.contentWindow.document;
+
+ let closeButton = tabDocument.getElementById("cancelButton");
+ closeButton.scrollIntoView();
+
+ // Close the account setup tab by clicking on the Cancel button.
+ EventUtils.synthesizeMouseAtCenter(
+ closeButton,
+ {},
+ tab.browser.contentWindow
+ );
+
+ // Confirm the exit dialog is visible.
+ Assert.ok(tabDocument.getElementById("confirmExitDialog").open);
+
+ // Check the checkbox and close the dialog.
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("useWithoutAccount"),
+ {},
+ tab.browser.contentWindow
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("exitDialogConfirmButton"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ // We should now have switched to the main mail tab.
+ Assert.equal(
+ mc.window.document.getElementById("tabmail").selectedTab.mode.name,
+ "mail3PaneTab",
+ "The currently selected tab is the primary Mail tab"
+ );
+
+ // Confirm the folder pane didn't load.
+ // Assert.ok(!mc.window.document.getElementById("tabmail").currentTabInfo.folderPaneVisible); TODO
+
+ // The spaces toolbar should be available and visible.
+ await spacesVisiblePromise;
+
+ // Confirm the pref was updated properly.
+ Assert.ok(Services.prefs.getBoolPref("app.use_without_mail_account", false));
+});
+
+registerCleanupFunction(function () {
+ // Reset the changed pref.
+ Services.prefs.setBoolPref("app.use_without_mail_account", false);
+
+ // Restore the local folders account.
+ MailServices.accounts.createLocalMailAccount();
+});
diff --git a/comm/mail/test/browser/account/browser_accountTelemetry.js b/comm/mail/test/browser/account/browser_accountTelemetry.js
new file mode 100644
index 0000000000..5488eaf9d6
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_accountTelemetry.js
@@ -0,0 +1,278 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test telemetry related to account.
+ */
+
+let { FeedUtils } = ChromeUtils.import("resource:///modules/FeedUtils.jsm");
+
+let { IMServices } = ChromeUtils.importESModule(
+ "resource:///modules/IMServices.sys.mjs"
+);
+let { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+let { MailTelemetryForTests } = ChromeUtils.import(
+ "resource:///modules/MailGlue.jsm"
+);
+
+let {
+ add_message_to_folder,
+ create_message,
+ msgGen,
+ get_special_folder,
+ create_folder,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+let { PromiseTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/PromiseTestUtils.jsm"
+);
+let { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+
+/**
+ * Check that we are counting account types.
+ */
+add_task(async function test_account_types() {
+ // Collect all added accounts to be cleaned up at the end.
+ let addedAccounts = [];
+
+ Services.telemetry.clearScalars();
+
+ const NUM_IMAP = 3;
+ const NUM_RSS = 1;
+ const NUM_IRC = 1;
+
+ // Add incoming servers.
+ let imapServer = MailServices.accounts
+ .createIncomingServer("nobody", "foo.invalid", "imap")
+ .QueryInterface(Ci.nsIImapIncomingServer);
+ let imAccount = IMServices.accounts.createAccount(
+ "telemetry-irc-user",
+ "prpl-irc"
+ );
+ imAccount.autoLogin = false;
+ let ircServer = MailServices.accounts.createIncomingServer(
+ "nobody",
+ "foo.invalid",
+ "im"
+ );
+ ircServer.wrappedJSObject.imAccount = imAccount;
+
+ // Add accounts and assign incoming servers.
+ for (let i = 0; i < NUM_IMAP; i++) {
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@foo.invalid";
+ let account = MailServices.accounts.createAccount();
+ account.incomingServer = imapServer;
+ account.addIdentity(identity);
+ addedAccounts.push(account);
+ }
+ for (let i = 0; i < NUM_RSS; i++) {
+ let account = FeedUtils.createRssAccount("rss");
+ addedAccounts.push(account);
+ }
+ for (let i = 0; i < NUM_IRC; i++) {
+ let account = MailServices.accounts.createAccount();
+ account.incomingServer = ircServer;
+ addedAccounts.push(account);
+ }
+
+ registerCleanupFunction(() => {
+ for (let account of addedAccounts) {
+ MailServices.accounts.removeAccount(account);
+ }
+ });
+
+ MailTelemetryForTests.reportAccountTypes();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+
+ // Check if we count account types correctly.
+ Assert.equal(
+ scalars["tb.account.count"].imap,
+ NUM_IMAP,
+ "IMAP account number must be correct"
+ );
+ Assert.equal(
+ scalars["tb.account.count"].rss,
+ NUM_RSS,
+ "RSS account number must be correct"
+ );
+ Assert.equal(
+ scalars["tb.account.count"].im_irc,
+ NUM_IRC,
+ "IRC account number must be correct"
+ );
+ Assert.equal(
+ scalars["tb.account.count"].none,
+ undefined,
+ "Should not report Local Folders account"
+ );
+});
+
+/**
+ * Check that we are counting account sizes.
+ */
+add_task(async function test_account_sizes() {
+ Services.telemetry.clearScalars();
+
+ const NUM_INBOX = 3;
+ const NUM_OTHER = 2;
+
+ let inbox = await get_special_folder(
+ Ci.nsMsgFolderFlags.Inbox,
+ true,
+ null,
+ false
+ );
+ let other = await create_folder("TestAccountSize");
+ for (let i = 0; i < NUM_INBOX; i++) {
+ await add_message_to_folder(
+ [inbox],
+ msgGen.makeMessage({ body: { body: `test inbox ${i}` } })
+ );
+ }
+ for (let i = 0; i < NUM_OTHER; i++) {
+ await add_message_to_folder(
+ [other],
+ msgGen.makeMessage({ body: { body: `test other ${i}` } })
+ );
+ }
+
+ MailTelemetryForTests.reportAccountSizes();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+
+ // Check if we count total messages correctly.
+ Assert.equal(
+ scalars["tb.account.total_messages"].Inbox,
+ NUM_INBOX,
+ "Number of messages in Inbox must be correct"
+ );
+ Assert.equal(
+ scalars["tb.account.total_messages"].Other,
+ NUM_OTHER,
+ "Number of messages in other folders must be correct"
+ );
+ Assert.equal(
+ scalars["tb.account.total_messages"].Total,
+ NUM_INBOX + NUM_OTHER,
+ "Number of messages in all folders must be correct"
+ );
+
+ // The folder sizes on Windows are not exactly the same with Linux/macOS.
+ function checkSize(actual, expected, message) {
+ Assert.ok(Math.abs(actual - expected) < 10, message);
+ }
+ // Check if we count size on disk correctly.
+ checkSize(
+ scalars["tb.account.size_on_disk"].Inbox,
+ 873,
+ "Size of Inbox must be correct"
+ );
+ checkSize(
+ scalars["tb.account.size_on_disk"].Other,
+ 618,
+ "Size of other folders must be correct"
+ );
+ checkSize(
+ scalars["tb.account.size_on_disk"].Total,
+ 873 + 618,
+ "Size of all folders must be correct"
+ );
+});
+
+/**
+ * Verify counting of OAuth2 providers
+ */
+add_task(async function test_account_oauth_providers() {
+ // Collect all added accounts to be cleaned up at the end
+ const addedAccounts = [];
+
+ Services.telemetry.clearScalars();
+
+ const EXPECTED_GOOGLE_COUNT = 2;
+ const EXPECTED_MICROSOFT_COUNT = 1;
+ const EXPECTED_YAHOO_AOL_COUNT = 2;
+ const EXPECTED_OTHER_COUNT = 2;
+
+ const hostnames = [
+ "imap.googlemail.com",
+ "imap.gmail.com",
+ "imap.mail.ru",
+ "imap.yandex.com",
+ "imap.mail.yahoo.com",
+ "imap.aol.com",
+ "outlook.office365.com",
+ "something.totally.unexpected",
+ ];
+
+ function createIncomingImapServer(username, hostname, authMethod) {
+ const incoming = MailServices.accounts.createIncomingServer(
+ username,
+ hostname,
+ "imap"
+ );
+
+ incoming.authMethod = authMethod;
+
+ const account = MailServices.accounts.createAccount();
+ account.incomingServer = incoming;
+
+ const identity = MailServices.accounts.createIdentity();
+ account.addIdentity(identity);
+
+ addedAccounts.push(account);
+ }
+
+ // Add incoming servers
+ let i = 0;
+ const otherAuthMethods = [
+ Ci.nsMsgAuthMethod.none,
+ Ci.nsMsgAuthMethod.passwordCleartext,
+ Ci.nsMsgAuthMethod.passwordEncrypted,
+ Ci.nsMsgAuthMethod.secure,
+ ];
+
+ for (const hostname of hostnames) {
+ // Create one with OAuth2
+ createIncomingImapServer("nobody", hostname, Ci.nsMsgAuthMethod.OAuth2);
+
+ // Create one with an arbitrary method from our list
+ createIncomingImapServer("somebody_else", hostname, otherAuthMethods[i]);
+ i = i + (1 % otherAuthMethods.length);
+ }
+
+ registerCleanupFunction(() => {
+ for (const account of addedAccounts) {
+ MailServices.accounts.removeAccount(account);
+ }
+ });
+
+ MailTelemetryForTests.reportAccountTypes();
+ const scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+
+ // Check if we count account types correctly.
+ Assert.equal(
+ scalars["tb.account.oauth2_provider_count"].google,
+ EXPECTED_GOOGLE_COUNT,
+ "should have expected number of Google accounts"
+ );
+ Assert.equal(
+ scalars["tb.account.oauth2_provider_count"].microsoft,
+ EXPECTED_MICROSOFT_COUNT,
+ "should have expected number of Microsoft accounts"
+ );
+ Assert.equal(
+ scalars["tb.account.oauth2_provider_count"].yahoo_aol,
+ EXPECTED_YAHOO_AOL_COUNT,
+ "should have expected number of Yahoo/AOL accounts"
+ );
+ Assert.equal(
+ scalars["tb.account.oauth2_provider_count"].other,
+ EXPECTED_OTHER_COUNT,
+ "should have expected number of other accounts"
+ );
+});
diff --git a/comm/mail/test/browser/account/browser_actions.js b/comm/mail/test/browser/account/browser_actions.js
new file mode 100644
index 0000000000..dff3b3dc2d
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_actions.js
@@ -0,0 +1,226 @@
+/* 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";
+
+var { click_account_tree_row, get_account_tree_row, open_advanced_settings } =
+ ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+ );
+var { close_popup, wait_for_popup_to_open } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var { content_tab_e } = ChromeUtils.import(
+ "resource://testing-common/mozmill/ContentTabHelpers.jsm"
+);
+
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var imapAccount, nntpAccount, originalAccountCount;
+
+add_setup(function () {
+ // There may be pre-existing accounts from other tests.
+ originalAccountCount = MailServices.accounts.allServers.length;
+ // There already should be a Local Folders account created.
+ // It is needed for this test.
+ Assert.ok(MailServices.accounts.localFoldersServer);
+
+ // Create an IMAP server
+ let imapServer = MailServices.accounts
+ .createIncomingServer("nobody", "example.com", "imap")
+ .QueryInterface(Ci.nsIImapIncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@example.com";
+
+ imapAccount = MailServices.accounts.createAccount();
+ imapAccount.incomingServer = imapServer;
+ imapAccount.addIdentity(identity);
+
+ // Create a NNTP server
+ let nntpServer = MailServices.accounts
+ .createIncomingServer(null, "example.nntp.invalid", "nntp")
+ .QueryInterface(Ci.nsINntpIncomingServer);
+
+ identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox2@example.com";
+
+ nntpAccount = MailServices.accounts.createAccount();
+ nntpAccount.incomingServer = nntpServer;
+ nntpAccount.addIdentity(identity);
+ // Now there should be 2 more accounts.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ originalAccountCount + 2
+ );
+});
+
+registerCleanupFunction(function () {
+ // Remove our test accounts to leave the profile clean.
+ MailServices.accounts.removeAccount(nntpAccount);
+ MailServices.accounts.removeAccount(imapAccount);
+ // There should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, originalAccountCount);
+});
+
+/**
+ * Check that the account actions for the account are enabled or disabled appropriately.
+ *
+ * @param {object} tab - The account manager tab.
+ * @param {number} accountKey - The key of the account to select.
+ * @param {boolean} isSetAsDefaultEnabled - True if the menuitem should be enabled, false otherwise.
+ * @param {boolean} isRemoveEnabled - True if the menuitem should be enabled, false otherwise.
+ * @param {boolean} isAddAccountEnabled - True if the menuitems (Add Mail Account+Add Other Account)
+ * should be enabled, false otherwise.
+ */
+async function subtest_check_account_actions(
+ tab,
+ accountKey,
+ isSetAsDefaultEnabled,
+ isRemoveEnabled,
+ isAddAccountEnabled
+) {
+ let accountRow = get_account_tree_row(accountKey, null, tab);
+ click_account_tree_row(tab, accountRow);
+
+ // click the Actions Button to bring up the popup with menuitems to test
+ let button = content_tab_e(tab, "accountActionsButton");
+ EventUtils.synthesizeMouseAtCenter(
+ button,
+ { clickCount: 1 },
+ button.ownerGlobal
+ );
+ await wait_for_popup_to_open(content_tab_e(tab, "accountActionsDropdown"));
+
+ let actionAddMailAccount = content_tab_e(tab, "accountActionsAddMailAccount");
+ Assert.notEqual(actionAddMailAccount, undefined);
+ Assert.equal(
+ !actionAddMailAccount.getAttribute("disabled"),
+ isAddAccountEnabled
+ );
+
+ let actionAddOtherAccount = content_tab_e(
+ tab,
+ "accountActionsAddOtherAccount"
+ );
+ Assert.notEqual(actionAddOtherAccount, undefined);
+ Assert.equal(
+ !actionAddOtherAccount.getAttribute("disabled"),
+ isAddAccountEnabled
+ );
+
+ let actionSetDefault = content_tab_e(tab, "accountActionsDropdownSetDefault");
+ Assert.notEqual(actionSetDefault, undefined);
+ Assert.equal(
+ !actionSetDefault.getAttribute("disabled"),
+ isSetAsDefaultEnabled
+ );
+
+ let actionRemove = content_tab_e(tab, "accountActionsDropdownRemove");
+ Assert.notEqual(actionRemove, undefined);
+ Assert.equal(!actionRemove.getAttribute("disabled"), isRemoveEnabled);
+
+ await close_popup(mc, content_tab_e(tab, "accountActionsDropdown"));
+}
+
+add_task(async function test_account_actions() {
+ // IMAP account: can be default, can be removed.
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_actions(tab, imapAccount.key, true, true, true);
+ });
+
+ // NNTP (News) account: can't be default, can be removed.
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_actions(
+ tab,
+ nntpAccount.key,
+ false,
+ true,
+ true
+ );
+ });
+
+ // Local Folders account: can't be removed, can't be default.
+ var localFoldersAccount = MailServices.accounts.FindAccountForServer(
+ MailServices.accounts.localFoldersServer
+ );
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_actions(
+ tab,
+ localFoldersAccount.key,
+ false,
+ false,
+ true
+ );
+ });
+ // SMTP server row: can't be removed, can't be default.
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_actions(tab, "smtp", false, false, true);
+ });
+
+ // on the IMAP account, disable Delete Account menu item
+ let disableItemPref = "mail.disable_button.delete_account";
+
+ // Set the pref on the default branch, otherwise .getBoolPref on it throws.
+ Services.prefs.getDefaultBranch("").setBoolPref(disableItemPref, true);
+ Services.prefs.lockPref(disableItemPref);
+
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_actions(
+ tab,
+ imapAccount.key,
+ true,
+ false,
+ true
+ );
+ });
+
+ Services.prefs.unlockPref(disableItemPref);
+ Services.prefs.getDefaultBranch("").deleteBranch(disableItemPref);
+
+ // on the IMAP account, disable Set as Default menu item
+ disableItemPref = "mail.disable_button.set_default_account";
+
+ Services.prefs.getDefaultBranch("").setBoolPref(disableItemPref, true);
+ Services.prefs.lockPref(disableItemPref);
+
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_actions(
+ tab,
+ imapAccount.key,
+ false,
+ true,
+ true
+ );
+ });
+
+ Services.prefs.unlockPref(disableItemPref);
+ Services.prefs.getDefaultBranch("").deleteBranch(disableItemPref);
+
+ // on the IMAP account, disable Add new Account menu items
+ disableItemPref = "mail.disable_new_account_addition";
+
+ Services.prefs.getDefaultBranch("").setBoolPref(disableItemPref, true);
+ Services.prefs.lockPref(disableItemPref);
+
+ await open_advanced_settings(async function (tab) {
+ await subtest_check_account_actions(
+ tab,
+ imapAccount.key,
+ true,
+ true,
+ false
+ );
+ });
+
+ Services.prefs.unlockPref(disableItemPref);
+ Services.prefs.getDefaultBranch("").deleteBranch(disableItemPref);
+});
diff --git a/comm/mail/test/browser/account/browser_archiveOptions.js b/comm/mail/test/browser/account/browser_archiveOptions.js
new file mode 100644
index 0000000000..fd8ed9868d
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_archiveOptions.js
@@ -0,0 +1,200 @@
+/* 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";
+
+var utils = ChromeUtils.import("resource://testing-common/mozmill/utils.jsm");
+
+var { click_account_tree_row, get_account_tree_row, open_advanced_settings } =
+ ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+ );
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var {
+ plan_for_modal_dialog,
+ plan_for_window_close,
+ wait_for_modal_dialog,
+ wait_for_window_close,
+} = ChromeUtils.import("resource://testing-common/mozmill/WindowHelpers.jsm");
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var defaultIdentity;
+
+add_setup(function () {
+ defaultIdentity = MailServices.accounts.defaultAccount.defaultIdentity;
+});
+
+/**
+ * Check that the archive options button is enabled or disabled appropriately.
+ *
+ * @param {object} tab - The account manager tab.
+ * @param {number} accountKey - Key of the account the check.
+ * @param {boolean} isEnabled - True if the button should be enabled, false otherwise.
+ */
+function subtest_check_archive_options_enabled(tab, accountKey, isEnabled) {
+ let accountRow = get_account_tree_row(accountKey, "am-copies.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+ let button = iframe.contentDocument.getElementById("archiveHierarchyButton");
+
+ Assert.equal(button.disabled, !isEnabled);
+}
+
+add_task(async function test_archive_options_enabled() {
+ let defaultAccount = MailServices.accounts.defaultAccount;
+ // First, create an IMAP server
+ let imapServer = MailServices.accounts
+ .createIncomingServer("nobody", "example.com", "imap")
+ .QueryInterface(Ci.nsIImapIncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@example.com";
+
+ let account = MailServices.accounts.createAccount();
+ account.incomingServer = imapServer;
+ account.addIdentity(identity);
+
+ // Then test that the archive options button is enabled/disabled appropriately
+
+ // Let the default identity archive to our IMAP folder, to ensure that the
+ // archive folder's server is used to determine the enabled/disabled state
+ // of the "archive options" button, *not* the incoming server for that
+ // identity.
+ defaultIdentity.archiveFolder = imapServer.rootFolder.URI;
+
+ imapServer.isGMailServer = false;
+ await open_advanced_settings(function (tab) {
+ subtest_check_archive_options_enabled(tab, account.key, true);
+ });
+ await open_advanced_settings(function (tab) {
+ subtest_check_archive_options_enabled(tab, defaultAccount.key, true);
+ });
+
+ imapServer.isGMailServer = true;
+ await open_advanced_settings(function (tab) {
+ subtest_check_archive_options_enabled(tab, account.key, false);
+ });
+ await open_advanced_settings(function (tab) {
+ subtest_check_archive_options_enabled(tab, defaultAccount.key, false);
+ });
+
+ MailServices.accounts.removeAccount(account);
+});
+
+async function subtest_initial_state(identity) {
+ plan_for_modal_dialog("archiveOptions", async function (ac) {
+ Assert.equal(
+ ac.window.document.getElementById("archiveGranularity").selectedIndex,
+ identity.archiveGranularity
+ );
+ Assert.equal(
+ ac.window.document.getElementById("archiveKeepFolderStructure").checked,
+ identity.archiveKeepFolderStructure
+ );
+ });
+ mc.window.openDialog(
+ "chrome://messenger/content/am-archiveoptions.xhtml",
+ "",
+ "centerscreen,chrome,modal,titlebar,resizable=yes",
+ { identity }
+ );
+ wait_for_modal_dialog("archiveOptions");
+}
+
+add_task(async function test_open_archive_options() {
+ for (let granularity = 0; granularity < 3; granularity++) {
+ defaultIdentity.archiveGranularity = granularity;
+ for (let kfs = 0; kfs < 2; kfs++) {
+ defaultIdentity.archiveKeepFolderStructure = kfs;
+ await subtest_initial_state(defaultIdentity);
+ }
+ }
+});
+
+function subtest_save_state(identity, granularity, kfs) {
+ plan_for_modal_dialog("archiveOptions", function (ac) {
+ ac.window.document.getElementById("archiveGranularity").selectedIndex =
+ granularity;
+ ac.window.document.getElementById("archiveKeepFolderStructure").checked =
+ kfs;
+ EventUtils.synthesizeKey("VK_RETURN", {}, ac.window);
+ ac.window.document.querySelector("dialog").acceptDialog();
+ });
+ mc.window.openDialog(
+ "chrome://messenger/content/am-archiveoptions.xhtml",
+ "",
+ "centerscreen,chrome,modal,titlebar,resizable=yes",
+ { identity }
+ );
+ wait_for_modal_dialog("archiveOptions");
+}
+
+add_task(function test_save_archive_options() {
+ defaultIdentity.archiveGranularity = 0;
+ defaultIdentity.archiveKeepFolderStructure = false;
+ subtest_save_state(defaultIdentity, 1, true);
+
+ Assert.equal(defaultIdentity.archiveGranularity, 1);
+ Assert.equal(defaultIdentity.archiveKeepFolderStructure, true);
+});
+
+function subtest_check_archive_enabled(tab, archiveEnabled) {
+ defaultIdentity.archiveEnabled = archiveEnabled;
+
+ click_account_tree_row(tab, 2);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+ let checkbox = iframe.contentDocument.getElementById(
+ "identity.archiveEnabled"
+ );
+
+ Assert.equal(checkbox.checked, archiveEnabled);
+}
+
+add_task(async function test_archive_enabled() {
+ await open_advanced_settings(function (amc) {
+ subtest_check_archive_enabled(amc, true);
+ });
+
+ await open_advanced_settings(function (amc) {
+ subtest_check_archive_enabled(amc, false);
+ });
+});
+
+function subtest_disable_archive(tab) {
+ defaultIdentity.archiveEnabled = true;
+ click_account_tree_row(tab, 2);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+ let checkbox = iframe.contentDocument.getElementById(
+ "identity.archiveEnabled"
+ );
+
+ Assert.ok(checkbox.checked);
+ Assert.ok(!checkbox.disabled);
+ EventUtils.synthesizeMouseAtCenter(
+ checkbox,
+ { clickCount: 1 },
+ checkbox.ownerGlobal
+ );
+ utils.waitFor(
+ () => !checkbox.checked,
+ "Archive checkbox didn't toggle to unchecked"
+ );
+
+ Assert.ok(!defaultIdentity.archiveEnabled);
+}
+
+add_task(async function test_disable_archive() {
+ await open_advanced_settings(subtest_disable_archive);
+});
diff --git a/comm/mail/test/browser/account/browser_deletion.js b/comm/mail/test/browser/account/browser_deletion.js
new file mode 100644
index 0000000000..45c8d701b1
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_deletion.js
@@ -0,0 +1,108 @@
+/* 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/. */
+
+/**
+ * This test checks proper deletion of an account from the Account manager.
+ */
+
+"use strict";
+
+var { open_advanced_settings, remove_account } = ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var gPopAccount, gImapAccount, gOriginalAccountCount;
+
+add_setup(function () {
+ // There may be pre-existing accounts from other tests.
+ gOriginalAccountCount = MailServices.accounts.allServers.length;
+
+ // Create a POP server
+ let popServer = MailServices.accounts
+ .createIncomingServer("nobody", "pop.foo.invalid", "pop3")
+ .QueryInterface(Ci.nsIPop3IncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@pop.foo.invalid";
+
+ gPopAccount = MailServices.accounts.createAccount();
+ gPopAccount.incomingServer = popServer;
+ gPopAccount.addIdentity(identity);
+
+ // Create an IMAP server
+ let imapServer = MailServices.accounts
+ .createIncomingServer("nobody", "imap.foo.invalid", "imap")
+ .QueryInterface(Ci.nsIImapIncomingServer);
+
+ identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@imap.foo.invalid";
+
+ gImapAccount = MailServices.accounts.createAccount();
+ gImapAccount.incomingServer = imapServer;
+ gImapAccount.addIdentity(identity);
+
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ gOriginalAccountCount + 2
+ );
+});
+
+registerCleanupFunction(function () {
+ // There should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, gOriginalAccountCount);
+});
+
+add_task(async function test_account_data_deletion() {
+ await open_advanced_settings(function (tab) {
+ subtest_account_data_deletion1(tab);
+ });
+
+ await open_advanced_settings(function (tab) {
+ subtest_account_data_deletion2(tab);
+ });
+});
+
+/**
+ * Bug 274452
+ * Check if files of an account are preserved.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_account_data_deletion1(tab) {
+ let accountDir = gPopAccount.incomingServer.localPath;
+ Assert.ok(accountDir.isDirectory());
+
+ // Get some existing file in the POP3 account data dir.
+ let inboxFile = accountDir.clone();
+ inboxFile.append("Inbox.msf");
+ Assert.ok(inboxFile.isFile());
+
+ remove_account(gPopAccount, tab, true, false);
+ gPopAccount = null;
+ Assert.ok(accountDir.exists());
+}
+
+/**
+ * Bug 274452
+ * Check if files of an account can be deleted.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_account_data_deletion2(tab) {
+ let accountDir = gImapAccount.incomingServer.localPath;
+ Assert.ok(accountDir.isDirectory());
+
+ // Get some file in the IMAP account data dir.
+ let inboxFile = accountDir.clone();
+ inboxFile.append("INBOX.msf");
+ Assert.ok(inboxFile.isFile());
+
+ remove_account(gImapAccount, tab, true, true);
+ gImapAccount = null;
+ Assert.ok(!accountDir.exists());
+}
diff --git a/comm/mail/test/browser/account/browser_mailAccountSetupWizard.js b/comm/mail/test/browser/account/browser_mailAccountSetupWizard.js
new file mode 100644
index 0000000000..33cc69e645
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_mailAccountSetupWizard.js
@@ -0,0 +1,931 @@
+/* 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";
+
+var { openAccountSetup, wait_for_account_tree_load } = ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+);
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var { input_value, delete_all_existing } = ChromeUtils.import(
+ "resource://testing-common/mozmill/KeyboardHelpers.jsm"
+);
+var { gMockPromptService } = ChromeUtils.import(
+ "resource://testing-common/mozmill/PromptHelpers.jsm"
+);
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+var { DNS } = ChromeUtils.import("resource:///modules/DNS.jsm");
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+let { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+var { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+var { nsMailServer } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Maild.jsm"
+);
+
+var originalAlertsServiceCID;
+// We need a mock alerts service to capture notification events when loading the
+// UI after a successful account configuration in order to catch the alert
+// triggered when trying to connect to the fake IMAP server.
+class MockAlertsService {
+ QueryInterface = ChromeUtils.generateQI(["nsIAlertsService"]);
+ showAlert() {}
+}
+
+var user = {
+ name: "Yamato Nadeshiko",
+ email: "yamato.nadeshiko@example.com",
+ password: "abc12345",
+ incomingHost: "testin.example.com",
+ outgoingHost: "testout.example.com",
+};
+var outgoingShortName = "Example Två";
+
+var imapUser = {
+ name: "John Doe",
+ email: "john.doe@example-imap.com",
+ password: "abc12345",
+ incomingHost: "testin.example-imap.com",
+ outgoingHost: "testout.example-imap.com",
+};
+
+var IMAPServer = {
+ open() {
+ const {
+ ImapDaemon,
+ ImapMessage,
+ IMAP_RFC2195_extension,
+ IMAP_RFC3501_handler,
+ mixinExtension,
+ } = ChromeUtils.import("resource://testing-common/mailnews/Imapd.jsm");
+ const { nsMailServer } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Maild.jsm"
+ );
+ IMAPServer.ImapMessage = ImapMessage;
+
+ this.daemon = new ImapDaemon();
+ this.server = new nsMailServer(daemon => {
+ let handler = new IMAP_RFC3501_handler(daemon);
+ mixinExtension(handler, IMAP_RFC2195_extension);
+
+ handler.kUsername = "john.doe@example-imap.com";
+ handler.kPassword = "abc12345";
+ handler.kAuthRequired = true;
+ handler.kAuthSchemes = ["PLAIN"];
+ return handler;
+ }, this.daemon);
+ this.server.start(1993);
+ info(`IMAP server started on port ${this.server.port}`);
+
+ registerCleanupFunction(() => this.close());
+ },
+ close() {
+ this.server.stop();
+ },
+ get port() {
+ return this.server.port;
+ },
+};
+
+var SMTPServer = {
+ open() {
+ const { SmtpDaemon, SMTP_RFC2821_handler } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Smtpd.jsm"
+ );
+ const { nsMailServer } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Maild.jsm"
+ );
+
+ this.daemon = new SmtpDaemon();
+ this.server = new nsMailServer(daemon => {
+ let handler = new SMTP_RFC2821_handler(daemon);
+ handler.kUsername = "john.doe@example-imap.com";
+ handler.kPassword = "abc12345";
+ handler.kAuthRequired = true;
+ handler.kAuthSchemes = ["PLAIN"];
+ return handler;
+ }, this.daemon);
+ this.server.start(1587);
+ info(`SMTP server started on port ${this.server.port}`);
+
+ registerCleanupFunction(() => this.close());
+ },
+ close() {
+ this.server.stop();
+ },
+ get port() {
+ return this.server.port;
+ },
+};
+
+var _srv = DNS.srv;
+var _txt = DNS.txt;
+DNS.srv = function (name) {
+ if (["_caldavs._tcp.localhost", "_carddavs._tcp.localhost"].includes(name)) {
+ return [{ prio: 0, weight: 0, host: "example.org", port: 443 }];
+ }
+ if (
+ [
+ "_caldavs._tcp.example-imap.com",
+ "_carddavs._tcp.example-imap.com",
+ ].includes(name)
+ ) {
+ return [{ prio: 0, weight: 0, host: "example.org", port: 443 }];
+ }
+ throw new Error(`Unexpected DNS SRV lookup: ${name}`);
+};
+DNS.txt = function (name) {
+ if (name == "_caldavs._tcp.localhost") {
+ return [{ data: "path=/browser/comm/calendar/test/browser/data/dns.sjs" }];
+ }
+ if (name == "_carddavs._tcp.localhost") {
+ return [
+ {
+ data: "path=/browser/comm/mail/components/addrbook/test/browser/data/dns.sjs",
+ },
+ ];
+ }
+ if (name == "_caldavs._tcp.example-imap.com") {
+ return [{ data: "path=/browser/comm/calendar/test/browser/data/dns.sjs" }];
+ }
+ if (name == "_carddavs._tcp.example-imap.com") {
+ return [
+ {
+ data: "path=/browser/comm/mail/components/addrbook/test/browser/data/dns.sjs",
+ },
+ ];
+ }
+ throw new Error(`Unexpected DNS TXT lookup: ${name}`);
+};
+
+const PREF_NAME = "mailnews.auto_config_url";
+const PREF_VALUE = Services.prefs.getCharPref(PREF_NAME);
+
+// Remove an account in the Account Manager, but not via the UI.
+function remove_account_internal(tab, account, outgoing) {
+ let win = tab.browser.contentWindow;
+
+ // Remove the account and incoming server
+ let serverId = account.incomingServer.serverURI;
+ MailServices.accounts.removeAccount(account);
+ account = null;
+ if (serverId in win.accountArray) {
+ delete win.accountArray[serverId];
+ }
+ win.selectServer(null, null);
+
+ // Remove the outgoing server
+ let smtpKey = outgoing.key;
+ MailServices.smtp.deleteServer(outgoing);
+ win.replaceWithDefaultSmtpServer(smtpKey);
+}
+
+add_task(async function test_mail_account_setup() {
+ originalAlertsServiceCID = MockRegistrar.register(
+ "@mozilla.org/alerts-service;1",
+ MockAlertsService
+ );
+
+ // Set the pref to load a local autoconfig file.
+ let url =
+ "http://mochi.test:8888/browser/comm/mail/test/browser/account/xml/";
+ Services.prefs.setCharPref(PREF_NAME, url);
+
+ let tab = await openAccountSetup();
+ let tabDocument = tab.browser.contentWindow.document;
+
+ // Input user's account information
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("realname"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ if (tabDocument.getElementById("realname").value) {
+ // If any realname is already filled, clear it out, we have our own.
+ delete_all_existing(mc, tabDocument.getElementById("realname"));
+ }
+ input_value(mc, user.name);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ input_value(mc, user.email);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ input_value(mc, user.password);
+
+ let notificationBox = tab.browser.contentWindow.gAccountSetup.notificationBox;
+
+ let notificationShowed = BrowserTestUtils.waitForCondition(
+ () =>
+ notificationBox.getNotificationWithValue("accountSetupSuccess") != null,
+ "Timeout waiting for error notification to be showed"
+ );
+
+ let popOption = tabDocument.getElementById("resultsOption-pop3");
+ let protocolPOPSelected = BrowserTestUtils.waitForCondition(
+ () => !popOption.hidden && popOption.classList.contains("selected"),
+ "Timeout waiting for the POP3 option to be visible and selected"
+ );
+
+ // Load the autoconfig file from http://localhost:433**/autoconfig/example.com
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("continueButton"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ // Wait for the successful notification to show up.
+ await notificationShowed;
+
+ // Only the POP protocol should be available, therefore we need to confirm
+ // that the UI is returning only 1 pre-selected protocol.
+ await protocolPOPSelected;
+
+ // Confirm that the IMAP and EXCHANGE options are hidden.
+ Assert.ok(tabDocument.getElementById("resultsOption-imap").hidden);
+ Assert.ok(tabDocument.getElementById("resultsOption-exchange").hidden);
+
+ // Register the prompt service to handle the confirm() dialog
+ gMockPromptService.register();
+ gMockPromptService.returnValue = true;
+
+ // Open the advanced settings (Account Manager) to create the account
+ // immediately. We use an invalid email/password so the setup will fail
+ // anyway.
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("manualConfigButton"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ await BrowserTestUtils.waitForCondition(
+ () => !tabDocument.getElementById("manualConfigArea").hidden,
+ "Timeout waiting for the manual edit area to become visible"
+ );
+
+ let tabmail = mc.window.document.getElementById("tabmail");
+ let tabChanged = BrowserTestUtils.waitForCondition(
+ () => tabmail.selectedTab != tab,
+ "Timeout waiting for the currently active tab to change"
+ );
+
+ let advancedSetupButton = tabDocument.getElementById("advancedSetupButton");
+ advancedSetupButton.scrollIntoView();
+
+ EventUtils.synthesizeMouseAtCenter(
+ advancedSetupButton,
+ {},
+ tab.browser.contentWindow
+ );
+
+ // Wait for the current Account Setup tab to be closed and the Account
+ // Settings tab to open before running other sub tests.
+ await tabChanged;
+
+ await subtest_verify_account(tabmail.selectedTab, user);
+
+ // Close the Account Settings tab.
+ tabmail.closeTab(tabmail.currentTabInfo);
+
+ // Confirm that we properly updated the folderPaneVisible attribute for the
+ // tabmail when we created the account in the background.
+ Assert.ok(tabmail.currentTabInfo.folderPaneVisible);
+
+ // Confirm that the folder pane is visible.
+ Assert.ok(BrowserTestUtils.is_visible(tabmail.currentAbout3Pane.folderTree));
+
+ let promptState = gMockPromptService.promptState;
+ Assert.equal("confirm", promptState.method);
+
+ // Clean up
+ gMockPromptService.unregister();
+ Services.prefs.setCharPref(PREF_NAME, PREF_VALUE);
+});
+
+async function subtest_verify_account(tab, user) {
+ await BrowserTestUtils.waitForCondition(
+ () => tab.browser.contentWindow.currentAccount != null,
+ "Timeout waiting for current account to become non-null"
+ );
+
+ let account = tab.browser.contentWindow.currentAccount;
+ let identity = account.defaultIdentity;
+ let incoming = account.incomingServer;
+ let outgoing = MailServices.smtp.getServerByKey(identity.smtpServerKey);
+
+ let config = {
+ "incoming server username": {
+ actual: incoming.username,
+ expected: user.email.split("@")[0],
+ },
+ // This was creating test failure.
+ //
+ // "outgoing server username": {
+ // actual: outgoing.username,
+ // expected: user.email,
+ // },
+ "incoming server hostname": {
+ // Note: N in the hostName is uppercase
+ actual: incoming.hostName,
+ expected: user.incomingHost,
+ },
+ "outgoing server hostname": {
+ // And this is lowercase
+ actual: outgoing.hostname,
+ expected: user.outgoingHost,
+ },
+ "user real name": { actual: identity.fullName, expected: user.name },
+ "user email address": { actual: identity.email, expected: user.email },
+ "outgoing description": {
+ actual: outgoing.description,
+ expected: outgoingShortName,
+ },
+ };
+
+ try {
+ for (let i in config) {
+ Assert.equal(
+ config[i].actual,
+ config[i].expected,
+ `Configured ${i} is ${config[i].actual}. It should be ${config[i].expected}.`
+ );
+ }
+ } finally {
+ remove_account_internal(tab, account, outgoing);
+ }
+}
+
+/**
+ * Make sure that we don't re-set the information we get from the config
+ * file if the password is incorrect.
+ */
+add_task(async function test_bad_password_uses_old_settings() {
+ // Set the pref to load a local autoconfig file, that will fetch the
+ // ../account/xml/example.com which contains the settings for the
+ // @example.com email account (see the 'user' object).
+ let url =
+ "http://mochi.test:8888/browser/comm/mail/test/browser/account/xml/";
+ Services.prefs.setCharPref(PREF_NAME, url);
+
+ Services.telemetry.clearScalars();
+
+ let tab = await openAccountSetup();
+ let tabDocument = tab.browser.contentWindow.document;
+
+ // Input user's account information
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("realname"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ if (tabDocument.getElementById("realname").value) {
+ // If any realname is already filled, clear it out, we have our own.
+ delete_all_existing(mc, tabDocument.getElementById("realname"));
+ }
+ input_value(mc, user.name);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ input_value(mc, user.email);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ input_value(mc, user.password);
+
+ // Load the autoconfig file from http://localhost:433**/autoconfig/example.com
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("continueButton"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ let createButton = tabDocument.getElementById("createButton");
+ await BrowserTestUtils.waitForCondition(
+ () => !createButton.hidden && !createButton.disabled,
+ "Timeout waiting for create button to become visible and active"
+ );
+
+ let notificationBox = tab.browser.contentWindow.gAccountSetup.notificationBox;
+
+ let notificationShowed = BrowserTestUtils.waitForCondition(
+ () => notificationBox.getNotificationWithValue("accountSetupError") != null,
+ "Timeout waiting for error notification to be showed"
+ );
+
+ createButton.scrollIntoView();
+ EventUtils.synthesizeMouseAtCenter(
+ createButton,
+ {},
+ tab.browser.contentWindow
+ );
+
+ await notificationShowed;
+
+ await BrowserTestUtils.waitForCondition(
+ () => !createButton.disabled,
+ "Timeout waiting for create button to become active"
+ );
+
+ let manualConfigButton = tabDocument.getElementById("manualConfigButton");
+ manualConfigButton.scrollIntoView();
+
+ EventUtils.synthesizeMouseAtCenter(
+ manualConfigButton,
+ {},
+ tab.browser.contentWindow
+ );
+
+ await BrowserTestUtils.waitForCondition(
+ () => !tabDocument.getElementById("manualConfigArea").hidden,
+ "Timeout waiting for the manual edit area to become visible"
+ );
+
+ let outgoingAuthSelect = tabDocument.getElementById("outgoingAuthMethod");
+ // Make sure the select field is inside the viewport.
+ outgoingAuthSelect.scrollIntoView();
+ outgoingAuthSelect.focus();
+
+ let popupOpened = BrowserTestUtils.waitForEvent(
+ document.getElementById("ContentSelectDropdown"),
+ "popupshown"
+ );
+ EventUtils.sendKey("space", tab.browser.contentWindow);
+ await popupOpened;
+
+ // The default value should be on "Normal password", which is after
+ // "No authentication", so we need to go up. We do this on purpose so we can
+ // properly test and track the order of options.
+ EventUtils.sendKey("up", tab.browser.contentWindow);
+
+ let userNameDisabled = BrowserTestUtils.waitForCondition(
+ () => tabDocument.getElementById("outgoingUsername").disabled,
+ "Timeout waiting for the outgoing username field to be disabled"
+ );
+ EventUtils.sendKey("return", tab.browser.contentWindow);
+
+ // Confirm that the outgoing username field is disabled.
+ await userNameDisabled;
+
+ // Revert the outgoing authentication method to "Normal Password".
+ outgoingAuthSelect.focus();
+ popupOpened = BrowserTestUtils.waitForEvent(
+ document.getElementById("ContentSelectDropdown"),
+ "popupshown"
+ );
+ // Change the outgoing authentication method to "No Authentication".
+ EventUtils.sendKey("space", tab.browser.contentWindow);
+ await popupOpened;
+
+ EventUtils.sendKey("down", tab.browser.contentWindow);
+
+ let usernameEnabled = BrowserTestUtils.waitForCondition(
+ () => !tabDocument.getElementById("outgoingUsername").disabled,
+ "Timeout waiting for the outgoing username field to be enabled"
+ );
+ EventUtils.sendKey("return", tab.browser.contentWindow);
+
+ // Confirm that the outgoing username field is enabled.
+ await usernameEnabled;
+
+ let notificationRemoved = BrowserTestUtils.waitForCondition(
+ () => notificationBox.getNotificationWithValue("accountSetupError") == null,
+ "Timeout waiting for error notification to be removed"
+ );
+
+ createButton.scrollIntoView();
+ EventUtils.synthesizeMouseAtCenter(
+ createButton,
+ {},
+ tab.browser.contentWindow
+ );
+
+ // Triggering again the "createButton" should clear previous notifications.
+ await notificationRemoved;
+
+ // Make sure all the values are the same as in the user object.
+ Assert.equal(
+ tabDocument.getElementById("outgoingHostname").value,
+ user.outgoingHost,
+ "Outgoing server changed!"
+ );
+ Assert.equal(
+ tabDocument.getElementById("incomingHostname").value,
+ user.incomingHost,
+ "incoming server changed!"
+ );
+
+ // A new error notification should appear.
+ await BrowserTestUtils.waitForCondition(
+ () => notificationBox.getNotificationWithValue("accountSetupError") != null,
+ "Timeout waiting for error notification to be showed"
+ );
+
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+ Assert.equal(
+ scalars["tb.account.failed_email_account_setup"]["xml-from-db"],
+ 1,
+ "Count of failed email account setup with xml config must be correct"
+ );
+ Assert.equal(
+ scalars["tb.account.failed_email_account_setup"].user,
+ 1,
+ "Count of failed email account setup with manual config must be correct"
+ );
+
+ // Clean up
+ Services.prefs.setCharPref(PREF_NAME, PREF_VALUE);
+
+ let closeButton = tabDocument.getElementById("cancelButton");
+ closeButton.scrollIntoView();
+
+ EventUtils.synthesizeMouseAtCenter(
+ closeButton,
+ {},
+ tab.browser.contentWindow
+ );
+});
+
+add_task(async function test_remember_password() {
+ await remember_password_test(true);
+ await remember_password_test(false);
+});
+
+/**
+ * Test remember_password checkbox behavior with
+ * signon.rememberSignons set to "aPrefValue"
+ *
+ * @param {boolean} aPrefValue - The preference value for signon.rememberSignons.
+ */
+async function remember_password_test(aPrefValue) {
+ // Save the pref for backup purpose.
+ let rememberSignons_pref_save = Services.prefs.getBoolPref(
+ "signon.rememberSignons",
+ true
+ );
+
+ Services.prefs.setBoolPref("signon.rememberSignons", aPrefValue);
+
+ let tab = await openAccountSetup();
+ let tabDocument = tab.browser.contentWindow.document;
+ let password = tabDocument.getElementById("password");
+ let passwordToggle = tabDocument.getElementById("passwordToggleButton");
+
+ // The password field is empty, so confirm that the toggle button is hidden.
+ Assert.ok(passwordToggle.hidden);
+
+ // Type something in the password field.
+ password.focus();
+ input_value(mc, "testing");
+
+ // The password toggle button should be visible now.
+ Assert.ok(!passwordToggle.hidden);
+
+ // Click on the password toggle button.
+ EventUtils.synthesizeMouseAtCenter(
+ passwordToggle,
+ {},
+ tab.browser.contentWindow
+ );
+
+ // The password field should have being turned into clear text.
+ Assert.equal(password.type, "text");
+
+ // Click on the password toggle button again.
+ EventUtils.synthesizeMouseAtCenter(
+ passwordToggle,
+ {},
+ tab.browser.contentWindow
+ );
+
+ // The password field should have being turned back into a password type.
+ Assert.equal(password.type, "password");
+
+ let rememberPassword = tabDocument.getElementById("rememberPassword");
+ Assert.ok(rememberPassword.disabled != aPrefValue);
+ Assert.equal(rememberPassword.checked, aPrefValue);
+
+ // Empty the password field.
+ delete_all_existing(mc, password);
+
+ // Restore the saved signon.rememberSignons value.
+ Services.prefs.setBoolPref(
+ "signon.rememberSignons",
+ rememberSignons_pref_save
+ );
+
+ let closeButton = tabDocument.getElementById("cancelButton");
+ closeButton.scrollIntoView();
+
+ // Close the wizard.
+ EventUtils.synthesizeMouseAtCenter(
+ closeButton,
+ {},
+ tab.browser.contentWindow
+ );
+}
+
+/**
+ * Test the full account setup with an IMAP account, verifying the correct info
+ * in the final page.
+ */
+add_task(async function test_full_account_setup() {
+ // Initialize the fake IMAP and SMTP server to simulate a real account login.
+ IMAPServer.open();
+ SMTPServer.open();
+
+ // Set the pref to load a local autoconfig file.
+ let url =
+ "http://mochi.test:8888/browser/comm/mail/test/browser/account/xml/";
+ Services.prefs.setCharPref(PREF_NAME, url);
+
+ let tab = await openAccountSetup();
+ let tabDocument = tab.browser.contentWindow.document;
+
+ // If any realname is already filled, clear it out, we have our own.
+ tabDocument.getElementById("realname").value = "";
+
+ // The focus should be on the "realname" input by default, so let's fill it.
+ input_value(mc, imapUser.name);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ input_value(mc, imapUser.email);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ input_value(mc, imapUser.password);
+
+ let notificationBox = tab.browser.contentWindow.gAccountSetup.notificationBox;
+
+ let notificationShowed = BrowserTestUtils.waitForCondition(
+ () =>
+ notificationBox.getNotificationWithValue("accountSetupSuccess") != null,
+ "Timeout waiting for error notification to be showed"
+ );
+
+ let imapOption = tabDocument.getElementById("resultsOption-imap");
+ let protocolIMAPSelected = BrowserTestUtils.waitForCondition(
+ () => !imapOption.hidden && imapOption.classList.contains("selected"),
+ "Timeout waiting for the IMAP option to be visible and selected"
+ );
+
+ // Since we're focused inside a form, pressing "Enter" should submit it.
+ EventUtils.synthesizeKey("VK_RETURN", {}, mc.window);
+
+ // Wait for the successful notification to show up.
+ await notificationShowed;
+
+ // Confirm the IMAP protocol is visible and selected.
+ await protocolIMAPSelected;
+
+ let finalViewShowed = BrowserTestUtils.waitForCondition(
+ () => !tabDocument.getElementById("successView").hidden,
+ "Timeout waiting for the final page to be visible"
+ );
+
+ let insecureDialogShowed = BrowserTestUtils.waitForCondition(
+ () => tabDocument.getElementById("insecureDialog").open,
+ "Timeout waiting for the #insecureDialog to be visible"
+ );
+
+ // Press "Enter" again to proceed with the account creation.
+ tabDocument.getElementById("createButton").focus();
+ EventUtils.synthesizeKey("VK_RETURN", {}, mc.window);
+
+ // Since we're using plain authentication in the mock IMAP server, the
+ // insecure warning dialog should appear. Let's wait for it.
+ await insecureDialogShowed;
+
+ // Click the acknowledge checkbox and confirm the insecure dialog.
+ let acknowledgeCheckbox = tabDocument.getElementById("acknowledgeWarning");
+ acknowledgeCheckbox.scrollIntoView();
+
+ EventUtils.synthesizeMouseAtCenter(
+ acknowledgeCheckbox,
+ {},
+ tab.browser.contentWindow
+ );
+
+ // Prepare to handle the linked services notification.
+ let syncingBox = tab.browser.contentWindow.gAccountSetup.syncingBox;
+
+ let syncingNotificationShowed = BrowserTestUtils.waitForCondition(
+ () => syncingBox.getNotificationWithValue("accountSetupLoading") != null,
+ "Timeout waiting for the syncing notification to be removed"
+ );
+
+ let syncingNotificationRemoved = BrowserTestUtils.waitForCondition(
+ () => !syncingBox.getNotificationWithValue("accountSetupLoading"),
+ "Timeout waiting for the syncing notification to be removed"
+ );
+
+ let confirmButton = tabDocument.getElementById("insecureConfirmButton");
+ confirmButton.scrollIntoView();
+
+ // Close the insecure dialog.
+ EventUtils.synthesizeMouseAtCenter(
+ confirmButton,
+ {},
+ tab.browser.contentWindow
+ );
+
+ // The final page should be visible.
+ await finalViewShowed;
+
+ let tabmail = mc.window.document.getElementById("tabmail");
+
+ // The tab shouldn't change even if we created a new account.
+ Assert.equal(tab, tabmail.selectedTab, "Tab should should still be the same");
+
+ // Assert the UI is properly filled with the new account info.
+ Assert.equal(
+ tabDocument.getElementById("newAccountName").textContent,
+ imapUser.name
+ );
+ Assert.equal(
+ tabDocument.getElementById("newAccountEmail").textContent,
+ imapUser.email
+ );
+ Assert.equal(
+ tabDocument.getElementById("newAccountProtocol").textContent,
+ "imap"
+ );
+
+ // The fetching of connected address books and calendars should start.
+ await syncingNotificationShowed;
+
+ // Wait for the fetching of address books and calendars to end.
+ await syncingNotificationRemoved;
+
+ // Wait for the linked address book section to be visible.
+ let addressBookSection = tabDocument.getElementById("linkedAddressBooks");
+ await TestUtils.waitForCondition(
+ () => BrowserTestUtils.is_visible(addressBookSection),
+ "linked address book section visible",
+ 250
+ );
+
+ // The section should be expanded already.
+ let abList = tabDocument.querySelector(
+ "#addressBooksSetup .linked-services-list"
+ );
+ Assert.ok(BrowserTestUtils.is_visible(abList), "address book list visible");
+
+ // Check the linked address book was found.
+ Assert.equal(abList.childElementCount, 1);
+ Assert.equal(
+ abList.querySelector("li > span.protocol-type").textContent,
+ "CardDAV"
+ );
+ Assert.equal(
+ abList.querySelector("li > span.list-item-name").textContent,
+ "You found me!"
+ );
+
+ // Connect the linked address book.
+ let abDirectoryPromise = TestUtils.topicObserved("addrbook-directory-synced");
+ EventUtils.synthesizeMouseAtCenter(
+ abList.querySelector("li > button.small-button"),
+ {},
+ tab.browser.contentWindow
+ );
+ let [abDirectory] = await abDirectoryPromise;
+ Assert.equal(abDirectory.dirName, "You found me!");
+ Assert.equal(abDirectory.dirType, Ci.nsIAbManager.CARDDAV_DIRECTORY_TYPE);
+ Assert.equal(
+ abDirectory.getStringValue("carddav.url", ""),
+ "https://example.org/browser/comm/mail/components/addrbook/test/browser/data/addressbook.sjs"
+ );
+
+ // Wait for the linked calendar section to be visible.
+ let calendarSection = tabDocument.getElementById("linkedCalendars");
+ await TestUtils.waitForCondition(
+ () => BrowserTestUtils.is_visible(calendarSection),
+ "linked calendar section visible",
+ 250
+ );
+
+ // The section should be expanded already.
+ let calendarList = tabDocument.querySelector(
+ "#calendarsSetup .linked-services-list"
+ );
+ Assert.ok(BrowserTestUtils.is_visible(calendarList), "calendar list visible");
+
+ // Check the linked calendar was found.
+ Assert.equal(calendarList.childElementCount, 2);
+ Assert.equal(
+ calendarList.querySelector("li > span.protocol-type").textContent,
+ "CalDAV"
+ );
+ Assert.equal(
+ calendarList.querySelector("li > span.list-item-name").textContent,
+ "You found me!"
+ );
+ Assert.equal(
+ calendarList.querySelector("li:nth-child(2) > span.protocol-type")
+ .textContent,
+ "CalDAV"
+ );
+ Assert.equal(
+ calendarList.querySelector("li:nth-child(2) > span.list-item-name")
+ .textContent,
+ "Röda dagar"
+ );
+
+ // Connect the linked calendar.
+ let calendarPromise = new Promise(resolve => {
+ let observer = {
+ onCalendarRegistered(calendar) {
+ cal.manager.removeObserver(this);
+ resolve(calendar);
+ },
+ onCalendarUnregistering() {},
+ onCalendarDeleting() {},
+ };
+ cal.manager.addObserver(observer);
+ });
+
+ let calendarDialogShowed = BrowserTestUtils.waitForCondition(
+ () => tabDocument.getElementById("calendarDialog").open,
+ "Timeout waiting for the #calendarDialog to be visible"
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ calendarList.querySelector("li > button.small-button"),
+ {},
+ tab.browser.contentWindow
+ );
+ await calendarDialogShowed;
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("calendarDialogConfirmButton"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ let calendar = await calendarPromise;
+ Assert.equal(calendar.name, "You found me!");
+ Assert.equal(calendar.type, "caldav");
+ // This address doesn't need to actually exist for the test to pass.
+ Assert.equal(
+ calendar.uri.spec,
+ "https://example.org/browser/comm/calendar/test/browser/data/calendar.sjs"
+ );
+
+ let logins = Services.logins.findLogins("https://example.org", null, "");
+ Assert.equal(logins.length, 1);
+ Assert.equal(
+ logins[0].username,
+ imapUser.email,
+ "username was saved for linked address book/calendar"
+ );
+ Assert.equal(
+ logins[0].password,
+ imapUser.password,
+ "password was saved for linked address book/calendar"
+ );
+
+ let tabChanged = BrowserTestUtils.waitForCondition(
+ () => tabmail.selectedTab != tab,
+ "Timeout waiting for the currently active tab to change"
+ );
+
+ let finishButton = tabDocument.getElementById("finishButton");
+ finishButton.focus();
+ finishButton.scrollIntoView();
+
+ // Close the wizard.
+ EventUtils.synthesizeMouseAtCenter(
+ finishButton,
+ {},
+ tab.browser.contentWindow
+ );
+
+ await tabChanged;
+
+ // Confirm the mail 3 pane is the currently selected tab.
+ Assert.equal(
+ tabmail.selectedTab.mode.name,
+ "mail3PaneTab",
+ "The currently selected tab is the primary Mail tab"
+ );
+
+ // Remove the address book and calendar.
+ MailServices.ab.deleteAddressBook(abDirectory.URI);
+ cal.manager.removeCalendar(calendar);
+
+ // Restore the original pref.
+ Services.prefs.setCharPref(PREF_NAME, PREF_VALUE);
+
+ // Wait for Thunderbird to connect to the server and check for messages.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 1000));
+
+ IMAPServer.close();
+ SMTPServer.close();
+ Services.logins.removeAllLogins();
+});
+
+registerCleanupFunction(function () {
+ MockRegistrar.unregister(originalAlertsServiceCID);
+ DNS.srv = _srv;
+ DNS.txt = _txt;
+});
diff --git a/comm/mail/test/browser/account/browser_manageIdentities.js b/comm/mail/test/browser/account/browser_manageIdentities.js
new file mode 100644
index 0000000000..f67f19891b
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_manageIdentities.js
@@ -0,0 +1,325 @@
+/* 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 for the account settings manage identity.
+ */
+
+"use strict";
+
+var {
+ click_account_tree_row,
+ get_account_tree_row,
+ open_advanced_settings,
+ openAccountSettings,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+);
+const { wait_for_frame_load } = ChromeUtils.import(
+ "resource://testing-common/mozmill/WindowHelpers.jsm"
+);
+var { gMockPromptService } = ChromeUtils.import(
+ "resource://testing-common/mozmill/PromptHelpers.jsm"
+);
+var { plan_for_modal_dialog, wait_for_modal_dialog } = ChromeUtils.import(
+ "resource://testing-common/mozmill/WindowHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+const { OpenPGPTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mozmill/OpenPGPTestUtils.jsm"
+);
+
+var gPopAccount, gOriginalAccountCount, gIdentitiesWin;
+
+/**
+ * Load the identities dialog.
+ *
+ * @returns {Window} The loaded window of the identities dialog.
+ */
+async function identitiesListDialogLoaded(win) {
+ let manageButton = win.document.getElementById(
+ "identity.manageIdentitiesbutton"
+ );
+ let identitiesDialogLoad = promiseLoadSubDialog(
+ "chrome://messenger/content/am-identities-list.xhtml"
+ );
+ EventUtils.synthesizeMouseAtCenter(manageButton, {}, win);
+ return identitiesDialogLoad;
+}
+
+/**
+ * Load an identity listed in the identities dialog.
+ *
+ * @param {number} identityIdx - The index of the identity, in the list.
+ * @returns {Window} The loaded window of the identities dialog.
+ */
+async function identityDialogLoaded(identityIdx) {
+ let identitiesList = gIdentitiesWin.document.getElementById("identitiesList");
+
+ // Let's dbl click to open the identity.
+ let identityDialogLoaded = promiseLoadSubDialog(
+ "chrome://messenger/content/am-identity-edit.xhtml"
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ identitiesList.children[identityIdx],
+ { clickCount: 2 },
+ gIdentitiesWin
+ );
+ return identityDialogLoaded;
+}
+
+/** Close the open dialog. */
+async function dialogClosed(win) {
+ let dialogElement = win.document.querySelector("dialog");
+ let dialogClosing = BrowserTestUtils.waitForEvent(
+ dialogElement,
+ "dialogclosing"
+ );
+ dialogElement.acceptDialog();
+ return dialogClosing;
+}
+
+add_setup(async function () {
+ // There may be pre-existing accounts from other tests.
+ gOriginalAccountCount = MailServices.accounts.allServers.length;
+
+ // Create a POP server
+ let popServer = MailServices.accounts
+ .createIncomingServer("nobody", "exampleX.invalid", "pop3")
+ .QueryInterface(Ci.nsIPop3IncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@example.invalid";
+
+ gPopAccount = MailServices.accounts.createAccount();
+ gPopAccount.incomingServer = popServer;
+ gPopAccount.addIdentity(identity);
+
+ // Now there should be one more account.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ gOriginalAccountCount + 1
+ );
+
+ let firstIdentity = gPopAccount.identities[0];
+
+ Assert.equal(
+ firstIdentity.autoEncryptDrafts,
+ true,
+ "encrypted drafts should be enabled by default"
+ );
+ Assert.equal(
+ firstIdentity.protectSubject,
+ true,
+ "protected subject should be enabled by default"
+ );
+ Assert.equal(
+ firstIdentity.signMail,
+ false,
+ "signing should be disabled by default"
+ );
+
+ firstIdentity.autoEncryptDrafts = false;
+
+ registerCleanupFunction(function rmAccount() {
+ // Remove our test account to leave the profile clean.
+ MailServices.accounts.removeAccount(gPopAccount);
+ // There should be only the original accounts left.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ gOriginalAccountCount
+ );
+ });
+
+ // Go to the account settings.
+ let tab = await openAccountSettings();
+ registerCleanupFunction(function closeTab() {
+ mc.window.document.getElementById("tabmail").closeTab(tab);
+ });
+
+ // To the account main page.
+ let accountRow = get_account_tree_row(
+ gPopAccount.key,
+ null, // "am-main.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ // Click "Manage Identities" to show the list of identities.
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+ gIdentitiesWin = await identitiesListDialogLoaded(iframe.contentWindow);
+});
+
+/**
+ * Test that adding a new identity works, and that the identity is listed
+ * once the dialog to add new identity closes.
+ */
+add_task(async function test_add_identity() {
+ let identitiesList = gIdentitiesWin.document.getElementById("identitiesList");
+
+ Assert.equal(
+ identitiesList.childElementCount,
+ 1,
+ "should start with 1 identity"
+ );
+
+ // Open the dialog to add a new identity.
+ let identityDialogLoaded = promiseLoadSubDialog(
+ "chrome://messenger/content/am-identity-edit.xhtml"
+ );
+ let addButton = gIdentitiesWin.document.getElementById("addButton");
+ EventUtils.synthesizeMouseAtCenter(addButton, {}, gIdentitiesWin);
+ let identityWin = await identityDialogLoaded;
+
+ // Fill in some values, and close. The new identity should now be listed.
+ identityWin.document.getElementById("identity.fullName").focus();
+ EventUtils.sendString("bob", identityWin);
+ identityWin.document.getElementById("identity.email").focus();
+ EventUtils.sendString("bob@openpgp.example", identityWin);
+
+ // Check the e2e tab is only available for existing identities that
+ // have the email set - that is, it should not be shown yet.
+ Assert.ok(identityWin.document.getElementById("identityE2ETab").hidden);
+
+ await dialogClosed(identityWin);
+
+ Assert.equal(
+ identitiesList.childElementCount,
+ 2,
+ "should have 2 identities now"
+ );
+});
+
+async function test_identity_idx(idx) {
+ info(`Checking identity #${idx}`);
+ let identityWin = await identityDialogLoaded(idx);
+
+ let identity = gPopAccount.identities[idx];
+ Assert.ok(!!identity, "identity #1 should be set");
+ let keyId = identity.getCharAttribute("openpgp_key_id");
+
+ // The e2e tab should now be shown.
+ Assert.ok(
+ !identityWin.document.getElementById("identityE2ETab").hidden,
+ "e2e tab should show"
+ );
+ // Click the e2e tab to switch to it (for further clicks below).
+ EventUtils.synthesizeMouseAtCenter(
+ identityWin.document.getElementById("identityE2ETab"),
+ {},
+ identityWin
+ );
+
+ Assert.equal(
+ identityWin.document.getElementById("openPgpKeyListRadio").value,
+ keyId,
+ "keyId should be correct"
+ );
+
+ Assert.equal(
+ identityWin.document
+ .getElementById("openPgpKeyListRadio")
+ .querySelectorAll("radio[selected]").length,
+ 1,
+ "Should have exactly one key selected (can be None)"
+ );
+
+ if (keyId) {
+ // Click "More information", then "Key Properties" to see that the key
+ // properties dialog opens.
+ let keyDetailsDialogLoaded = promiseLoadSubDialog(
+ "chrome://openpgp/content/ui/keyDetailsDlg.xhtml"
+ );
+ info(`Will open key details dialog for key 0x${keyId}`);
+ let arrowHead = identityWin.document.querySelector(
+ `#openPgpOption${keyId} button.arrowhead`
+ );
+ arrowHead.scrollIntoView(); // Test window is small on CI...
+ EventUtils.synthesizeMouseAtCenter(arrowHead, {}, identityWin);
+ let propsButton = identityWin.document.querySelector(
+ `#openPgpOption${keyId} button.openpgp-props-btn`
+ );
+ Assert.ok(BrowserTestUtils.is_visible(propsButton));
+ propsButton.scrollIntoView(); // Test window is small on CI...
+ EventUtils.synthesizeMouseAtCenter(propsButton, {}, identityWin);
+ let keyDetailsDialog = await keyDetailsDialogLoaded;
+ info(`Key details dialog for key 0x${keyId} loaded`);
+ keyDetailsDialog.close();
+ }
+
+ Assert.equal(
+ identityWin.document.getElementById("encryptionChoices").value,
+ identity.encryptionPolicy,
+ "Encrypt setting should be correct"
+ );
+
+ // Signing checked based on the pref.
+ Assert.equal(
+ identityWin.document.getElementById("identity_sign_mail").checked,
+ identity.signMail
+ );
+ // Disabled if the identity don't have a key configured.
+ Assert.equal(
+ identityWin.document.getElementById("identity_sign_mail").disabled,
+ !identity.getCharAttribute("openpgp_key_id")
+ );
+
+ return dialogClosed(identityWin);
+}
+
+add_task(async function test_identity_idx_1() {
+ return test_identity_idx(1);
+});
+
+add_task(async function test_identity_changes() {
+ let identity = gPopAccount.identities[1];
+
+ // Check that prefs were copied from identity 0 to identity 1
+ Assert.equal(
+ identity.autoEncryptDrafts,
+ false,
+ "encrypted drafts should be disabled in [1] because we disabled it in [0]"
+ );
+ Assert.equal(
+ identity.protectSubject,
+ true,
+ "protected subject should be enabled in [1] because it is enabled in [0]"
+ );
+ Assert.equal(
+ identity.signMail,
+ false,
+ "signing should be disabled in [1] because it is disabled in [0]"
+ );
+
+ // Let's poke identity 1 and check the changes got applied
+ // Note: can't set the prefs to encrypt/sign unless there's also a key.
+
+ let [id] = await OpenPGPTestUtils.importPrivateKey(
+ window,
+ new FileUtils.File(
+ getTestFilePath(
+ "../openpgp/data/keys/bob@openpgp.example-0xfbfcc82a015e7330-secret.asc"
+ )
+ )
+ );
+ info(`Set up openpgp key; id=${id}`);
+
+ identity.setUnicharAttribute("openpgp_key_id", id.split("0x").join(""));
+ identity.signMail = "true"; // Sign by default.
+ identity.encryptionPolicy = 2; // Require encryption.
+ info("Modified identity 1 - will check it now");
+ await test_identity_idx(1);
+
+ info("Will load identity 0 again and re-check that");
+ await test_identity_idx(0);
+});
diff --git a/comm/mail/test/browser/account/browser_portSetting.js b/comm/mail/test/browser/account/browser_portSetting.js
new file mode 100644
index 0000000000..ae2c613b56
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_portSetting.js
@@ -0,0 +1,87 @@
+/* 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";
+
+var { click_account_tree_row, get_account_tree_row, open_advanced_settings } =
+ ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+ );
+var { FAKE_SERVER_HOSTNAME, mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var { input_value, delete_all_existing } = ChromeUtils.import(
+ "resource://testing-common/mozmill/KeyboardHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var PORT_NUMBERS_TO_TEST = [
+ "110", // The original port number. We don't input this though.
+ "456", // Random port number.
+ "995", // The SSL port number.
+ "110", // Back to the original.
+];
+
+var gTestNumber;
+
+async function subtest_check_set_port_number(tab, dontSet) {
+ // This test expects the following POP account to exist by default
+ // with port number 110 and no security.
+ let server = MailServices.accounts.findServer(
+ "tinderbox",
+ FAKE_SERVER_HOSTNAME,
+ "pop3"
+ );
+ let account = MailServices.accounts.FindAccountForServer(server);
+
+ let accountRow = get_account_tree_row(account.key, "am-server.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+ let portElem = iframe.contentDocument.getElementById("server.port");
+ portElem.focus();
+
+ if (portElem.value != PORT_NUMBERS_TO_TEST[gTestNumber - 1]) {
+ throw new Error(
+ "Port Value is not " +
+ PORT_NUMBERS_TO_TEST[gTestNumber - 1] +
+ " as expected, it is: " +
+ portElem.value
+ );
+ }
+
+ if (!dontSet) {
+ delete_all_existing(mc, portElem);
+ input_value(mc, PORT_NUMBERS_TO_TEST[gTestNumber]);
+
+ await new Promise(resolve => setTimeout(resolve));
+ }
+}
+
+async function subtest_check_port_number(tab) {
+ await subtest_check_set_port_number(tab, true);
+}
+
+add_task(async function test_account_port_setting() {
+ for (
+ gTestNumber = 1;
+ gTestNumber < PORT_NUMBERS_TO_TEST.length;
+ ++gTestNumber
+ ) {
+ await open_advanced_settings(subtest_check_set_port_number);
+ }
+
+ await open_advanced_settings(subtest_check_port_number);
+
+ Assert.report(
+ false,
+ undefined,
+ undefined,
+ "Test ran to completion successfully"
+ );
+});
diff --git a/comm/mail/test/browser/account/browser_retestConfig.js b/comm/mail/test/browser/account/browser_retestConfig.js
new file mode 100644
index 0000000000..d6d66740c2
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_retestConfig.js
@@ -0,0 +1,110 @@
+/* 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";
+
+var { openAccountSetup } = ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+);
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var { input_value, delete_all_existing } = ChromeUtils.import(
+ "resource://testing-common/mozmill/KeyboardHelpers.jsm"
+);
+var { close_window } = ChromeUtils.import(
+ "resource://testing-common/mozmill/WindowHelpers.jsm"
+);
+
+var user = {
+ name: "test",
+ email: "test@momo.invalid",
+ altEmail: "test2@momo.invalid",
+};
+
+const PREF_NAME = "mailnews.auto_config_url";
+const PREF_VALUE = Services.prefs.getCharPref(PREF_NAME);
+
+add_setup(function () {
+ Services.prefs.setCharPref("mail.setup.loglevel", "All");
+
+ let url =
+ "http://mochi.test:8888/browser/comm/mail/test/browser/account/xml/";
+ Services.prefs.setCharPref(PREF_NAME, url);
+});
+
+registerCleanupFunction(function () {
+ Services.prefs.setCharPref(PREF_NAME, PREF_VALUE);
+ Services.prefs.clearUserPref("mail.setup.loglevel");
+});
+
+add_task(async function test_re_test_config() {
+ // Opening multiple windows in the same run seems to require letting the stack
+ // unwind before opening the next one, so do that here.
+ let tab = await openAccountSetup();
+ let tabDocument = tab.browser.contentWindow.document;
+ // Input user's account information
+ EventUtils.synthesizeMouseAtCenter(
+ tabDocument.getElementById("realname"),
+ {},
+ tab.browser.contentWindow
+ );
+
+ if (tabDocument.getElementById("realname").value) {
+ // If any realname is already filled, clear it out, we have our own.
+ delete_all_existing(mc, tabDocument.getElementById("realname"));
+ }
+ input_value(mc, user.name);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ input_value(mc, user.email);
+
+ // Click "continue" button.
+ let nextButton = tabDocument.getElementById("continueButton");
+ EventUtils.synthesizeMouseAtCenter(nextButton, {}, tab.browser.contentWindow);
+
+ // Wait for 'edit' button to be enabled.
+ let editButton = tabDocument.getElementById("manualConfigButton");
+ await BrowserTestUtils.waitForCondition(
+ () => !editButton.hidden && !editButton.disabled,
+ "Timeout waiting for edit button to become visible and active"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(editButton, {}, tab.browser.contentWindow);
+
+ // Click "re-test" button.
+ let testButton = tabDocument.getElementById("reTestButton");
+ EventUtils.synthesizeMouseAtCenter(testButton, {}, tab.browser.contentWindow);
+
+ await BrowserTestUtils.waitForCondition(
+ () => !testButton.disabled,
+ "Timeout waiting for re-test button to become active"
+ );
+
+ // There used to be a "start over" button (line commented out below). Now just
+ // changing the value of the email field does the trick.
+ tabDocument.getElementById("realname").focus();
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+ tabDocument.getElementById("email").focus();
+ input_value(mc, user.altEmail);
+ EventUtils.synthesizeKey("VK_TAB", {}, mc.window);
+
+ // Wait for the "continue" button to be back, which means we're back to the
+ // original state.
+ await BrowserTestUtils.waitForCondition(
+ () => !nextButton.hidden,
+ "Timeout waiting for continue button to become visible"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(nextButton, {}, tab.browser.contentWindow);
+
+ // Previously, we'd switched to the manual editing state. Now we've started
+ // over, we should make sure the information is presented back in its original
+ // "automatic" mode.
+ Assert.ok(
+ tabDocument.getElementById("manualConfigArea").hidden,
+ "We're not back to the original state!"
+ );
+
+ mc.window.document.getElementById("tabmail").closeTab(tab);
+});
diff --git a/comm/mail/test/browser/account/browser_settingsInfrastructure.js b/comm/mail/test/browser/account/browser_settingsInfrastructure.js
new file mode 100644
index 0000000000..184479c5e8
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_settingsInfrastructure.js
@@ -0,0 +1,488 @@
+/* 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/. */
+
+/**
+ * This test checks proper operation of the account settings panes infrastructure
+ * in the Account manager. E.g. if the values of elements are properly stored when
+ * panes are switched.
+ *
+ * New checks can be added to it as needed.
+ */
+
+"use strict";
+
+var { click_account_tree_row, get_account_tree_row, open_advanced_settings } =
+ ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+ );
+var { FAKE_SERVER_HOSTNAME } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var gPopAccount, gImapAccount, gOriginalAccountCount;
+
+add_setup(function () {
+ // There may be pre-existing accounts from other tests.
+ gOriginalAccountCount = MailServices.accounts.allServers.length;
+
+ // Create a POP server
+ let popServer = MailServices.accounts
+ .createIncomingServer("nobody", "pop.invalid", "pop3")
+ .QueryInterface(Ci.nsIPop3IncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@pop.invalid";
+
+ gPopAccount = MailServices.accounts.createAccount();
+ gPopAccount.incomingServer = popServer;
+ gPopAccount.addIdentity(identity);
+
+ // Create an IMAP server
+ let imapServer = MailServices.accounts
+ .createIncomingServer("nobody", "imap.invalid", "imap")
+ .QueryInterface(Ci.nsIImapIncomingServer);
+
+ identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@imap.invalid";
+
+ gImapAccount = MailServices.accounts.createAccount();
+ gImapAccount.incomingServer = imapServer;
+ gImapAccount.addIdentity(identity);
+
+ // Now there should be one more account.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ gOriginalAccountCount + 2
+ );
+});
+
+registerCleanupFunction(function () {
+ // Remove our test accounts to leave the profile clean.
+ MailServices.accounts.removeAccount(gPopAccount);
+ MailServices.accounts.removeAccount(gImapAccount);
+
+ // There should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, gOriginalAccountCount);
+});
+
+/**
+ * Bug 525024.
+ * Check that the options in the server pane are properly preserved across
+ * pane switches.
+ */
+add_task(async function test_account_dot_IDs() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_dot_IDs(tab);
+ });
+});
+
+/**
+ * Check that the options in the server pane are stored even if the id
+ * of the element contains multiple dots (not used in standard TB yet
+ * but extensions may want it).
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_account_dot_IDs(tab) {
+ let accountRow = get_account_tree_row(
+ gPopAccount.key,
+ "am-server.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+ // Check whether a standard element with "server.loginAtStartUp" stores its
+ // value properly.
+ let loginCheck = iframe.getElementById("server.loginAtStartUp");
+ Assert.ok(!loginCheck.checked);
+ EventUtils.synthesizeMouseAtCenter(loginCheck, {}, loginCheck.ownerGlobal);
+
+ accountRow = get_account_tree_row(gPopAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ accountRow = get_account_tree_row(gPopAccount.key, "am-server.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ // Re-assign iframe.contentDocument because it was lost when changing panes
+ // (uses loadURI to load a new document).
+ iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // Check by element properties.
+ loginCheck = iframe.getElementById("server.loginAtStartUp");
+ Assert.ok(loginCheck.checked);
+
+ // Check for correct value in the accountValues array, that will be saved into prefs.
+ let rawCheckValue = tab.browser.contentWindow.getAccountValue(
+ gPopAccount,
+ tab.browser.contentWindow.getValueArrayFor(gPopAccount),
+ "server",
+ "loginAtStartUp",
+ null,
+ false
+ );
+
+ Assert.ok(rawCheckValue);
+
+ // The "server.login.At.StartUp" value does not exist yet, so the value should be 'null'.
+ rawCheckValue = tab.browser.contentWindow.getAccountValue(
+ gPopAccount,
+ tab.browser.contentWindow.getValueArrayFor(gPopAccount),
+ "server",
+ "login.At.StartUp",
+ null,
+ false
+ );
+ Assert.equal(rawCheckValue, null);
+
+ // Change the ID so that "server.login.At.StartUp" exists now.
+ loginCheck.id = "server.login.At.StartUp";
+
+ EventUtils.synthesizeMouseAtCenter(loginCheck, {}, loginCheck.ownerGlobal);
+ EventUtils.synthesizeMouseAtCenter(loginCheck, {}, loginCheck.ownerGlobal);
+
+ // Check for correct value in the accountValues array, that will be saved into prefs.
+ rawCheckValue = tab.browser.contentWindow.getAccountValue(
+ gPopAccount,
+ tab.browser.contentWindow.getValueArrayFor(gPopAccount),
+ "server",
+ "login.At.StartUp",
+ null,
+ false
+ );
+
+ Assert.ok(rawCheckValue);
+}
+
+/**
+ * Test for bug 807101.
+ * Check if form controls are properly disabled when their attached prefs are locked.
+ */
+add_task(async function test_account_locked_prefs() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_locked_prefs_addressing(tab);
+ });
+
+ await open_advanced_settings(function (tab) {
+ subtest_check_locked_prefs_server(tab);
+ });
+});
+
+/**
+ * Check that the LDAP server selection elements (radio group) are properly
+ * disabled when their attached pref (prefstring attribute) is locked.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_locked_prefs_addressing(tab) {
+ let accountRow = get_account_tree_row(
+ gPopAccount.key,
+ "am-addressing.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // By default, 'use global LDAP server preferences' is set, not the
+ // 'different LDAP server'.
+ let useLDAPdirectory = iframe.getElementById("directories");
+ Assert.ok(!useLDAPdirectory.selected);
+
+ // So the server selector is disabled.
+ let LDAPdirectory = iframe.getElementById("identity.directoryServer");
+ Assert.ok(LDAPdirectory.disabled);
+
+ // And the Edit button too.
+ let LDAPeditButton = iframe.getElementById("editButton");
+ Assert.ok(LDAPeditButton.disabled);
+
+ // Now toggle the 'different LDAP server' on. The server selector
+ // and edit button should enable.
+ EventUtils.synthesizeMouseAtCenter(
+ useLDAPdirectory,
+ {},
+ useLDAPdirectory.ownerGlobal
+ );
+ Assert.ok(!LDAPdirectory.disabled);
+ Assert.ok(!LDAPeditButton.disabled);
+
+ // Lock the pref for the server selector.
+ let prefstring = LDAPdirectory.getAttribute("prefstring");
+ let controlPref = prefstring.replace(
+ "%identitykey%",
+ gPopAccount.defaultIdentity.key
+ );
+ Services.prefs.getDefaultBranch("").setBoolPref(controlPref, "xxx");
+ Services.prefs.lockPref(controlPref);
+
+ // Refresh the pane by switching to another one.
+ accountRow = get_account_tree_row(gPopAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ accountRow = get_account_tree_row(
+ gPopAccount.key,
+ "am-addressing.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ // Re-assign iframe.contentDocument because it was lost when changing panes
+ // (uses loadURI to load a new document).
+ iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // We are now back and the 'different LDAP server' should still be selected
+ // (the setting was saved).
+ useLDAPdirectory = iframe.getElementById("directories");
+ Assert.ok(useLDAPdirectory.selected);
+
+ // But now the server selector should be disabled due to locked pref.
+ LDAPdirectory = iframe.getElementById("identity.directoryServer");
+ Assert.ok(LDAPdirectory.disabled);
+
+ // The edit button still enabled (does not depend on the same pref lock)
+ LDAPeditButton = iframe.getElementById("editButton");
+ Assert.ok(!LDAPeditButton.disabled);
+
+ // Unlock the pref to clean up.
+ Services.prefs.unlockPref(controlPref);
+ Services.prefs.getDefaultBranch("").deleteBranch(controlPref);
+}
+
+/**
+ * Check that the POP3 'keep on server' settings elements (2-level
+ * checkboxes + textbox) are properly disabled when their attached pref
+ * (prefstring attribute) is locked.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_locked_prefs_server(tab) {
+ let accountRow = get_account_tree_row(
+ gPopAccount.key,
+ "am-server.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // Top level leaveOnServer checkbox, disabled by default.
+ let leaveOnServer = iframe.getElementById("pop3.leaveMessagesOnServer");
+ Assert.ok(!leaveOnServer.disabled);
+ Assert.ok(!leaveOnServer.checked);
+
+ // Second level deleteByAge checkbox, disabled by default.
+ let deleteByAge = iframe.getElementById("pop3.deleteByAgeFromServer");
+ Assert.ok(deleteByAge.disabled);
+ Assert.ok(!deleteByAge.checked);
+
+ // Third level daysToLeave textbox, disabled by default.
+ let daysToLeave = iframe.getElementById("pop3.numDaysToLeaveOnServer");
+ Assert.ok(daysToLeave.disabled);
+
+ // When leaveOnServer is checked, only deleteByAge will get enabled.
+ EventUtils.synthesizeMouseAtCenter(
+ leaveOnServer,
+ {},
+ leaveOnServer.ownerGlobal
+ );
+ Assert.ok(leaveOnServer.checked);
+ Assert.ok(!deleteByAge.disabled);
+ Assert.ok(daysToLeave.disabled);
+
+ // When deleteByAge is checked, daysToLeave will get enabled.
+ EventUtils.synthesizeMouseAtCenter(deleteByAge, {}, deleteByAge.ownerGlobal);
+ Assert.ok(deleteByAge.checked);
+ Assert.ok(!daysToLeave.disabled);
+
+ // Lock the pref deleteByAge checkbox (middle of the element hierarchy).
+ let prefstring = deleteByAge.getAttribute("prefstring");
+ let controlPref = prefstring.replace(
+ "%serverkey%",
+ gPopAccount.incomingServer.key
+ );
+ Services.prefs.getDefaultBranch("").setBoolPref(controlPref, true);
+ Services.prefs.lockPref(controlPref);
+
+ // Refresh the pane by switching to another one.
+ accountRow = get_account_tree_row(gPopAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ accountRow = get_account_tree_row(gPopAccount.key, "am-server.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ // Re-assign iframe.contentDocument because it was lost when changing panes
+ // (uses loadURI to load a new document).
+ iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // Now leaveOnServer was preserved as checked.
+ leaveOnServer = iframe.getElementById("pop3.leaveMessagesOnServer");
+ Assert.ok(!leaveOnServer.disabled);
+ Assert.ok(leaveOnServer.checked);
+
+ // Now deleteByAge was preserved as checked but is locked/disabled.
+ deleteByAge = iframe.getElementById("pop3.deleteByAgeFromServer");
+ Assert.ok(deleteByAge.disabled);
+ Assert.ok(deleteByAge.checked);
+
+ // Because deleteByAge is checked, daysToLeave should be enabled.
+ daysToLeave = iframe.getElementById("pop3.numDaysToLeaveOnServer");
+ Assert.ok(!daysToLeave.disabled);
+
+ // When leaveOnserver is unchecked, both of deleteByAge and daysToLeave
+ // should get disabled.
+ EventUtils.synthesizeMouseAtCenter(
+ leaveOnServer,
+ {},
+ leaveOnServer.ownerGlobal
+ );
+ Assert.ok(!leaveOnServer.disabled);
+ Assert.ok(!leaveOnServer.checked);
+
+ Assert.ok(deleteByAge.disabled);
+ Assert.ok(deleteByAge.checked);
+ Assert.ok(daysToLeave.disabled);
+
+ // Unlock the pref to clean up.
+ Services.prefs.unlockPref(controlPref);
+ Services.prefs.getDefaultBranch("").deleteBranch(controlPref);
+}
+
+/**
+ * Bug 530142.
+ * Check that that if one field is set to a value, switching directly to another
+ * account pane showing the same field really loads the value from the new account,
+ * even when empty. This is tested on the Reply-To field.
+ */
+add_task(async function test_replyTo_leak() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_replyTo_leak(tab);
+ });
+});
+
+/**
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_replyTo_leak(tab) {
+ let accountRow = get_account_tree_row(gPopAccount.key, null, tab);
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // The Reply-To field should be empty.
+ let replyAddress = iframe.getElementById("identity.replyTo");
+ Assert.equal(replyAddress.value, "");
+
+ // Now we set a value into it and switch to another account, the main pane.
+ replyAddress.value = "somewhere@else.com";
+
+ // This test expects the following POP account to exist by default
+ // in the test profile with port number 110 and no security.
+ let firstServer = MailServices.accounts.findServer(
+ "tinderbox",
+ FAKE_SERVER_HOSTNAME,
+ "pop3"
+ );
+ let firstAccount = MailServices.accounts.FindAccountForServer(firstServer);
+
+ accountRow = get_account_tree_row(firstAccount.key, null, tab);
+ click_account_tree_row(tab, accountRow);
+
+ // the Reply-To field should be empty as this account does not have it set.
+ replyAddress = iframe.getElementById("identity.replyTo");
+ Assert.equal(replyAddress.value, "");
+}
+
+/**
+ * Test for bug 804091.
+ * Check if onchange handlers are properly executed when panes are switched.
+ */
+add_task(async function test_account_onchange_handler() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_onchange_handler(tab);
+ });
+});
+
+/**
+ * Check if onchange handlers are properly executed when panes are switched.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_onchange_handler(tab) {
+ let accountRow = get_account_tree_row(
+ gImapAccount.key,
+ "am-offline.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ let autoSync = iframe.getElementById("autosyncValue");
+ // 30 is the default value so check if we are in clean state.
+ Assert.equal(autoSync.value, 30);
+
+ let autoSyncInterval = iframe.getElementById("autosyncInterval");
+ // 1 is the default value and means the 30 is in days.
+ Assert.equal(autoSyncInterval.value, 1);
+
+ // Now type in 35 (days).
+ let byAge = iframe.getElementById("useAutosync.ByAge");
+ EventUtils.synthesizeMouseAtCenter(byAge, {}, byAge.ownerGlobal);
+ autoSync.select();
+ autoSync.focus();
+ EventUtils.sendString("35", mc.window);
+
+ // Immediately switch to another pane and back.
+ accountRow = get_account_tree_row(gImapAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ accountRow = get_account_tree_row(gImapAccount.key, "am-offline.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ iframe =
+ tab.browser.contentWindow.document.getElementById(
+ "contentFrame"
+ ).contentDocument;
+
+ // The pane optimized the entered value a bit. So now we should find 5.
+ autoSync = iframe.getElementById("autosyncValue");
+ Assert.equal(autoSync.value, 5);
+
+ // And the unit is 7 days = week.
+ autoSyncInterval = iframe.getElementById("autosyncInterval");
+ Assert.equal(autoSyncInterval.value, 7);
+}
diff --git a/comm/mail/test/browser/account/browser_tree.js b/comm/mail/test/browser/account/browser_tree.js
new file mode 100644
index 0000000000..547e6de489
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_tree.js
@@ -0,0 +1,208 @@
+/* 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/. */
+
+/**
+ * This test checks proper operation of the account tree in the Account manager.
+ */
+
+"use strict";
+
+var {
+ click_account_tree_row,
+ get_account_tree_row,
+ open_advanced_settings,
+ remove_account,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var { content_tab_e } = ChromeUtils.import(
+ "resource://testing-common/mozmill/ContentTabHelpers.jsm"
+);
+
+var gPopAccount, gOriginalAccountCount;
+
+add_setup(function () {
+ // There may be pre-existing accounts from other tests.
+ gOriginalAccountCount = MailServices.accounts.allServers.length;
+
+ // Create a POP server
+ let popServer = MailServices.accounts
+ .createIncomingServer("nobody", "foo.invalid", "pop3")
+ .QueryInterface(Ci.nsIPop3IncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@foo.invalid";
+
+ gPopAccount = MailServices.accounts.createAccount();
+ gPopAccount.incomingServer = popServer;
+ gPopAccount.addIdentity(identity);
+
+ // Now there should be one more account.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ gOriginalAccountCount + 1
+ );
+});
+
+registerCleanupFunction(function () {
+ if (gPopAccount) {
+ // Remove our test account to leave the profile clean.
+ MailServices.accounts.removeAccount(gPopAccount);
+ gPopAccount = null;
+ }
+ // There should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, gOriginalAccountCount);
+});
+
+/**
+ * Test for bug 536248.
+ * Check if the account manager dialog remembers the open state of accounts.
+ */
+add_task(async function test_account_open_state() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_open_state(tab, true);
+ });
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_open_state(tab, false);
+ });
+ // After this test all the accounts must be "open".
+});
+
+/**
+ * Check if the open state of accounts is in the wished state.
+ *
+ * @param {object} tab - The account manager tab.
+ * @param {boolean} wishedState - The open state in which the account row should be found.
+ */
+function subtest_check_account_open_state(tab, wishedState) {
+ let accountRow = get_account_tree_row(gPopAccount.key, null, tab);
+ click_account_tree_row(tab, accountRow);
+
+ // See if the account row is in the wished open state.
+ let accountTree = content_tab_e(tab, "accounttree");
+ Assert.equal(accountRow, accountTree.selectedIndex);
+ Assert.equal(
+ !accountTree.rows[accountRow].classList.contains("collapsed"),
+ wishedState
+ );
+
+ accountTree.rows[accountRow].classList.toggle("collapsed");
+ Assert.equal(
+ accountTree.rows[accountRow].classList.contains("collapsed"),
+ wishedState
+ );
+
+ // Whatever the open state of the account was, selecting one of its subpanes
+ // must open it.
+ tab.browser.contentWindow.selectServer(
+ gPopAccount.incomingServer,
+ "am-junk.xhtml"
+ );
+ Assert.ok(!accountTree.rows[accountRow].classList.contains("collapsed"));
+
+ // Set the proper state again for continuation of the test.
+ if (wishedState) {
+ accountTree.collapseRowAtIndex(accountRow);
+ } else {
+ accountTree.expandRowAtIndex(accountRow);
+ }
+ Assert.equal(
+ accountTree.rows[accountRow].classList.contains("collapsed"),
+ wishedState
+ );
+}
+
+/**
+ * Bug 740617.
+ * Check if the default account is styled in bold.
+ */
+add_task(async function test_default_account_highlight() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_default_account_highlight(tab);
+ });
+});
+
+/**
+ * Check if the default account is styled in bold and another account is not.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_default_account_highlight(tab) {
+ // Select the default account.
+ let accountRow = get_account_tree_row(
+ MailServices.accounts.defaultAccount.key,
+ null,
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ let accountTree = content_tab_e(tab, "accounttree");
+ Assert.equal(accountRow, accountTree.selectedIndex);
+
+ // We can't read the computed style of the tree cell directly, so at least see
+ // if the isDefaultServer-true property is set on it. Hopefully the proper style
+ // is attached to this property.
+ Assert.ok(accountTree.rows[accountRow].classList.contains("isDefaultServer"));
+
+ // Now select another account that is not default.
+ accountRow = get_account_tree_row(gPopAccount.key, null, tab);
+ click_account_tree_row(tab, accountRow);
+
+ // There should isDefaultServer-true on its tree cell.
+ Assert.ok(
+ !accountTree.rows[accountRow].classList.contains("isDefaultServer")
+ );
+}
+/**
+ * Bug 58713.
+ * Check if after deleting an account the next one is selected.
+ *
+ * This test should always be the last one as it removes our specially
+ * created gPopAccount.
+ */
+add_task(async function test_selection_after_account_deletion() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_selection_after_account_deletion(tab);
+ });
+});
+
+/**
+ * Check if after deleting an account the next one is selected.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_selection_after_account_deletion(tab) {
+ let accountList = [];
+ let accountTree = content_tab_e(tab, "accounttree");
+ // Build the list of accounts in the account tree (order is important).
+ for (let row of accountTree.children) {
+ if ("_account" in row) {
+ let curAccount = row._account;
+ if (!accountList.includes(curAccount)) {
+ accountList.push(curAccount);
+ }
+ }
+ }
+
+ // Get position of the current account in the account list.
+ let accountIndex = accountList.indexOf(gPopAccount);
+
+ // Remove our account.
+ remove_account(gPopAccount, tab);
+ gPopAccount = null;
+ // Now there should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, gOriginalAccountCount);
+
+ // See if the currently selected account is the one next in the account list.
+ let accountRow = accountTree.selectedIndex;
+ Assert.equal(
+ accountTree.rows[accountRow]._account,
+ accountList[accountIndex + 1]
+ );
+}
diff --git a/comm/mail/test/browser/account/browser_values.js b/comm/mail/test/browser/account/browser_values.js
new file mode 100644
index 0000000000..8d44a65f5a
--- /dev/null
+++ b/comm/mail/test/browser/account/browser_values.js
@@ -0,0 +1,401 @@
+/* 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/. */
+
+/**
+ * This test checks proper operation of the account settings panes
+ * when certain special or invalid values are entered into the fields.
+ */
+
+"use strict";
+
+var { click_account_tree_row, get_account_tree_row, open_advanced_settings } =
+ ChromeUtils.import(
+ "resource://testing-common/mozmill/AccountManagerHelpers.jsm"
+ );
+var { input_value } = ChromeUtils.import(
+ "resource://testing-common/mozmill/KeyboardHelpers.jsm"
+);
+var { gMockPromptService } = ChromeUtils.import(
+ "resource://testing-common/mozmill/PromptHelpers.jsm"
+);
+var { plan_for_modal_dialog, wait_for_modal_dialog } = ChromeUtils.import(
+ "resource://testing-common/mozmill/WindowHelpers.jsm"
+);
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+var { mc } = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+
+var gPopAccount, gOriginalAccountCount;
+
+add_setup(function () {
+ // There may be pre-existing accounts from other tests.
+ gOriginalAccountCount = MailServices.accounts.allServers.length;
+
+ // Create a POP server
+ let popServer = MailServices.accounts
+ .createIncomingServer("nobody", "example.invalid", "pop3")
+ .QueryInterface(Ci.nsIPop3IncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox@example.invalid";
+
+ gPopAccount = MailServices.accounts.createAccount();
+ gPopAccount.incomingServer = popServer;
+ gPopAccount.addIdentity(identity);
+
+ // Now there should be one more account.
+ Assert.equal(
+ MailServices.accounts.allServers.length,
+ gOriginalAccountCount + 1
+ );
+});
+
+registerCleanupFunction(function () {
+ // Remove our test account to leave the profile clean.
+ MailServices.accounts.removeAccount(gPopAccount);
+ // There should be only the original accounts left.
+ Assert.equal(MailServices.accounts.allServers.length, gOriginalAccountCount);
+});
+
+/**
+ * Bug 208628.
+ * Check that if the CC field is empty, enabling CC will automatically
+ * prefill the currently default email address.
+ */
+add_task(async function test_default_CC_address() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_default_CC_address(tab);
+ });
+});
+
+/**
+ * Check that if the CC field is empty, enabling CC will automatically
+ * prefill the currently default email address.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_default_CC_address(tab) {
+ let accountRow = get_account_tree_row(
+ gPopAccount.key,
+ "am-copies.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+
+ let defaultAddress =
+ iframe.contentDocument.getElementById("identity.email").value;
+ let ccCheck = iframe.contentDocument.getElementById("identity.doCc");
+ let ccAddress = iframe.contentDocument.getElementById("identity.doCcList");
+ // The CC checkbox is not enabled and the address value is empty.
+ Assert.ok(!ccCheck.checked);
+ Assert.equal(ccAddress.value, "");
+ // After ticking the CC checkbox the default address should be prefilled.
+ EventUtils.synthesizeMouseAtCenter(ccCheck, {}, ccCheck.ownerGlobal);
+ Assert.equal(ccAddress.value, defaultAddress);
+
+ let bccCheck = iframe.contentDocument.getElementById("identity.doBcc");
+ let bccAddress = iframe.contentDocument.getElementById("identity.doBccList");
+ // The BCC checkbox is not enabled but we set the address value to something.
+ Assert.ok(!bccCheck.checked);
+ Assert.equal(bccAddress.value, "");
+ let bccUserAddress = "somebody@else.invalid";
+ bccAddress.value = bccUserAddress;
+ // After ticking the BCC checkbox the current value of the address should not change.
+ EventUtils.synthesizeMouseAtCenter(bccCheck, {}, bccCheck.ownerGlobal);
+ Assert.equal(bccAddress.value, bccUserAddress);
+}
+
+/**
+ * Bug 720199.
+ * Check if the account name automatically changes when the user changes
+ * the username or hostname.
+ */
+add_task(async function test_account_name() {
+ // We already have a POP account ready.
+ // Create also a NNTP server.
+ let nntpServer = MailServices.accounts
+ .createIncomingServer(null, "example.nntp.invalid", "nntp")
+ .QueryInterface(Ci.nsINntpIncomingServer);
+
+ let identity = MailServices.accounts.createIdentity();
+ identity.email = "tinderbox2@example.invalid";
+
+ let nntpAccount = MailServices.accounts.createAccount();
+ nntpAccount.incomingServer = nntpServer;
+ nntpAccount.addIdentity(identity);
+
+ Assert.equal(
+ gPopAccount.incomingServer.prettyName,
+ "nobody on example.invalid"
+ );
+ Assert.equal(nntpAccount.incomingServer.prettyName, "example.nntp.invalid");
+
+ // The automatic account name update works only if the name is
+ // in the form of user@host.
+ gPopAccount.incomingServer.prettyName = "nobody@example.invalid";
+
+ let newHost = "some.host.invalid";
+ let newUser = "somebody";
+
+ // On NNTP there is no user name so just set new hostname.
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_name(nntpAccount, newHost, null, tab);
+ });
+
+ // And see if the account name is updated to it.
+ Assert.equal(nntpAccount.incomingServer.prettyName, newHost);
+
+ // On POP3 there is both user name and host name.
+ // Set new host name first.
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_name(gPopAccount, newHost, null, tab);
+ });
+
+ // And see if in the account name the host part is updated to it.
+ Assert.equal(gPopAccount.incomingServer.prettyName, "nobody@" + newHost);
+
+ // Set new host name first.
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_name(gPopAccount, null, newUser, tab);
+ });
+
+ // And see if in the account name the user part is updated.
+ Assert.equal(gPopAccount.incomingServer.prettyName, newUser + "@" + newHost);
+
+ newHost = "another.host.invalid";
+ newUser = "anotherbody";
+
+ // Set user name and host name at once.
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_name(gPopAccount, newHost, newUser, tab);
+ });
+
+ // And see if in the account name the host part is updated to it.
+ Assert.equal(gPopAccount.incomingServer.prettyName, newUser + "@" + newHost);
+
+ // Now have an account name where the name does not match the hostname.
+ gPopAccount.incomingServer.prettyName = newUser + "@example.invalid";
+
+ newHost = "third.host.invalid";
+ // Set the host name again.
+ await open_advanced_settings(function (tab) {
+ subtest_check_account_name(gPopAccount, newHost, null, tab);
+ });
+
+ // And the account name should not be touched.
+ Assert.equal(
+ gPopAccount.incomingServer.prettyName,
+ newUser + "@example.invalid"
+ );
+
+ MailServices.accounts.removeAccount(nntpAccount);
+}).skip(); // Restart is required to apply change to server name or username.
+
+/**
+ * Changes the user name and hostname to the supplied values.
+ *
+ * @param {object} account - The account to change
+ * @param {string} newHostname - The hostname value to set
+ * @param {string} newUsername - The username value to set
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_account_name(account, newHostname, newUsername, tab) {
+ let accountRow = get_account_tree_row(account.key, "am-server.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+
+ if (newHostname) {
+ let hostname = iframe.contentDocument.getElementById("server.hostName");
+ Assert.equal(hostname.value, account.incomingServer.hostName);
+
+ // Now change the server host name.
+ hostname.value = newHostname;
+ }
+
+ if (newUsername) {
+ let username = iframe.contentDocument.getElementById("server.username");
+ Assert.equal(username.value, account.incomingServer.username);
+
+ // Now change the server user name.
+ username.value = newUsername;
+ }
+
+ if (newUsername) {
+ gMockPromptService.register();
+ }
+
+ tab.browser.contentWindow.onAccept(true);
+ if (newUsername) {
+ Assert.equal("alert", gMockPromptService.promptState.method);
+ gMockPromptService.unregister();
+ }
+}
+
+/**
+ * Bug 536768.
+ * Check if invalid junk target settings (folders) are fixed to sane values.
+ */
+add_task(async function test_invalid_junk_target() {
+ // Set the junk target prefs to invalid values.
+ let branch = Services.prefs.getBranch(
+ "mail.server." + gPopAccount.incomingServer.key + "."
+ );
+ branch.setCharPref("spamActionTargetAccount", "some random non-existent URI");
+ branch.setStringPref(
+ "spamActionTargetFolder",
+ "some random non-existent URI"
+ );
+ let moveOnSpam = true;
+ branch.setBoolPref("moveOnSpam", moveOnSpam);
+ await open_advanced_settings(function (tab) {
+ subtest_check_invalid_junk_target(tab);
+ });
+
+ // The pref has no default so its non-existence means it was cleared.
+ moveOnSpam = branch.getBoolPref("moveOnSpam", false);
+ Assert.ok(!moveOnSpam);
+ // The targets should point to the same pop account now.
+ let targetAccount = branch.getCharPref("spamActionTargetAccount");
+ Assert.equal(targetAccount, gPopAccount.incomingServer.serverURI);
+ let targetFolder = branch.getStringPref("spamActionTargetFolder");
+ Assert.equal(targetFolder, gPopAccount.incomingServer.serverURI + "/Junk");
+});
+
+/**
+ * Just show the Junk settings pane and let it fix the values.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_invalid_junk_target(tab) {
+ let accountRow = get_account_tree_row(gPopAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+ tab.browser.contentWindow.onAccept(true);
+}
+
+/**
+ * Bug 327812.
+ * Checks if invalid server hostnames are not accepted.
+ */
+add_task(async function test_invalid_hostname() {
+ let branch = Services.prefs.getBranch(
+ "mail.server." + gPopAccount.incomingServer.key + "."
+ );
+ let origHostname = branch.getCharPref("hostname");
+
+ await open_advanced_settings(function (tab) {
+ subtest_check_invalid_hostname(tab, false, origHostname);
+ });
+ await open_advanced_settings(function (tab) {
+ subtest_check_invalid_hostname(tab, true, origHostname);
+ });
+
+ // The new bad hostname should not have been saved.
+ let newHostname = branch.getCharPref("hostname");
+ Assert.equal(origHostname, newHostname);
+});
+
+/**
+ * Set the hostname to an invalid value and check if it gets fixed.
+ *
+ * @param {object} tab - The account manager tab.
+ * @param {boolean} exitSettings - Attempt to close the Account settings dialog.
+ * @param {string} originalHostname - Original hostname of this server.
+ */
+function subtest_check_invalid_hostname(tab, exitSettings, originalHostname) {
+ let accountRow = get_account_tree_row(
+ gPopAccount.key,
+ "am-server.xhtml",
+ tab
+ );
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+ let hostname = iframe.contentDocument.getElementById("server.hostName");
+ Assert.equal(hostname.value, originalHostname);
+
+ hostname.value = "some_invalid+host&domain*in>invalid";
+
+ if (!exitSettings) {
+ accountRow = get_account_tree_row(gPopAccount.key, "am-junk.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ // The invalid hostname should be set back to previous value at this point...
+ accountRow = get_account_tree_row(gPopAccount.key, "am-server.xhtml", tab);
+ click_account_tree_row(tab, accountRow);
+
+ // ...let's check that:
+ iframe = tab.browser.contentWindow.document.getElementById("contentFrame");
+ hostname = iframe.contentDocument.getElementById("server.hostName");
+ Assert.equal(hostname.value, originalHostname);
+ } else {
+ // If the hostname is bad, we should get a warning dialog.
+ plan_for_modal_dialog("commonDialogWindow", function (cdc) {
+ // Just dismiss it.
+ cdc.window.document.documentElement
+ .querySelector("dialog")
+ .acceptDialog();
+ });
+ tab.browser.contentWindow.onAccept(true);
+ wait_for_modal_dialog("commonDialogWindow");
+ }
+}
+
+/**
+ * Bug 1426328.
+ * Check that the AM will trim user added spaces around text values.
+ */
+const badName = "trailing space ";
+const badEmail = " leading_space@example.com";
+
+add_task(async function test_trailing_spaces() {
+ await open_advanced_settings(function (tab) {
+ subtest_check_trailing_spaces(tab);
+ });
+ Assert.equal(gPopAccount.incomingServer.prettyName, badName.trim());
+ Assert.equal(gPopAccount.defaultIdentity.email, badEmail.trim());
+});
+
+/**
+ * Check that the AM will trim user added spaces around text values
+ * when storing them into the account.
+ *
+ * @param {object} tab - The account manager tab.
+ */
+function subtest_check_trailing_spaces(tab) {
+ let accountRow = get_account_tree_row(gPopAccount.key, null, tab);
+ click_account_tree_row(tab, accountRow);
+
+ let iframe =
+ tab.browser.contentWindow.document.getElementById("contentFrame");
+
+ let accountName = iframe.contentDocument.getElementById("server.prettyName");
+ let defaultAddress = iframe.contentDocument.getElementById("identity.email");
+ accountName.value = "";
+ defaultAddress.value = "";
+ input_value(mc, badName, accountName);
+ input_value(mc, badEmail, defaultAddress);
+
+ Assert.equal(
+ accountName.value,
+ badName,
+ "accountName should have the correct value typed in"
+ );
+ // type="email" inputs are now automatically trimmed
+ Assert.equal(
+ defaultAddress.value,
+ badEmail.trim(),
+ "defaultAddress should have the correct value typed in"
+ );
+}
diff --git a/comm/mail/test/browser/account/head.js b/comm/mail/test/browser/account/head.js
new file mode 100644
index 0000000000..8bae9d629a
--- /dev/null
+++ b/comm/mail/test/browser/account/head.js
@@ -0,0 +1,78 @@
+/* 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/. */
+
+// From browser/components/preferences/tests/head.js
+
+function is_element_visible(aElement, aMsg) {
+ isnot(aElement, null, "Element should not be null, when checking visibility");
+ ok(!BrowserTestUtils.is_hidden(aElement), aMsg);
+}
+
+function openAndLoadSubDialog(
+ aURL,
+ aFeatures = null,
+ aParams = null,
+ aClosingCallback = null
+) {
+ let promise = promiseLoadSubDialog(aURL);
+ content.gSubDialog.open(
+ aURL,
+ { features: aFeatures, closingCallback: aClosingCallback },
+ aParams
+ );
+ return promise;
+}
+
+function promiseLoadSubDialog(aURL) {
+ if (Services.env.get("MOZ_HEADLESS")) {
+ throw new Error("promiseLoadSubDialog doesn't work in headless mode!");
+ }
+
+ return new Promise((resolve, reject) => {
+ content.gSubDialog._dialogStack.addEventListener(
+ "dialogopen",
+ function dialogopen(aEvent) {
+ if (
+ aEvent.detail.dialog._frame.contentWindow.location == "about:blank"
+ ) {
+ return;
+ }
+ content.gSubDialog._dialogStack.removeEventListener(
+ "dialogopen",
+ dialogopen
+ );
+
+ is(
+ aEvent.detail.dialog._frame.contentWindow.location.toString(),
+ aURL,
+ "Check the proper URL is loaded"
+ );
+
+ // Check visibility
+ is_element_visible(aEvent.detail.dialog._overlay, "Overlay is visible");
+
+ // Check that stylesheets were injected
+ let expectedStyleSheetURLs =
+ aEvent.detail.dialog._injectedStyleSheets.slice(0);
+ for (let styleSheet of aEvent.detail.dialog._frame.contentDocument
+ .styleSheets) {
+ let i = expectedStyleSheetURLs.indexOf(styleSheet.href);
+ if (i >= 0) {
+ info("found " + styleSheet.href);
+ expectedStyleSheetURLs.splice(i, 1);
+ }
+ }
+ is(
+ expectedStyleSheetURLs.length,
+ 0,
+ "All expectedStyleSheetURLs should have been found"
+ );
+
+ // Wait for the next event tick to make sure the remaining part of the
+ // testcase runs after the dialog gets ready for input.
+ executeSoon(() => resolve(aEvent.detail.dialog._frame.contentWindow));
+ }
+ );
+ });
+}
diff --git a/comm/mail/test/browser/account/xml/example-imap.com b/comm/mail/test/browser/account/xml/example-imap.com
new file mode 100644
index 0000000000..783eabcb94
--- /dev/null
+++ b/comm/mail/test/browser/account/xml/example-imap.com
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<clientConfig>
+ <emailProvider id="example-imap.com">
+ <domain>example-imap.com</domain>
+ <displayName>Example Två</displayName>
+ <incomingServer type="imap">
+ <hostname>localhost</hostname>
+ <port>1993</port>
+ <socketType>plain</socketType>
+ <username>john.doe@example-imap.com</username>
+ <password>abc12345</password>
+ <authentication>plain</authentication>
+ </incomingServer>
+ <outgoingServer type="smtp">
+ <hostname>localhost</hostname>
+ <port>1587</port>
+ <socketType>plain</socketType>
+ <username>john.doe@example-imap.com</username>
+ <password>abc12345</password>
+ <authentication>plain</authentication>
+ <addThisServer>true</addThisServer>
+ <useGlobalPreferredServer>false</useGlobalPreferredServer>
+ </outgoingServer>
+ </emailProvider>
+</clientConfig>
diff --git a/comm/mail/test/browser/account/xml/example-imap.com^headers^ b/comm/mail/test/browser/account/xml/example-imap.com^headers^
new file mode 100644
index 0000000000..f203c6368e
--- /dev/null
+++ b/comm/mail/test/browser/account/xml/example-imap.com^headers^
@@ -0,0 +1 @@
+Content-Type: text/xml
diff --git a/comm/mail/test/browser/account/xml/example.com b/comm/mail/test/browser/account/xml/example.com
new file mode 100644
index 0000000000..b07c00fda0
--- /dev/null
+++ b/comm/mail/test/browser/account/xml/example.com
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<clientConfig>
+ <emailProvider id="example.com">
+ <domain>example.com</domain>
+ <displayName>Example Två</displayName>
+ <incomingServer type="pop3">
+ <hostname>testin.%EMAILDOMAIN%</hostname>
+ <port>995</port>
+ <socketType>SSL</socketType>
+ <username>%EMAILLOCALPART%</username>
+ <authentication>plain</authentication>
+ </incomingServer>
+ <outgoingServer type="smtp">
+ <hostname>testout.%EMAILDOMAIN%</hostname>
+ <port>587</port>
+ <socketType>STARTTLS</socketType>
+ <username>%EMAILADDRESS%</username>
+ <password>Blä4</password>
+ <authentication>plain</authentication>
+ <addThisServer>true</addThisServer>
+ <useGlobalPreferredServer>false</useGlobalPreferredServer>
+ </outgoingServer>
+ </emailProvider>
+</clientConfig>
diff --git a/comm/mail/test/browser/account/xml/example.com^headers^ b/comm/mail/test/browser/account/xml/example.com^headers^
new file mode 100644
index 0000000000..f203c6368e
--- /dev/null
+++ b/comm/mail/test/browser/account/xml/example.com^headers^
@@ -0,0 +1 @@
+Content-Type: text/xml
diff --git a/comm/mail/test/browser/account/xml/momo.invalid b/comm/mail/test/browser/account/xml/momo.invalid
new file mode 100644
index 0000000000..f0ba552c9a
--- /dev/null
+++ b/comm/mail/test/browser/account/xml/momo.invalid
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<clientConfig version="1.1">
+ <emailProvider id="momo.invalid">
+ <domain>momo.invalid</domain>
+ <displayName>MoMo Mail</displayName>
+ <displayShortName>Yahoo</displayShortName>
+ <incomingServer type="pop3">
+ <hostname>pop.mail.momo.invalid</hostname>
+ <port>995</port>
+ <socketType>SSL</socketType>
+ <username>%EMAILLOCALPART%</username>
+ <authentication>password-cleartext</authentication>
+ </incomingServer>
+ <incomingServer type="imap">
+ <hostname>imap.mail.momo.invalid</hostname>
+ <port>993</port>
+ <socketType>SSL</socketType>
+ <username>%EMAILLOCALPART%</username>
+ <authentication>password-cleartext</authentication>
+ </incomingServer>
+ <outgoingServer type="smtp">
+ <hostname>smtp.mail.momo.invalid</hostname>
+ <port>465</port>
+ <socketType>SSL</socketType>
+ <username>%EMAILLOCALPART%</username>
+ <authentication>password-cleartext</authentication>
+ </outgoingServer>
+ </emailProvider>
+</clientConfig>
diff --git a/comm/mail/test/browser/account/xml/momo.invalid^headers^ b/comm/mail/test/browser/account/xml/momo.invalid^headers^
new file mode 100644
index 0000000000..f203c6368e
--- /dev/null
+++ b/comm/mail/test/browser/account/xml/momo.invalid^headers^
@@ -0,0 +1 @@
+Content-Type: text/xml