Revision control

Copy as Markdown

Other Tools

/* 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(
);
const { FxAccounts } = ChromeUtils.importESModule(
);
FxAccounts.config.promiseConnectAccountURI = entryPoint =>
FxAccounts.config.promiseManageURI = entryPoint =>
FxAccounts.config.promiseChangeAvatarURI = entryPoint =>
const ALL_ENGINES = [
"servers",
"identities",
"addressbooks",
"calendars",
"passwords",
];
const PREF_PREFIX = "services.sync.engine";
let prefsWindow, prefsDocument, tabmail;
add_setup(async function () {
for (const engine of ALL_ENGINES) {
Services.prefs.setBoolPref(`${PREF_PREFIX}.${engine}`, true);
}
({ prefsWindow, prefsDocument } = await openNewPrefsTab("paneSync"));
tabmail = document.getElementById("tabmail");
/** @implements {nsIExternalProtocolService} */
const mockExternalProtocolService = {
QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]),
externalProtocolHandlerExists() {},
isExposedProtocol() {},
loadURI(uri) {
Assert.report(
true,
undefined,
undefined,
`should not be opening ${uri.spec} in an external browser`
);
},
};
const mockExternalProtocolServiceCID = MockRegistrar.register(
"@mozilla.org/uriloader/external-protocol-service;1",
mockExternalProtocolService
);
registerCleanupFunction(() => {
MockRegistrar.unregister(mockExternalProtocolServiceCID);
});
});
add_task(async function testSectionStates() {
const noFxaAccount = prefsDocument.getElementById("noFxaAccount");
const hasFxaAccount = prefsDocument.getElementById("hasFxaAccount");
const accountStates = [noFxaAccount, hasFxaAccount];
const fxaLoginUnverified = prefsDocument.getElementById("fxaLoginUnverified");
const fxaLoginRejected = prefsDocument.getElementById("fxaLoginRejected");
const fxaLoginVerified = prefsDocument.getElementById("fxaLoginVerified");
const loginStates = [fxaLoginUnverified, fxaLoginRejected, fxaLoginVerified];
const fxaDeviceInfo = prefsDocument.getElementById("fxaDeviceInfo");
const syncConnected = prefsDocument.getElementById("syncConnected");
const syncDisconnected = prefsDocument.getElementById("syncDisconnected");
const syncStates = [syncConnected, syncDisconnected];
function assertStateVisible(states, visibleState) {
for (const state of states) {
const visible = BrowserTestUtils.isVisible(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.isVisible(fxaDeviceInfo),
deviceInfoVisible,
`fxaDeviceInfo should be ${deviceInfoVisible ? "visible" : "hidden"}`
);
assertStateVisible(syncStates, syncState);
}
async function assertTabOpens(target, expectedURL) {
if (typeof target == "string") {
target = prefsDocument.getElementById(target);
}
const tabPromise = BrowserTestUtils.waitForEvent(window, "TabOpen");
EventUtils.synthesizeMouseAtCenter(target, {}, prefsWindow);
await tabPromise;
const 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.
const baseState = {
email: "test@invalid",
displayName: "Testy McTest",
avatarURL:
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,
});
const photo = fxaLoginVerified.querySelector(".contact-photo");
Assert.equal(
photo.src,
"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");
const deviceNameInput = prefsDocument.getElementById("fxaDeviceNameInput");
const deviceNameCancel = prefsDocument.getElementById("fxaDeviceNameCancel");
const deviceNameSave = prefsDocument.getElementById("fxaDeviceNameSave");
const deviceNameChange = prefsDocument.getElementById(
"fxaDeviceNameChangeDeviceName"
);
Assert.ok(deviceNameInput.readOnly, "input is read-only");
Assert.ok(
BrowserTestUtils.isHidden(deviceNameCancel),
"cancel button is hidden"
);
Assert.ok(BrowserTestUtils.isHidden(deviceNameSave), "save button is hidden");
Assert.ok(
BrowserTestUtils.isVisible(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.isVisible(deviceNameCancel),
"cancel button is visible"
);
Assert.ok(
BrowserTestUtils.isVisible(deviceNameSave),
"save button is visible"
);
Assert.ok(
BrowserTestUtils.isHidden(deviceNameChange),
"change button is hidden"
);
EventUtils.synthesizeMouseAtCenter(deviceNameCancel, {}, prefsWindow);
Assert.ok(deviceNameInput.readOnly, "input is read-only");
Assert.ok(
BrowserTestUtils.isHidden(deviceNameCancel),
"cancel button is hidden"
);
Assert.ok(BrowserTestUtils.isHidden(deviceNameSave), "save button is hidden");
Assert.ok(
BrowserTestUtils.isVisible(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 (const engine of ALL_ENGINES) {
const enabled = Services.prefs.getBoolPref(`${PREF_PREFIX}.${engine}`);
Assert.equal(
enabled,
expectedEnabled.includes(engine),
`${engine} should be ${
expectedEnabled.includes(engine) ? "enabled" : "disabled"
}`
);
}
}
function assertEnginesShown(...expectEngines) {
const ENGINES_TO_ITEMS = {
servers: "showSyncAccount",
identities: "showSyncIdentity",
addressbooks: "showSyncAddress",
calendars: "showSyncCalendar",
passwords: "showSyncPasswords",
};
const expectItems = expectEngines.map(engine => ENGINES_TO_ITEMS[engine]);
const 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}.servers`, 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: ["servers", "identities", "passwords"],
});
assertEnginesEnabled("servers", "identities", "passwords");
assertEnginesShown("servers", "identities", "passwords");
await openEngineDialog({
expectEngines: ["servers", "identities", "passwords"],
toggleEngines: ["calendars", "passwords"],
action: "cancel",
});
assertEnginesEnabled("servers", "identities", "passwords");
assertEnginesShown("servers", "identities", "passwords");
await openEngineDialog({
expectEngines: ["servers", "identities", "passwords"],
toggleEngines: ["calendars", "passwords"],
action: "accept",
});
assertEnginesEnabled("servers", "identities", "calendars");
assertEnginesShown("servers", "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 = {
servers: "configSyncAccount",
identities: "configSyncIdentity",
addressbooks: "configSyncAddress",
calendars: "configSyncCalendar",
passwords: "configSyncPasswords",
};
await promiseSubDialog(
prefsDocument.getElementById(button),
function (dialogWindow) {
const dialogDocument = dialogWindow.document;
const expectItems = expectEngines.map(
engine => ENGINES_TO_CHECKBOXES[engine]
);
const 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 (const toggleItem of toggleEngines) {
const checkbox = dialogDocument.getElementById(
ENGINES_TO_CHECKBOXES[toggleItem]
);
checkbox.checked = !checkbox.checked;
}
},
action
);
}