diff options
Diffstat (limited to 'comm/mail/test/browser/account')
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 |