summaryrefslogtreecommitdiffstats
path: root/services/fxaccounts/tests/xpcshell/test_web_channel.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/fxaccounts/tests/xpcshell/test_web_channel.js')
-rw-r--r--services/fxaccounts/tests/xpcshell/test_web_channel.js1358
1 files changed, 1358 insertions, 0 deletions
diff --git a/services/fxaccounts/tests/xpcshell/test_web_channel.js b/services/fxaccounts/tests/xpcshell/test_web_channel.js
new file mode 100644
index 0000000000..63742439ea
--- /dev/null
+++ b/services/fxaccounts/tests/xpcshell/test_web_channel.js
@@ -0,0 +1,1358 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { ON_PROFILE_CHANGE_NOTIFICATION, WEBCHANNEL_ID, log } =
+ ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js");
+const { CryptoUtils } = ChromeUtils.importESModule(
+ "resource://services-crypto/utils.sys.mjs"
+);
+const { FxAccountsWebChannel, FxAccountsWebChannelHelpers } =
+ ChromeUtils.importESModule(
+ "resource://gre/modules/FxAccountsWebChannel.sys.mjs"
+ );
+
+const URL_STRING = "https://example.com";
+
+const mockSendingContext = {
+ browsingContext: { top: { embedderElement: {} } },
+ principal: {},
+ eventTarget: {},
+};
+
+add_test(function () {
+ validationHelper(undefined, "Error: Missing configuration options");
+
+ validationHelper(
+ {
+ channel_id: WEBCHANNEL_ID,
+ },
+ "Error: Missing 'content_uri' option"
+ );
+
+ validationHelper(
+ {
+ content_uri: "bad uri",
+ channel_id: WEBCHANNEL_ID,
+ },
+ /NS_ERROR_MALFORMED_URI/
+ );
+
+ validationHelper(
+ {
+ content_uri: URL_STRING,
+ },
+ "Error: Missing 'channel_id' option"
+ );
+
+ run_next_test();
+});
+
+add_task(async function test_rejection_reporting() {
+ Services.prefs.setBoolPref(
+ "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
+ false
+ );
+
+ let mockMessage = {
+ command: "fxaccounts:login",
+ messageId: "1234",
+ data: { email: "testuser@testuser.com" },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ login(accountData) {
+ equal(
+ accountData.email,
+ "testuser@testuser.com",
+ "Should forward incoming message data to the helper"
+ );
+ return Promise.reject(new Error("oops"));
+ },
+ },
+ });
+
+ let promiseSend = new Promise(resolve => {
+ channel._channel.send = (message, context) => {
+ resolve({ message, context });
+ };
+ });
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+
+ let { message, context } = await promiseSend;
+
+ equal(context, mockSendingContext, "Should forward the original context");
+ equal(
+ message.command,
+ "fxaccounts:login",
+ "Should include the incoming command"
+ );
+ equal(message.messageId, "1234", "Should include the message ID");
+ equal(
+ message.data.error.message,
+ "Error: oops",
+ "Should convert the error message to a string"
+ );
+ notStrictEqual(
+ message.data.error.stack,
+ null,
+ "Should include the stack for JS error rejections"
+ );
+});
+
+add_test(function test_exception_reporting() {
+ let mockMessage = {
+ command: "fxaccounts:sync_preferences",
+ messageId: "5678",
+ data: { entryPoint: "fxa:verification_complete" },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ openSyncPreferences(browser, entryPoint) {
+ equal(
+ entryPoint,
+ "fxa:verification_complete",
+ "Should forward incoming message data to the helper"
+ );
+ throw new TypeError("splines not reticulated");
+ },
+ },
+ });
+
+ channel._channel.send = (message, context) => {
+ equal(context, mockSendingContext, "Should forward the original context");
+ equal(
+ message.command,
+ "fxaccounts:sync_preferences",
+ "Should include the incoming command"
+ );
+ equal(message.messageId, "5678", "Should include the message ID");
+ equal(
+ message.data.error.message,
+ "TypeError: splines not reticulated",
+ "Should convert the exception to a string"
+ );
+ notStrictEqual(
+ message.data.error.stack,
+ null,
+ "Should include the stack for JS exceptions"
+ );
+
+ run_next_test();
+ };
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_error_message_remove_profile_path() {
+ const errors = {
+ windows: {
+ err: new Error(
+ "Win error 183 during operation rename on file C:\\Users\\Some Computer\\AppData\\Roaming\\" +
+ "Mozilla\\Firefox\\Profiles\\dbzjmzxa.default\\signedInUser.json (Cannot create a file)"
+ ),
+ expected:
+ "Error: Win error 183 during operation rename on file C:[REDACTED]signedInUser.json (Cannot create a file)",
+ },
+ unix: {
+ err: new Error(
+ "Unix error 28 during operation write on file /Users/someuser/Library/Application Support/" +
+ "Firefox/Profiles/dbzjmzxa.default-release-7/signedInUser.json (No space left on device)"
+ ),
+ expected:
+ "Error: Unix error 28 during operation write on file [REDACTED]signedInUser.json (No space left on device)",
+ },
+ netpath: {
+ err: new Error(
+ "Win error 32 during operation rename on file \\\\SVC.LOC\\HOMEDIRS$\\USERNAME\\Mozilla\\" +
+ "Firefox\\Profiles\\dbzjmzxa.default-release-7\\signedInUser.json (No space left on device)"
+ ),
+ expected:
+ "Error: Win error 32 during operation rename on file [REDACTED]signedInUser.json (No space left on device)",
+ },
+ mount: {
+ err: new Error(
+ "Win error 649 during operation rename on file C:\\SnapVolumes\\MountPoints\\" +
+ "{9e399ec5-0000-0000-0000-100000000000}\\SVROOT\\Users\\username\\AppData\\Roaming\\Mozilla\\Firefox\\" +
+ "Profiles\\dbzjmzxa.default-release\\signedInUser.json (The create operation failed)"
+ ),
+ expected:
+ "Error: Win error 649 during operation rename on file C:[REDACTED]signedInUser.json " +
+ "(The create operation failed)",
+ },
+ };
+ const mockMessage = {
+ command: "fxaccounts:sync_preferences",
+ messageId: "1234",
+ };
+ const channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ });
+
+ let testNum = 0;
+ const toTest = Object.keys(errors).length;
+ for (const key in errors) {
+ let error = errors[key];
+ channel._channel.send = (message, context) => {
+ equal(
+ message.data.error.message,
+ error.expected,
+ "Should remove the profile path from the error message"
+ );
+ testNum++;
+ if (testNum === toTest) {
+ run_next_test();
+ }
+ };
+ channel._sendError(error.err, mockMessage, mockSendingContext);
+ }
+});
+
+add_test(function test_profile_image_change_message() {
+ var mockMessage = {
+ command: "profile:change",
+ data: { uid: "foo" },
+ };
+
+ makeObserver(ON_PROFILE_CHANGE_NOTIFICATION, function (subject, topic, data) {
+ Assert.equal(data, "foo");
+ run_next_test();
+ });
+
+ var channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ });
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_login_message() {
+ let mockMessage = {
+ command: "fxaccounts:login",
+ data: { email: "testuser@testuser.com" },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ login(accountData) {
+ Assert.equal(accountData.email, "testuser@testuser.com");
+ run_next_test();
+ return Promise.resolve();
+ },
+ },
+ });
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_logout_message() {
+ let mockMessage = {
+ command: "fxaccounts:logout",
+ data: { uid: "foo" },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ logout(uid) {
+ Assert.equal(uid, "foo");
+ run_next_test();
+ return Promise.resolve();
+ },
+ },
+ });
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_delete_message() {
+ let mockMessage = {
+ command: "fxaccounts:delete",
+ data: { uid: "foo" },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ logout(uid) {
+ Assert.equal(uid, "foo");
+ run_next_test();
+ return Promise.resolve();
+ },
+ },
+ });
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_can_link_account_message() {
+ let mockMessage = {
+ command: "fxaccounts:can_link_account",
+ data: { email: "testuser@testuser.com" },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ shouldAllowRelink(email) {
+ Assert.equal(email, "testuser@testuser.com");
+ run_next_test();
+ },
+ },
+ });
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_sync_preferences_message() {
+ let mockMessage = {
+ command: "fxaccounts:sync_preferences",
+ data: { entryPoint: "fxa:verification_complete" },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ openSyncPreferences(browser, entryPoint) {
+ Assert.equal(entryPoint, "fxa:verification_complete");
+ Assert.equal(
+ browser,
+ mockSendingContext.browsingContext.top.embedderElement
+ );
+ run_next_test();
+ },
+ },
+ });
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_fxa_status_message() {
+ let mockMessage = {
+ command: "fxaccounts:fxa_status",
+ messageId: 123,
+ data: {
+ service: "sync",
+ context: "fx_desktop_v3",
+ },
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ helpers: {
+ async getFxaStatus(service, sendingContext, isPairing, context) {
+ Assert.equal(service, "sync");
+ Assert.equal(sendingContext, mockSendingContext);
+ Assert.ok(!isPairing);
+ Assert.equal(context, "fx_desktop_v3");
+ return {
+ signedInUser: {
+ email: "testuser@testuser.com",
+ sessionToken: "session-token",
+ uid: "uid",
+ verified: true,
+ },
+ capabilities: {
+ engines: ["creditcards", "addresses"],
+ },
+ };
+ },
+ },
+ });
+
+ channel._channel = {
+ send(response, sendingContext) {
+ Assert.equal(response.command, "fxaccounts:fxa_status");
+ Assert.equal(response.messageId, 123);
+
+ let signedInUser = response.data.signedInUser;
+ Assert.ok(!!signedInUser);
+ Assert.equal(signedInUser.email, "testuser@testuser.com");
+ Assert.equal(signedInUser.sessionToken, "session-token");
+ Assert.equal(signedInUser.uid, "uid");
+ Assert.equal(signedInUser.verified, true);
+
+ deepEqual(response.data.capabilities.engines, [
+ "creditcards",
+ "addresses",
+ ]);
+
+ run_next_test();
+ },
+ };
+
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+});
+
+add_test(function test_unrecognized_message() {
+ let mockMessage = {
+ command: "fxaccounts:unrecognized",
+ data: {},
+ };
+
+ let channel = new FxAccountsWebChannel({
+ channel_id: WEBCHANNEL_ID,
+ content_uri: URL_STRING,
+ });
+
+ // no error is expected.
+ channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
+ run_next_test();
+});
+
+add_test(function test_helpers_should_allow_relink_same_email() {
+ let helpers = new FxAccountsWebChannelHelpers();
+
+ helpers.setPreviousAccountNameHashPref("testuser@testuser.com");
+ Assert.ok(helpers.shouldAllowRelink("testuser@testuser.com"));
+
+ run_next_test();
+});
+
+add_test(function test_helpers_should_allow_relink_different_email() {
+ let helpers = new FxAccountsWebChannelHelpers();
+
+ helpers.setPreviousAccountNameHashPref("testuser@testuser.com");
+
+ helpers._promptForRelink = acctName => {
+ return acctName === "allowed_to_relink@testuser.com";
+ };
+
+ Assert.ok(helpers.shouldAllowRelink("allowed_to_relink@testuser.com"));
+ Assert.ok(!helpers.shouldAllowRelink("not_allowed_to_relink@testuser.com"));
+
+ run_next_test();
+});
+
+add_task(async function test_helpers_login_without_customize_sync() {
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ setSignedInUser(accountData) {
+ return new Promise(resolve => {
+ // ensure fxAccounts is informed of the new user being signed in.
+ Assert.equal(accountData.email, "testuser@testuser.com");
+
+ // verifiedCanLinkAccount should be stripped in the data.
+ Assert.equal(false, "verifiedCanLinkAccount" in accountData);
+
+ resolve();
+ });
+ },
+ },
+ telemetry: {
+ recordConnection: sinon.spy(),
+ },
+ },
+ weaveXPCOM: {
+ whenLoaded() {},
+ Weave: {
+ Service: {
+ configure() {},
+ },
+ },
+ },
+ });
+
+ // ensure the previous account pref is overwritten.
+ helpers.setPreviousAccountNameHashPref("lastuser@testuser.com");
+
+ await helpers.login({
+ email: "testuser@testuser.com",
+ verifiedCanLinkAccount: true,
+ customizeSync: false,
+ });
+ Assert.ok(
+ helpers._fxAccounts.telemetry.recordConnection.calledWith([], "webchannel")
+ );
+});
+
+add_task(async function test_helpers_login_set_previous_account_name_hash() {
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ setSignedInUser(accountData) {
+ return new Promise(resolve => {
+ // previously signed in user preference is updated.
+ Assert.equal(
+ helpers.getPreviousAccountNameHashPref(),
+ CryptoUtils.sha256Base64("newuser@testuser.com")
+ );
+ resolve();
+ });
+ },
+ },
+ telemetry: {
+ recordConnection() {},
+ },
+ },
+ weaveXPCOM: {
+ whenLoaded() {},
+ Weave: {
+ Service: {
+ configure() {},
+ },
+ },
+ },
+ });
+
+ // ensure the previous account pref is overwritten.
+ helpers.setPreviousAccountNameHashPref("lastuser@testuser.com");
+
+ await helpers.login({
+ email: "newuser@testuser.com",
+ verifiedCanLinkAccount: true,
+ customizeSync: false,
+ verified: true,
+ });
+});
+
+add_task(
+ async function test_helpers_login_dont_set_previous_account_name_hash_for_unverified_emails() {
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ setSignedInUser(accountData) {
+ return new Promise(resolve => {
+ // previously signed in user preference should not be updated.
+ Assert.equal(
+ helpers.getPreviousAccountNameHashPref(),
+ CryptoUtils.sha256Base64("lastuser@testuser.com")
+ );
+ resolve();
+ });
+ },
+ },
+ telemetry: {
+ recordConnection() {},
+ },
+ },
+ weaveXPCOM: {
+ whenLoaded() {},
+ Weave: {
+ Service: {
+ configure() {},
+ },
+ },
+ },
+ });
+
+ // ensure the previous account pref is overwritten.
+ helpers.setPreviousAccountNameHashPref("lastuser@testuser.com");
+
+ await helpers.login({
+ email: "newuser@testuser.com",
+ verifiedCanLinkAccount: true,
+ customizeSync: false,
+ });
+ }
+);
+
+add_task(async function test_helpers_login_with_customize_sync() {
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ setSignedInUser(accountData) {
+ return new Promise(resolve => {
+ // ensure fxAccounts is informed of the new user being signed in.
+ Assert.equal(accountData.email, "testuser@testuser.com");
+
+ // customizeSync should be stripped in the data.
+ Assert.equal(false, "customizeSync" in accountData);
+
+ resolve();
+ });
+ },
+ },
+ telemetry: {
+ recordConnection: sinon.spy(),
+ },
+ },
+ weaveXPCOM: {
+ whenLoaded() {},
+ Weave: {
+ Service: {
+ configure() {},
+ },
+ },
+ },
+ });
+
+ await helpers.login({
+ email: "testuser@testuser.com",
+ verifiedCanLinkAccount: true,
+ customizeSync: true,
+ });
+ Assert.ok(
+ helpers._fxAccounts.telemetry.recordConnection.calledWith([], "webchannel")
+ );
+});
+
+add_task(
+ async function test_helpers_login_with_customize_sync_and_declined_engines() {
+ let configured = false;
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ setSignedInUser(accountData) {
+ return new Promise(resolve => {
+ // ensure fxAccounts is informed of the new user being signed in.
+ Assert.equal(accountData.email, "testuser@testuser.com");
+
+ // customizeSync should be stripped in the data.
+ Assert.equal(false, "customizeSync" in accountData);
+ Assert.equal(false, "services" in accountData);
+ resolve();
+ });
+ },
+ },
+ telemetry: {
+ recordConnection: sinon.spy(),
+ },
+ },
+ weaveXPCOM: {
+ whenLoaded() {},
+ Weave: {
+ Service: {
+ configure() {
+ configured = true;
+ },
+ },
+ },
+ },
+ });
+
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.addons"),
+ true
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.bookmarks"),
+ true
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.history"),
+ true
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.passwords"),
+ true
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.prefs"),
+ true
+ );
+ Assert.equal(Services.prefs.getBoolPref("services.sync.engine.tabs"), true);
+ await helpers.login({
+ email: "testuser@testuser.com",
+ verifiedCanLinkAccount: true,
+ customizeSync: true,
+ services: {
+ sync: {
+ offeredEngines: [
+ "addons",
+ "bookmarks",
+ "history",
+ "passwords",
+ "prefs",
+ ],
+ declinedEngines: ["addons", "prefs"],
+ },
+ },
+ });
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.addons"),
+ false
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.bookmarks"),
+ true
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.history"),
+ true
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.passwords"),
+ true
+ );
+ Assert.equal(
+ Services.prefs.getBoolPref("services.sync.engine.prefs"),
+ false
+ );
+ Assert.equal(Services.prefs.getBoolPref("services.sync.engine.tabs"), true);
+ Assert.ok(configured, "sync was configured");
+ Assert.ok(
+ helpers._fxAccounts.telemetry.recordConnection.calledWith(
+ ["sync"],
+ "webchannel"
+ )
+ );
+ }
+);
+
+add_task(async function test_helpers_login_with_offered_sync_engines() {
+ let helpers;
+ let configured = false;
+ const setSignedInUserCalled = new Promise(resolve => {
+ helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ async setSignedInUser(accountData) {
+ resolve(accountData);
+ },
+ },
+ telemetry: {
+ recordConnection() {},
+ },
+ },
+ weaveXPCOM: {
+ whenLoaded() {},
+ Weave: {
+ Service: {
+ configure() {
+ configured = true;
+ },
+ },
+ },
+ },
+ });
+ });
+
+ Services.prefs.setBoolPref("services.sync.engine.creditcards", false);
+ Services.prefs.setBoolPref("services.sync.engine.addresses", false);
+
+ await helpers.login({
+ email: "testuser@testuser.com",
+ verifiedCanLinkAccount: true,
+ customizeSync: true,
+ services: {
+ sync: {
+ declinedEngines: ["addresses"],
+ offeredEngines: ["creditcards", "addresses"],
+ },
+ },
+ });
+
+ const accountData = await setSignedInUserCalled;
+
+ // ensure fxAccounts is informed of the new user being signed in.
+ equal(accountData.email, "testuser@testuser.com");
+
+ // services should be stripped in the data.
+ ok(!("services" in accountData));
+ // credit cards was offered but not declined.
+ equal(Services.prefs.getBoolPref("services.sync.engine.creditcards"), true);
+ // addresses was offered and explicitely declined.
+ equal(Services.prefs.getBoolPref("services.sync.engine.addresses"), false);
+ ok(configured);
+});
+
+add_task(async function test_helpers_login_nothing_offered() {
+ let helpers;
+ let configured = false;
+ const setSignedInUserCalled = new Promise(resolve => {
+ helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ async setSignedInUser(accountData) {
+ resolve(accountData);
+ },
+ },
+ telemetry: {
+ recordConnection() {},
+ },
+ },
+ weaveXPCOM: {
+ whenLoaded() {},
+ Weave: {
+ Service: {
+ configure() {
+ configured = true;
+ },
+ },
+ },
+ },
+ });
+ });
+
+ // doesn't really matter if it's *all* engines...
+ const allEngines = [
+ "addons",
+ "addresses",
+ "bookmarks",
+ "creditcards",
+ "history",
+ "passwords",
+ "prefs",
+ ];
+ for (let name of allEngines) {
+ Services.prefs.clearUserPref("services.sync.engine." + name);
+ }
+
+ await helpers.login({
+ email: "testuser@testuser.com",
+ verifiedCanLinkAccount: true,
+ services: {
+ sync: {},
+ },
+ });
+
+ const accountData = await setSignedInUserCalled;
+ // ensure fxAccounts is informed of the new user being signed in.
+ equal(accountData.email, "testuser@testuser.com");
+
+ for (let name of allEngines) {
+ Assert.ok(!Services.prefs.prefHasUserValue("services.sync.engine." + name));
+ }
+ Assert.ok(configured);
+});
+
+add_test(function test_helpers_open_sync_preferences() {
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {},
+ });
+
+ let mockBrowser = {
+ loadURI(uri) {
+ Assert.equal(
+ uri.spec,
+ "about:preferences?entrypoint=fxa%3Averification_complete#sync"
+ );
+ run_next_test();
+ },
+ };
+
+ helpers.openSyncPreferences(mockBrowser, "fxa:verification_complete");
+});
+
+add_task(async function test_helpers_getFxAStatus_extra_engines() {
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ getUserAccountData() {
+ return Promise.resolve({
+ email: "testuser@testuser.com",
+ sessionToken: "sessionToken",
+ uid: "uid",
+ verified: true,
+ });
+ },
+ },
+ },
+ privateBrowsingUtils: {
+ isBrowserPrivate: () => true,
+ },
+ });
+
+ Services.prefs.setBoolPref(
+ "services.sync.engine.creditcards.available",
+ true
+ );
+ // Not defining "services.sync.engine.addresses.available" on purpose.
+
+ let fxaStatus = await helpers.getFxaStatus("sync", mockSendingContext);
+ ok(!!fxaStatus);
+ ok(!!fxaStatus.signedInUser);
+ deepEqual(fxaStatus.capabilities.engines, ["creditcards"]);
+});
+
+add_task(async function test_helpers_getFxaStatus_allowed_signedInUser() {
+ let wasCalled = {
+ getUserAccountData: false,
+ shouldAllowFxaStatus: false,
+ };
+
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ getUserAccountData() {
+ wasCalled.getUserAccountData = true;
+ return Promise.resolve({
+ email: "testuser@testuser.com",
+ sessionToken: "sessionToken",
+ uid: "uid",
+ verified: true,
+ });
+ },
+ },
+ },
+ });
+
+ helpers.shouldAllowFxaStatus = (service, sendingContext) => {
+ wasCalled.shouldAllowFxaStatus = true;
+ Assert.equal(service, "sync");
+ Assert.equal(sendingContext, mockSendingContext);
+
+ return true;
+ };
+
+ return helpers.getFxaStatus("sync", mockSendingContext).then(fxaStatus => {
+ Assert.ok(!!fxaStatus);
+ Assert.ok(wasCalled.getUserAccountData);
+ Assert.ok(wasCalled.shouldAllowFxaStatus);
+
+ Assert.ok(!!fxaStatus.signedInUser);
+ let { signedInUser } = fxaStatus;
+
+ Assert.equal(signedInUser.email, "testuser@testuser.com");
+ Assert.equal(signedInUser.sessionToken, "sessionToken");
+ Assert.equal(signedInUser.uid, "uid");
+ Assert.ok(signedInUser.verified);
+
+ // These properties are filtered and should not
+ // be returned to the requester.
+ Assert.equal(false, "kSync" in signedInUser);
+ Assert.equal(false, "kXCS" in signedInUser);
+ Assert.equal(false, "kExtSync" in signedInUser);
+ Assert.equal(false, "kExtKbHash" in signedInUser);
+ });
+});
+
+add_task(async function test_helpers_getFxaStatus_allowed_no_signedInUser() {
+ let wasCalled = {
+ getUserAccountData: false,
+ shouldAllowFxaStatus: false,
+ };
+
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ getUserAccountData() {
+ wasCalled.getUserAccountData = true;
+ return Promise.resolve(null);
+ },
+ },
+ },
+ });
+
+ helpers.shouldAllowFxaStatus = (service, sendingContext) => {
+ wasCalled.shouldAllowFxaStatus = true;
+ Assert.equal(service, "sync");
+ Assert.equal(sendingContext, mockSendingContext);
+
+ return true;
+ };
+
+ return helpers.getFxaStatus("sync", mockSendingContext).then(fxaStatus => {
+ Assert.ok(!!fxaStatus);
+ Assert.ok(wasCalled.getUserAccountData);
+ Assert.ok(wasCalled.shouldAllowFxaStatus);
+
+ Assert.equal(null, fxaStatus.signedInUser);
+ });
+});
+
+add_task(async function test_helpers_getFxaStatus_not_allowed() {
+ let wasCalled = {
+ getUserAccountData: false,
+ shouldAllowFxaStatus: false,
+ };
+
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ getUserAccountData() {
+ wasCalled.getUserAccountData = true;
+ return Promise.resolve(null);
+ },
+ },
+ },
+ });
+
+ helpers.shouldAllowFxaStatus = (
+ service,
+ sendingContext,
+ isPairing,
+ context
+ ) => {
+ wasCalled.shouldAllowFxaStatus = true;
+ Assert.equal(service, "sync");
+ Assert.equal(sendingContext, mockSendingContext);
+ Assert.ok(!isPairing);
+ Assert.equal(context, "fx_desktop_v3");
+
+ return false;
+ };
+
+ return helpers
+ .getFxaStatus("sync", mockSendingContext, false, "fx_desktop_v3")
+ .then(fxaStatus => {
+ Assert.ok(!!fxaStatus);
+ Assert.ok(!wasCalled.getUserAccountData);
+ Assert.ok(wasCalled.shouldAllowFxaStatus);
+
+ Assert.equal(null, fxaStatus.signedInUser);
+ });
+});
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_sync_service_not_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return false;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "sync",
+ mockSendingContext,
+ false
+ );
+ Assert.ok(shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_desktop_context_not_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return false;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "",
+ mockSendingContext,
+ false,
+ "fx_desktop_v3"
+ );
+ Assert.ok(shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_oauth_service_not_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return false;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "dcdb5ae7add825d2",
+ mockSendingContext,
+ false
+ );
+ Assert.ok(shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_no_service_not_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return false;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "",
+ mockSendingContext,
+ false
+ );
+ Assert.ok(shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_sync_service_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return true;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "sync",
+ mockSendingContext,
+ false
+ );
+ Assert.ok(shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_desktop_context_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return true;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "",
+ mockSendingContext,
+ false,
+ "fx_desktop_v3"
+ );
+ Assert.ok(shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_oauth_service_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return true;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "dcdb5ae7add825d2",
+ mockSendingContext,
+ false
+ );
+ Assert.ok(!shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_oauth_service_pairing_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return true;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "dcdb5ae7add825d2",
+ mockSendingContext,
+ true
+ );
+ Assert.ok(shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(
+ async function test_helpers_shouldAllowFxaStatus_no_service_private_browsing() {
+ let wasCalled = {
+ isPrivateBrowsingMode: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({});
+
+ helpers.isPrivateBrowsingMode = sendingContext => {
+ wasCalled.isPrivateBrowsingMode = true;
+ Assert.equal(sendingContext, mockSendingContext);
+ return true;
+ };
+
+ let shouldAllowFxaStatus = helpers.shouldAllowFxaStatus(
+ "",
+ mockSendingContext,
+ false
+ );
+ Assert.ok(!shouldAllowFxaStatus);
+ Assert.ok(wasCalled.isPrivateBrowsingMode);
+ }
+);
+
+add_task(async function test_helpers_isPrivateBrowsingMode_private_browsing() {
+ let wasCalled = {
+ isBrowserPrivate: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({
+ privateBrowsingUtils: {
+ isBrowserPrivate(browser) {
+ wasCalled.isBrowserPrivate = true;
+ Assert.equal(
+ browser,
+ mockSendingContext.browsingContext.top.embedderElement
+ );
+ return true;
+ },
+ },
+ });
+
+ let isPrivateBrowsingMode = helpers.isPrivateBrowsingMode(mockSendingContext);
+ Assert.ok(isPrivateBrowsingMode);
+ Assert.ok(wasCalled.isBrowserPrivate);
+});
+
+add_task(async function test_helpers_isPrivateBrowsingMode_private_browsing() {
+ let wasCalled = {
+ isBrowserPrivate: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({
+ privateBrowsingUtils: {
+ isBrowserPrivate(browser) {
+ wasCalled.isBrowserPrivate = true;
+ Assert.equal(
+ browser,
+ mockSendingContext.browsingContext.top.embedderElement
+ );
+ return false;
+ },
+ },
+ });
+
+ let isPrivateBrowsingMode = helpers.isPrivateBrowsingMode(mockSendingContext);
+ Assert.ok(!isPrivateBrowsingMode);
+ Assert.ok(wasCalled.isBrowserPrivate);
+});
+
+add_task(async function test_helpers_change_password() {
+ let wasCalled = {
+ updateUserAccountData: false,
+ updateDeviceRegistration: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ updateUserAccountData(credentials) {
+ return new Promise(resolve => {
+ Assert.ok(credentials.hasOwnProperty("email"));
+ Assert.ok(credentials.hasOwnProperty("uid"));
+ Assert.ok(credentials.hasOwnProperty("unwrapBKey"));
+ Assert.ok(credentials.hasOwnProperty("device"));
+ Assert.equal(null, credentials.device);
+ Assert.equal(null, credentials.encryptedSendTabKeys);
+ // "foo" isn't a field known by storage, so should be dropped.
+ Assert.ok(!credentials.hasOwnProperty("foo"));
+ wasCalled.updateUserAccountData = true;
+
+ resolve();
+ });
+ },
+
+ updateDeviceRegistration() {
+ Assert.equal(arguments.length, 0);
+ wasCalled.updateDeviceRegistration = true;
+ return Promise.resolve();
+ },
+ },
+ },
+ });
+ await helpers.changePassword({
+ email: "email",
+ uid: "uid",
+ unwrapBKey: "unwrapBKey",
+ foo: "foo",
+ });
+ Assert.ok(wasCalled.updateUserAccountData);
+ Assert.ok(wasCalled.updateDeviceRegistration);
+});
+
+add_task(async function test_helpers_change_password_with_error() {
+ let wasCalled = {
+ updateUserAccountData: false,
+ updateDeviceRegistration: false,
+ };
+ let helpers = new FxAccountsWebChannelHelpers({
+ fxAccounts: {
+ _internal: {
+ updateUserAccountData() {
+ wasCalled.updateUserAccountData = true;
+ return Promise.reject();
+ },
+
+ updateDeviceRegistration() {
+ wasCalled.updateDeviceRegistration = true;
+ return Promise.resolve();
+ },
+ },
+ },
+ });
+ try {
+ await helpers.changePassword({});
+ Assert.equal(false, "changePassword should have rejected");
+ } catch (_) {
+ Assert.ok(wasCalled.updateUserAccountData);
+ Assert.ok(!wasCalled.updateDeviceRegistration);
+ }
+});
+
+function makeObserver(aObserveTopic, aObserveFunc) {
+ let callback = function (aSubject, aTopic, aData) {
+ log.debug("observed " + aTopic + " " + aData);
+ if (aTopic == aObserveTopic) {
+ removeMe();
+ aObserveFunc(aSubject, aTopic, aData);
+ }
+ };
+
+ function removeMe() {
+ log.debug("removing observer for " + aObserveTopic);
+ Services.obs.removeObserver(callback, aObserveTopic);
+ }
+
+ Services.obs.addObserver(callback, aObserveTopic);
+ return removeMe;
+}
+
+function validationHelper(params, expected) {
+ try {
+ new FxAccountsWebChannel(params);
+ } catch (e) {
+ if (typeof expected === "string") {
+ return Assert.equal(e.toString(), expected);
+ }
+ return Assert.ok(e.toString().match(expected));
+ }
+ throw new Error("Validation helper error");
+}