diff options
Diffstat (limited to 'comm/mail/test/browser/account/browser_mailAccountSetupWizard.js')
-rw-r--r-- | comm/mail/test/browser/account/browser_mailAccountSetupWizard.js | 931 |
1 files changed, 931 insertions, 0 deletions
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; +}); |