diff options
Diffstat (limited to 'comm/mail/components/preferences/test/browser/browser_sync.js')
-rw-r--r-- | comm/mail/components/preferences/test/browser/browser_sync.js | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/comm/mail/components/preferences/test/browser/browser_sync.js b/comm/mail/components/preferences/test/browser/browser_sync.js new file mode 100644 index 0000000000..108695ebb5 --- /dev/null +++ b/comm/mail/components/preferences/test/browser/browser_sync.js @@ -0,0 +1,419 @@ +/* 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/. */ + +const { MockRegistrar } = ChromeUtils.importESModule( + "resource://testing-common/MockRegistrar.sys.mjs" +); + +const { FxAccounts } = ChromeUtils.importESModule( + "resource://gre/modules/FxAccounts.sys.mjs" +); +FxAccounts.config.promiseConnectAccountURI = entryPoint => + `https://example.org/?page=connect&entryPoint=${entryPoint}`; +FxAccounts.config.promiseManageURI = entryPoint => + `https://example.org/?page=manage&entryPoint=${entryPoint}`; +FxAccounts.config.promiseChangeAvatarURI = entryPoint => + `https://example.org/?page=avatar&entryPoint=${entryPoint}`; + +const ALL_ENGINES = [ + "accounts", + "identities", + "addressbooks", + "calendars", + "passwords", +]; +const PREF_PREFIX = "services.sync.engine"; + +let prefsWindow, prefsDocument, tabmail; + +add_setup(async function () { + for (let engine of ALL_ENGINES) { + Services.prefs.setBoolPref(`${PREF_PREFIX}.${engine}`, true); + } + + ({ prefsWindow, prefsDocument } = await openNewPrefsTab("paneSync")); + tabmail = document.getElementById("tabmail"); + + /** @implements {nsIExternalProtocolService} */ + let mockExternalProtocolService = { + QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]), + externalProtocolHandlerExists(protocolScheme) {}, + isExposedProtocol(protocolScheme) {}, + loadURI(uri, windowContext) { + Assert.report( + true, + undefined, + undefined, + `should not be opening ${uri.spec} in an external browser` + ); + }, + }; + + let mockExternalProtocolServiceCID = MockRegistrar.register( + "@mozilla.org/uriloader/external-protocol-service;1", + mockExternalProtocolService + ); + registerCleanupFunction(() => { + MockRegistrar.unregister(mockExternalProtocolServiceCID); + }); +}); + +add_task(async function testSectionStates() { + let noFxaAccount = prefsDocument.getElementById("noFxaAccount"); + let hasFxaAccount = prefsDocument.getElementById("hasFxaAccount"); + let accountStates = [noFxaAccount, hasFxaAccount]; + + let fxaLoginUnverified = prefsDocument.getElementById("fxaLoginUnverified"); + let fxaLoginRejected = prefsDocument.getElementById("fxaLoginRejected"); + let fxaLoginVerified = prefsDocument.getElementById("fxaLoginVerified"); + let loginStates = [fxaLoginUnverified, fxaLoginRejected, fxaLoginVerified]; + + let fxaDeviceInfo = prefsDocument.getElementById("fxaDeviceInfo"); + let syncConnected = prefsDocument.getElementById("syncConnected"); + let syncDisconnected = prefsDocument.getElementById("syncDisconnected"); + let syncStates = [syncConnected, syncDisconnected]; + + function assertStateVisible(states, visibleState) { + for (let state of states) { + let visible = BrowserTestUtils.is_visible(state); + Assert.equal( + visible, + state == visibleState, + `${state.id} should be ${state == visibleState ? "visible" : "hidden"}` + ); + } + } + + function checkStates({ + accountState, + loginState = null, + deviceInfoVisible = false, + syncState = null, + }) { + prefsWindow.gSyncPane.updateWeavePrefs(); + assertStateVisible(accountStates, accountState); + assertStateVisible(loginStates, loginState); + Assert.equal( + BrowserTestUtils.is_visible(fxaDeviceInfo), + deviceInfoVisible, + `fxaDeviceInfo should be ${deviceInfoVisible ? "visible" : "hidden"}` + ); + assertStateVisible(syncStates, syncState); + } + + async function assertTabOpens(target, expectedURL) { + if (typeof target == "string") { + target = prefsDocument.getElementById(target); + } + + let tabPromise = BrowserTestUtils.waitForEvent(window, "TabOpen"); + EventUtils.synthesizeMouseAtCenter(target, {}, prefsWindow); + await tabPromise; + let tab = tabmail.currentTabInfo; + await BrowserTestUtils.browserLoaded(tab.browser); + Assert.equal( + tab.browser.currentURI.spec, + `https://example.org/${expectedURL}`, + "a tab opened to the correct URL" + ); + tabmail.closeTab(tab); + } + + info("No account"); + Assert.equal(prefsWindow.UIState.get().status, "not_configured"); + checkStates({ accountState: noFxaAccount }); + + // Check clicking the Sign In button opens the connect page in a tab. + await assertTabOpens("noFxaSignIn", "?page=connect&entryPoint="); + + // Override the window's UIState object with mock values. + let baseState = { + email: "test@invalid", + displayName: "Testy McTest", + avatarURL: + "https://example.org/browser/comm/mail/components/preferences/test/browser/files/avatar.png", + avatarIsDefault: false, + }; + let mockState; + prefsWindow.UIState = { + ON_UPDATE: "sync-ui-state:update", + STATUS_LOGIN_FAILED: "login_failed", + STATUS_NOT_CONFIGURED: "not_configured", + STATUS_NOT_VERIFIED: "not_verified", + STATUS_SIGNED_IN: "signed_in", + get() { + return mockState; + }, + }; + + info("Login not verified"); + mockState = { ...baseState, status: "not_verified" }; + checkStates({ + accountState: hasFxaAccount, + loginState: fxaLoginUnverified, + deviceInfoVisible: true, + }); + Assert.deepEqual( + await prefsDocument.l10n.getAttributes( + prefsDocument.getElementById("fxaAccountMailNotVerified") + ), + { + id: "sync-pane-email-not-verified", + args: { userEmail: "test@invalid" }, + }, + "email address set correctly" + ); + + // Untested: Resend and remove account buttons. + + info("Login rejected"); + mockState = { ...baseState, status: "login_failed" }; + checkStates({ + accountState: hasFxaAccount, + loginState: fxaLoginRejected, + deviceInfoVisible: true, + }); + Assert.deepEqual( + await prefsDocument.l10n.getAttributes( + prefsDocument.getElementById("fxaAccountLoginRejected") + ), + { + id: "sync-signedin-login-failure", + args: { userEmail: "test@invalid" }, + }, + "email address set correctly" + ); + + // Untested: Sign in and remove account buttons. + + info("Logged in, sync disabled"); + mockState = { ...baseState, status: "verified", syncEnabled: false }; + checkStates({ + accountState: hasFxaAccount, + loginState: fxaLoginVerified, + deviceInfoVisible: true, + syncState: syncDisconnected, + }); + let photo = fxaLoginVerified.querySelector(".contact-photo"); + Assert.equal( + photo.src, + "https://example.org/browser/comm/mail/components/preferences/test/browser/files/avatar.png", + "avatar image set correctly" + ); + + // Check clicking the avatar image opens the avatar page in a tab. + await assertTabOpens(photo, "?page=avatar&entryPoint=preferences"); + + Assert.equal( + prefsDocument.getElementById("fxaDisplayName").textContent, + "Testy McTest", + "display name set correctly" + ); + Assert.equal( + prefsDocument.getElementById("fxaEmailAddress").textContent, + "test@invalid", + "email address set correctly" + ); + + // Check clicking the management link opens the management page in a tab. + await assertTabOpens("verifiedManage", "?page=manage&entryPoint=preferences"); + + // Untested: Sign out button. + + info("Device name section"); + let deviceNameInput = prefsDocument.getElementById("fxaDeviceNameInput"); + let deviceNameCancel = prefsDocument.getElementById("fxaDeviceNameCancel"); + let deviceNameSave = prefsDocument.getElementById("fxaDeviceNameSave"); + let deviceNameChange = prefsDocument.getElementById( + "fxaDeviceNameChangeDeviceName" + ); + Assert.ok(deviceNameInput.readOnly, "input is read-only"); + Assert.ok( + BrowserTestUtils.is_hidden(deviceNameCancel), + "cancel button is hidden" + ); + Assert.ok( + BrowserTestUtils.is_hidden(deviceNameSave), + "save button is hidden" + ); + Assert.ok( + BrowserTestUtils.is_visible(deviceNameChange), + "change button is visible" + ); + + EventUtils.synthesizeMouseAtCenter(deviceNameChange, {}, prefsWindow); + Assert.ok(!deviceNameInput.readOnly, "input is writeable"); + Assert.equal(prefsDocument.activeElement, deviceNameInput, "input is active"); + Assert.ok( + BrowserTestUtils.is_visible(deviceNameCancel), + "cancel button is visible" + ); + Assert.ok( + BrowserTestUtils.is_visible(deviceNameSave), + "save button is visible" + ); + Assert.ok( + BrowserTestUtils.is_hidden(deviceNameChange), + "change button is hidden" + ); + + EventUtils.synthesizeMouseAtCenter(deviceNameCancel, {}, prefsWindow); + Assert.ok(deviceNameInput.readOnly, "input is read-only"); + Assert.ok( + BrowserTestUtils.is_hidden(deviceNameCancel), + "cancel button is hidden" + ); + Assert.ok( + BrowserTestUtils.is_hidden(deviceNameSave), + "save button is hidden" + ); + Assert.ok( + BrowserTestUtils.is_visible(deviceNameChange), + "change button is visible" + ); + + // Check the turn on sync button works. + await openEngineDialog({ expectEngines: ALL_ENGINES, button: "syncSetup" }); + + info("Logged in, sync enabled"); + mockState = { ...baseState, status: "verified", syncEnabled: true }; + checkStates({ + accountState: hasFxaAccount, + loginState: fxaLoginVerified, + deviceInfoVisible: true, + syncState: syncConnected, + }); + + // Untested: Sync now button. + + // Check the learn more link opens a tab. + await assertTabOpens("enginesLearnMore", "?page=learnMore"); + + // Untested: Disconnect button. +}); + +add_task(async function testEngines() { + function assertEnginesEnabled(...expectedEnabled) { + for (let engine of ALL_ENGINES) { + let enabled = Services.prefs.getBoolPref(`${PREF_PREFIX}.${engine}`); + Assert.equal( + enabled, + expectedEnabled.includes(engine), + `${engine} should be ${ + expectedEnabled.includes(engine) ? "enabled" : "disabled" + }` + ); + } + } + + function assertEnginesShown(...expectEngines) { + let ENGINES_TO_ITEMS = { + accounts: "showSyncAccount", + identities: "showSyncIdentity", + addressbooks: "showSyncAddress", + calendars: "showSyncCalendar", + passwords: "showSyncPasswords", + }; + let expectItems = expectEngines.map(engine => ENGINES_TO_ITEMS[engine]); + let items = Array.from( + prefsDocument.querySelectorAll("#showSyncedList > li:not([hidden])"), + li => li.id + ); + Assert.deepEqual(items, expectItems, "enabled engines shown correctly"); + } + + assertEnginesShown(...ALL_ENGINES); + Services.prefs.setBoolPref(`${PREF_PREFIX}.accounts`, false); + assertEnginesShown("identities", "addressbooks", "calendars", "passwords"); + Services.prefs.setBoolPref(`${PREF_PREFIX}.identities`, false); + Services.prefs.setBoolPref(`${PREF_PREFIX}.addressbooks`, false); + Services.prefs.setBoolPref(`${PREF_PREFIX}.calendars`, false); + assertEnginesShown("passwords"); + Services.prefs.setBoolPref(`${PREF_PREFIX}.passwords`, false); + assertEnginesShown(); + + info("Checking the engine selection dialog"); + await openEngineDialog({ + toggleEngines: ["accounts", "identities", "passwords"], + }); + + assertEnginesEnabled("accounts", "identities", "passwords"); + assertEnginesShown("accounts", "identities", "passwords"); + + await openEngineDialog({ + expectEngines: ["accounts", "identities", "passwords"], + toggleEngines: ["calendars", "passwords"], + action: "cancel", + }); + + assertEnginesEnabled("accounts", "identities", "passwords"); + assertEnginesShown("accounts", "identities", "passwords"); + + await openEngineDialog({ + expectEngines: ["accounts", "identities", "passwords"], + toggleEngines: ["calendars", "passwords"], + action: "accept", + }); + + assertEnginesEnabled("accounts", "identities", "calendars"); + assertEnginesShown("accounts", "identities", "calendars"); + + Services.prefs.setBoolPref(`${PREF_PREFIX}.addressbooks`, true); + Services.prefs.setBoolPref(`${PREF_PREFIX}.passwords`, true); + assertEnginesShown(...ALL_ENGINES); +}); + +async function openEngineDialog({ + expectEngines = [], + toggleEngines = [], + action = "accept", + button = "syncChangeOptions", +}) { + const ENGINES_TO_CHECKBOXES = { + accounts: "configSyncAccount", + identities: "configSyncIdentity", + addressbooks: "configSyncAddress", + calendars: "configSyncCalendar", + passwords: "configSyncPasswords", + }; + let dialogPromise = BrowserTestUtils.promiseAlertDialogOpen( + undefined, + "chrome://messenger/content/preferences/syncDialog.xhtml", + { isSubDialog: true } + ); + EventUtils.synthesizeMouseAtCenter( + prefsDocument.getElementById(button), + {}, + prefsWindow + ); + let dialogWindow = await dialogPromise; + let dialogDocument = dialogWindow.document; + await new Promise(resolve => dialogWindow.setTimeout(resolve)); + + let expectItems = expectEngines.map(engine => ENGINES_TO_CHECKBOXES[engine]); + + let checkedItems = Array.from( + dialogDocument.querySelectorAll(`input[type="checkbox"]`) + ) + .filter(cb => cb.checked) + .map(cb => cb.id); + Assert.deepEqual( + checkedItems, + expectItems, + "enabled engines checked correctly" + ); + + for (let toggleItem of toggleEngines) { + let checkbox = dialogDocument.getElementById( + ENGINES_TO_CHECKBOXES[toggleItem] + ); + checkbox.checked = !checkbox.checked; + } + + EventUtils.synthesizeMouseAtCenter( + dialogDocument.querySelector("dialog").getButton(action), + {}, + dialogWindow + ); +} |