/* 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"; const { DNS } = ChromeUtils.importESModule("resource:///modules/DNS.sys.mjs"); const { cal } = ChromeUtils.importESModule( "resource:///modules/calendar/calUtils.sys.mjs" ); const PREF_NAME = "mailnews.auto_config_url"; const PREF_VALUE = Services.prefs.getCharPref(PREF_NAME); const _srv = DNS.srv; const _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.imap.test", "_carddavs._tcp.imap.test"].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 [ { strings: ["path=/browser/comm/calendar/test/browser/data/dns.sjs"] }, ]; } if (name == "_carddavs._tcp.localhost") { return [ { strings: [ "path=/browser/comm/mail/components/addrbook/test/browser/data/dns.sjs", ], }, ]; } if (name == "_caldavs._tcp.imap.test") { return [ { strings: ["path=/browser/comm/calendar/test/browser/data/dns.sjs"] }, ]; } if (name == "_carddavs._tcp.imap.test") { return [ { strings: [ "path=/browser/comm/mail/components/addrbook/test/browser/data/dns.sjs", ], }, ]; } throw new Error(`Unexpected DNS TXT lookup: ${name}`); }; add_setup(function () { // Set the pref to load a local autoconfig file. const url = "http://mochi.test:8888/browser/comm/mail/test/browser/account/xml/"; Services.prefs.setCharPref(PREF_NAME, url); }); registerCleanupFunction(function () { DNS.srv = _srv; DNS.txt = _txt; // Restore the original pref. Services.prefs.setCharPref(PREF_NAME, PREF_VALUE); }); add_task(async function test_cancelling_sync_accounts_load() { IMAPServer.open(); SMTPServer.open(); const emailUser = { name: "John Doe", email: "john.doe@imap.test", password: "abc12345", incomingHost: "testin.imap.test", outgoingHost: "testout.imap.test", }; const dialog = await subtest_open_account_hub_dialog(); await subtest_fill_initial_config_fields(dialog, emailUser); const footer = dialog.querySelector("account-hub-footer"); const footerForward = footer.querySelector("#forward"); const footerBack = footer.querySelector("#back"); const configFoundTemplate = dialog.querySelector("email-config-found"); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible(configFoundTemplate.querySelector("#imap")), "The IMAP config option should be visible." ); // Continue button should lead to password template. EventUtils.synthesizeMouseAtCenter(footerForward, {}); Assert.ok( BrowserTestUtils.isHidden(configFoundTemplate), "The config found template should be hidden." ); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible(dialog.querySelector("email-password-form")), "The email password form should be visible." ); const emailPasswordTemplate = dialog.querySelector("email-password-form"); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible( emailPasswordTemplate.querySelector("#password") ), "The password form input should be visible." ); const passwordInput = emailPasswordTemplate.querySelector("#password"); EventUtils.synthesizeMouseAtCenter(passwordInput, {}); // Entering the correct password should hide current subview. const inputEvent = BrowserTestUtils.waitForEvent( passwordInput, "input", true, event => event.target.value === "abc12345" ); EventUtils.sendString("abc12345", window); await inputEvent; EventUtils.synthesizeMouseAtCenter(footerForward, {}); await TestUtils.waitForCondition( () => BrowserTestUtils.isHidden(emailPasswordTemplate), "The email password subview should be hidden." ); let imapAccount; await TestUtils.waitForCondition( () => (imapAccount = MailServices.accounts.accounts.find( account => account.identities[0]?.email === emailUser.email )), "The imap account should be created." ); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible( dialog.querySelector("email-sync-accounts-form") ), "The sync accounts view should be in view." ); // Cancelling here should prevent showing any sync accounts, whether we've fetched them or not. Assert.ok( BrowserTestUtils.isVisible(footerBack), "The cancel button should be visible." ); EventUtils.synthesizeMouseAtCenter(footerBack, {}); const syncAccountsTemplate = dialog.querySelector("email-sync-accounts-form"); const selectAllAddressBooks = syncAccountsTemplate.querySelector( "#selectAllAddressBooks" ); const selectAllCalendars = syncAccountsTemplate.querySelector( "#selectAllCalendars" ); // Cancelling should show an error notificaiton. const header = syncAccountsTemplate.shadowRoot.querySelector("account-hub-header"); await TestUtils.waitForCondition( () => header.shadowRoot .querySelector("#emailFormNotification") .classList.contains("error"), "The error notification should be present." ); // The toggle buttons should be hidden as we haven't set the state of the sync view. Assert.ok( BrowserTestUtils.isHidden(selectAllAddressBooks), "The address book select all button should be hidden." ); Assert.ok( BrowserTestUtils.isHidden(selectAllCalendars), "The calendar select all button should be hidden." ); await subtest_clear_status_bar(); MailServices.accounts.removeAccount(imapAccount); Services.logins.removeAllLogins(); IMAPServer.close(); SMTPServer.close(); await subtest_close_account_hub_dialog(dialog, syncAccountsTemplate); }); add_task(async function test_account_load_sync_accounts_imap_account() { IMAPServer.open(); SMTPServer.open(); const emailUser = { name: "John Doe", email: "john.doe@imap.test", password: "abc12345", incomingHost: "testin.imap.test", outgoingHost: "testout.imap.test", }; const dialog = await subtest_open_account_hub_dialog(); await subtest_fill_initial_config_fields(dialog, emailUser); const footer = dialog.querySelector("account-hub-footer"); const footerForward = footer.querySelector("#forward"); const configFoundTemplate = dialog.querySelector("email-config-found"); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible(configFoundTemplate.querySelector("#imap")), "The IMAP config option should be visible" ); // Continue button should lead to password template. EventUtils.synthesizeMouseAtCenter(footerForward, {}); Assert.ok( BrowserTestUtils.isHidden(configFoundTemplate), "The config found template should be hidden." ); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible(dialog.querySelector("email-password-form")), "The email password form should be visible." ); const emailPasswordTemplate = dialog.querySelector("email-password-form"); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible( emailPasswordTemplate.querySelector("#password") ), "The password form input should be visible." ); const passwordInput = emailPasswordTemplate.querySelector("#password"); EventUtils.synthesizeMouseAtCenter(passwordInput, {}); // Entering the correct password should hide current subview. const inputEvent = BrowserTestUtils.waitForEvent( passwordInput, "input", true, event => event.target.value === "abc12345" ); EventUtils.sendString("abc12345", window); await inputEvent; EventUtils.synthesizeMouseAtCenter(footerForward, {}); await TestUtils.waitForCondition( () => BrowserTestUtils.isHidden(emailPasswordTemplate), "The email password subview should be hidden." ); let imapAccount; await TestUtils.waitForCondition( () => (imapAccount = MailServices.accounts.accounts.find( account => account.identities[0]?.email === emailUser.email )), "The imap account should be created." ); await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible( dialog.querySelector("email-sync-accounts-form") ), "The sync accounts view should be in view." ); const syncAccountsTemplate = dialog.querySelector("email-sync-accounts-form"); // Wait for the select all buttons to be in view to show the sync accounts. const selectAllAddressBooksButtonPromise = await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible( syncAccountsTemplate.querySelector("#selectAllAddressBooks") ), "The select all address books button should be visible." ); const selectAllCalendarsButtonPromise = await TestUtils.waitForCondition( () => BrowserTestUtils.isVisible( syncAccountsTemplate.querySelector("#selectAllCalendars") ), "The select all calendars button should be visible." ); await selectAllAddressBooksButtonPromise; await selectAllCalendarsButtonPromise; const selectAllAddressBooks = syncAccountsTemplate.querySelector( "#selectAllAddressBooks" ); const selectAllCalendars = syncAccountsTemplate.querySelector( "#selectAllCalendars" ); const selectedAddressBooks = syncAccountsTemplate.querySelector( "#selectedAddressBooks" ); const selectedCalendars = syncAccountsTemplate.querySelector("#selectedCalendars"); Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectAllAddressBooks).id, "account-hub-deselect-all", "Address book select toggle should be deselect all." ); Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectedAddressBooks).args.count, 1, "Address books count should be 1." ); Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectAllCalendars).id, "account-hub-deselect-all", "Calendars select toggle should be deselect all." ); Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectedCalendars).args.count, 2, "Calendars count should be 2." ); const addressBooks = syncAccountsTemplate.querySelectorAll( "#addressBooks label" ); Assert.equal(addressBooks.length, 1, "There should be one address book."); Assert.equal( syncAccountsTemplate.querySelectorAll("#addressBooks input:checked").length, 1, "There should 1 checked address book." ); Assert.equal( addressBooks[0].textContent, "You found me!", "The address book found should have the name - You found me!" ); const calendars = syncAccountsTemplate.querySelectorAll("#calendars label"); Assert.equal(calendars.length, 2, "There should be two calendars."); Assert.equal( syncAccountsTemplate.querySelectorAll("#calendars input:checked").length, 2, "There should 2 checked calendars." ); Assert.equal( calendars[0].textContent, "You found me!", "The first calendar found should have the name - You found me!" ); Assert.equal( calendars[1].textContent, "Röda dagar", "The second calendar found should have the name - Röda dagar" ); // Unchecking an input should update the count label, and the select toggle // fluent id. let checkEvent = BrowserTestUtils.waitForEvent( addressBooks[0].querySelector("input"), "change", true, event => !event.target.checked ); EventUtils.synthesizeMouseAtCenter( addressBooks[0].querySelector("input"), {} ); await checkEvent; Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectAllAddressBooks).id, "account-hub-select-all", "Address book select toggle should be select all." ); Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectedAddressBooks).args.count, 0, "Address books count should be 0." ); // Selecting deselect all calendars should update all inputs to be unchecked // and set the count to 0. const selectToggleEvent = BrowserTestUtils.waitForEvent( selectAllCalendars, "click" ); EventUtils.synthesizeMouseAtCenter(selectAllCalendars, {}); await selectToggleEvent; Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectAllCalendars).id, "account-hub-select-all", "Calendar select toggle should be select all." ); Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectedCalendars).args.count, 0, "Calendars count should be 0." ); // Select the first calendar and address book and click continue to add // the selected calendar and address book. checkEvent = BrowserTestUtils.waitForEvent( addressBooks[0].querySelector("input"), "change", true, event => event.target.checked ); EventUtils.synthesizeMouseAtCenter( addressBooks[0].querySelector("input"), {} ); await checkEvent; checkEvent = BrowserTestUtils.waitForEvent( calendars[0].querySelector("input"), "change", true, event => event.target.checked ); EventUtils.synthesizeMouseAtCenter(calendars[0].querySelector("input"), {}); await checkEvent; Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectedAddressBooks).args.count, 1, "Address books count should be 1." ); Assert.equal( syncAccountsTemplate.l10n.getAttributes(selectedCalendars).args.count, 1, "Calendars count should be 1." ); const calendarPromise = new Promise(resolve => { const observer = { onCalendarRegistered(calendar) { cal.manager.removeObserver(this); resolve(calendar); }, onCalendarUnregistering() {}, onCalendarDeleting() {}, }; cal.manager.addObserver(observer); }); const addressBookDirectoryPromise = TestUtils.topicObserved( "addrbook-directory-synced" ); EventUtils.synthesizeMouseAtCenter(footerForward, {}); // Check existence of address book and calendar. const [addressBookDirectory] = await addressBookDirectoryPromise; Assert.equal(addressBookDirectory.dirName, "You found me!"); Assert.equal( addressBookDirectory.dirType, Ci.nsIAbManager.CARDDAV_DIRECTORY_TYPE ); Assert.equal( addressBookDirectory.getStringValue("carddav.url", ""), "https://example.org/browser/comm/mail/components/addrbook/test/browser/data/addressbook.sjs" ); const calendar = await calendarPromise; Assert.equal(calendar.name, "You found me!"); Assert.equal(calendar.type, "caldav"); // Remove the address book and calendar. MailServices.ab.deleteAddressBook(addressBookDirectory.URI); cal.manager.removeCalendar(calendar); await subtest_clear_status_bar(); MailServices.accounts.removeAccount(imapAccount); Services.logins.removeAllLogins(); IMAPServer.close(); SMTPServer.close(); await subtest_close_account_hub_dialog( dialog, dialog.querySelector("email-added-success") ); });