373 lines
12 KiB
JavaScript
373 lines
12 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
ChromeUtils.defineESModuleGetters(this, {
|
|
FxAccountsWebChannelHelpers:
|
|
"resource://gre/modules/FxAccountsWebChannel.sys.mjs",
|
|
SelectableProfileService:
|
|
"resource:///modules/profiles/SelectableProfileService.sys.mjs",
|
|
});
|
|
|
|
// Set up mocked profiles
|
|
const mockedProfiles = [
|
|
{
|
|
name: "Profile1",
|
|
path: PathUtils.join(PathUtils.tempDir, "current-profile"),
|
|
email: "testuser1@test.com",
|
|
},
|
|
{
|
|
name: "Profile2",
|
|
path: PathUtils.join(PathUtils.tempDir, "other-profile"),
|
|
email: "testuser2@test.com",
|
|
},
|
|
];
|
|
|
|
// Emulates the response from the user
|
|
let gResponse = 1;
|
|
(function replacePromptService() {
|
|
let originalPromptService = Services.prompt;
|
|
Services.prompt = {
|
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptService]),
|
|
confirmEx: () => gResponse,
|
|
};
|
|
registerCleanupFunction(() => {
|
|
Services.prompt = originalPromptService;
|
|
});
|
|
})();
|
|
|
|
add_setup(function setup() {
|
|
// FOG needs a profile directory to put its data in.
|
|
do_get_profile();
|
|
// FOG needs to be initialized in order for data to flow.
|
|
Services.fog.initializeFOG();
|
|
|
|
// The profile service requires the directory service to have been initialized.
|
|
Cc["@mozilla.org/xre/directory-provider;1"].getService(Ci.nsIXREDirProvider);
|
|
|
|
// The normal isEnabled getter relies on there being a properly working toolkit
|
|
// profile service. For the purposes of this test just mirror the state of the
|
|
// preference.
|
|
Object.defineProperty(SelectableProfileService, "isEnabled", {
|
|
get() {
|
|
return Services.prefs.getBoolPref("browser.profiles.enabled");
|
|
},
|
|
});
|
|
});
|
|
|
|
const dialogVariants = [
|
|
{
|
|
description: "A previous account was signed into this profile",
|
|
prefs: {
|
|
"browser.profiles.enabled": true,
|
|
"browser.profiles.sync.allow-danger-merge": false,
|
|
},
|
|
expectedResponses: [
|
|
{
|
|
responseVal: 0,
|
|
expectedResult: { action: "create-profile" },
|
|
expectedTelemetry: {
|
|
variant_shown: "merge-warning",
|
|
option_clicked: "create-profile",
|
|
},
|
|
},
|
|
{
|
|
responseVal: 1,
|
|
expectedResult: { action: "cancel" },
|
|
expectedTelemetry: {
|
|
variant_shown: "merge-warning",
|
|
option_clicked: "cancel",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
description:
|
|
"A previous account was signed into this profile, with merge allowed",
|
|
prefs: {
|
|
"browser.profiles.enabled": true,
|
|
"browser.profiles.sync.allow-danger-merge": true,
|
|
},
|
|
expectedResponses: [
|
|
{
|
|
responseVal: 0,
|
|
expectedResult: { action: "continue" },
|
|
expectedTelemetry: {
|
|
variant_shown: "merge-warning-allow-merge",
|
|
option_clicked: "continue",
|
|
},
|
|
},
|
|
{
|
|
responseVal: 1,
|
|
expectedResult: { action: "create-profile" },
|
|
expectedTelemetry: {
|
|
variant_shown: "merge-warning-allow-merge",
|
|
option_clicked: "create-profile",
|
|
},
|
|
},
|
|
{
|
|
responseVal: 2,
|
|
expectedResult: { action: "cancel" },
|
|
expectedTelemetry: {
|
|
option_clicked: "cancel",
|
|
variant_shown: "merge-warning-allow-merge",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
add_task(
|
|
async function test_previously_signed_in_dialog_variants_result_and_telemetry() {
|
|
// Create a helper instance
|
|
let helpers = new FxAccountsWebChannelHelpers();
|
|
|
|
// We "pretend" there was another account previously logged in
|
|
helpers.setPreviousAccountNameHashPref("testuser@testuser.com");
|
|
|
|
// Mock methods
|
|
helpers._getAllProfiles = async () => mockedProfiles;
|
|
helpers._getCurrentProfileName = () => mockedProfiles[0].name;
|
|
helpers._readJSONFileAsync = async function (_filePath) {
|
|
return null;
|
|
};
|
|
|
|
for (let variant of dialogVariants) {
|
|
info(`Testing variant: ${variant.description}`);
|
|
// Set the preferences for this variant
|
|
for (let [prefName, prefValue] of Object.entries(variant.prefs)) {
|
|
Services.prefs.setBoolPref(prefName, prefValue);
|
|
}
|
|
|
|
for (let i = 0; i < variant.expectedResponses.length; i++) {
|
|
let { responseVal, expectedResult, expectedTelemetry } =
|
|
variant.expectedResponses[i];
|
|
|
|
gResponse = responseVal;
|
|
let result =
|
|
await helpers.promptProfileSyncWarningIfNeeded("testuser2@test.com");
|
|
//Verify we returned the expected result
|
|
Assert.deepEqual(result, expectedResult);
|
|
|
|
let gleanValue = Glean.syncMergeDialog.clicked.testGetValue();
|
|
// Verify the telemetry is shaped as expected
|
|
Assert.equal(
|
|
gleanValue[i].extra.variant_shown,
|
|
expectedTelemetry.variant_shown,
|
|
"Correctly logged which dialog variant was shown to the user"
|
|
);
|
|
Assert.equal(
|
|
gleanValue[i].extra.option_clicked,
|
|
expectedTelemetry.option_clicked,
|
|
"Correctly logged which option the user selected"
|
|
);
|
|
}
|
|
// Reset Glean for next iteration
|
|
Services.fog.testResetFOG();
|
|
}
|
|
|
|
// Clean up preferences
|
|
Services.prefs.clearUserPref("browser.profiles.enabled");
|
|
Services.prefs.clearUserPref("browser.profiles.sync.allow-danger-merge");
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Testing the dialog variants where another profile is signed into the account
|
|
* we're trying to sign into
|
|
*/
|
|
const anotherProfileDialogVariants = [
|
|
{
|
|
description:
|
|
"Another profile is logged into the account we're trying to sign into",
|
|
prefs: {
|
|
"browser.profiles.enabled": true,
|
|
"browser.profiles.sync.allow-danger-merge": false,
|
|
},
|
|
expectedResponses: [
|
|
{
|
|
responseVal: 0,
|
|
// switch-profile also returns what the profile we switch to
|
|
expectedResult: {
|
|
action: "switch-profile",
|
|
data: {
|
|
name: "Profile2",
|
|
path: PathUtils.join(PathUtils.tempDir, "other-profile"),
|
|
email: "testuser2@test.com",
|
|
},
|
|
},
|
|
expectedTelemetry: {
|
|
option_clicked: "switch-profile",
|
|
variant_shown: "sync-warning",
|
|
},
|
|
},
|
|
{
|
|
responseVal: 1,
|
|
expectedResult: { action: "cancel" },
|
|
expectedTelemetry: {
|
|
option_clicked: "cancel",
|
|
variant_shown: "sync-warning",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
description:
|
|
"Another profile is logged into the account we're trying to sign into, with merge allowed",
|
|
prefs: {
|
|
"browser.profiles.enabled": true,
|
|
"browser.profiles.sync.allow-danger-merge": true,
|
|
},
|
|
expectedResponses: [
|
|
{
|
|
responseVal: 0,
|
|
expectedResult: { action: "continue" },
|
|
expectedTelemetry: {
|
|
option_clicked: "continue",
|
|
variant_shown: "sync-warning-allow-merge",
|
|
},
|
|
},
|
|
{
|
|
responseVal: 1,
|
|
// switch-profile also returns what the profile we switch to
|
|
expectedResult: {
|
|
action: "switch-profile",
|
|
data: {
|
|
name: "Profile2",
|
|
path: PathUtils.join(PathUtils.tempDir, "other-profile"),
|
|
email: "testuser2@test.com",
|
|
},
|
|
},
|
|
expectedTelemetry: {
|
|
option_clicked: "switch-profile",
|
|
variant_shown: "sync-warning-allow-merge",
|
|
},
|
|
},
|
|
{
|
|
responseVal: 2,
|
|
expectedResult: { action: "cancel" },
|
|
expectedTelemetry: {
|
|
option_clicked: "cancel",
|
|
variant_shown: "sync-warning-allow-merge",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
add_task(
|
|
async function test_another_profile_signed_in_variants_result_and_telemetry() {
|
|
// Create a helper instance
|
|
let helpers = new FxAccountsWebChannelHelpers();
|
|
|
|
// Mock methods
|
|
helpers._getAllProfiles = async () => mockedProfiles;
|
|
helpers._getCurrentProfileName = () => mockedProfiles[0].name;
|
|
// Mock the file reading to simulate the account being signed into the other profile
|
|
helpers._readJSONFileAsync = async function (filePath) {
|
|
if (filePath.includes("current-profile")) {
|
|
// No signed-in user in the current profile
|
|
return null;
|
|
} else if (filePath.includes("other-profile")) {
|
|
// The account is signed into the other profile
|
|
return {
|
|
version: 1,
|
|
accountData: { email: "testuser2@test.com" },
|
|
};
|
|
}
|
|
return null;
|
|
};
|
|
|
|
for (let variant of anotherProfileDialogVariants) {
|
|
info(`Testing variant: ${variant.description}`);
|
|
// Set the preferences for this variant
|
|
for (let [prefName, prefValue] of Object.entries(variant.prefs)) {
|
|
Services.prefs.setBoolPref(prefName, prefValue);
|
|
}
|
|
|
|
for (let i = 0; i < variant.expectedResponses.length; i++) {
|
|
let { responseVal, expectedResult, expectedTelemetry } =
|
|
variant.expectedResponses[i];
|
|
|
|
gResponse = responseVal;
|
|
let result =
|
|
await helpers.promptProfileSyncWarningIfNeeded("testuser2@test.com");
|
|
//Verify we returned the expected result
|
|
Assert.deepEqual(result, expectedResult);
|
|
|
|
let gleanValue = Glean.syncMergeDialog.clicked.testGetValue();
|
|
// Verify the telemetry is shaped as expected
|
|
Assert.equal(
|
|
gleanValue[i].extra.variant_shown,
|
|
expectedTelemetry.variant_shown,
|
|
"Correctly logged which dialog variant was shown to the user"
|
|
);
|
|
Assert.equal(
|
|
gleanValue[i].extra.option_clicked,
|
|
expectedTelemetry.option_clicked,
|
|
"Correctly logged which option the user selected"
|
|
);
|
|
}
|
|
// Reset Glean for next iteration
|
|
Services.fog.testResetFOG();
|
|
}
|
|
|
|
// Clean up preferences
|
|
Services.prefs.clearUserPref("browser.profiles.enabled");
|
|
Services.prefs.clearUserPref("browser.profiles.sync.allow-danger-merge");
|
|
}
|
|
);
|
|
|
|
add_task(async function test_current_profile_is_correctly_skipped() {
|
|
// Define two profiles.
|
|
const fakeProfiles = [
|
|
{ name: "Profile1", path: PathUtils.join(PathUtils.tempDir, "profile1") },
|
|
{ name: "Profile2", path: PathUtils.join(PathUtils.tempDir, "profile2") },
|
|
];
|
|
|
|
// Fake signedInUser.json content for each profile.
|
|
// Profile1 (the current profile) is signed in with user@example.com.
|
|
// Profile2 is signed in with other@example.com.
|
|
const fakeSignedInUsers = {
|
|
[PathUtils.join(PathUtils.tempDir, "profile1", "signedInUser.json")]: {
|
|
accountData: { email: "user@example.com" },
|
|
version: 1,
|
|
},
|
|
[PathUtils.join(PathUtils.tempDir, "profile2", "signedInUser.json")]: {
|
|
accountData: { email: "other@example.com" },
|
|
version: 1,
|
|
},
|
|
};
|
|
|
|
// Create an instance of the FxAccountsWebChannelHelpers.
|
|
let channel = new FxAccountsWebChannelHelpers();
|
|
|
|
// Override the methods to return our fake data.
|
|
channel._getAllProfiles = async () => fakeProfiles;
|
|
channel._getCurrentProfileName = () => "Profile1";
|
|
channel._readJSONFileAsync = async filePath =>
|
|
fakeSignedInUsers[filePath] || null;
|
|
|
|
// Case 1: The account email is in the current profile.
|
|
let associatedProfile =
|
|
await channel._getProfileAssociatedWithAcct("user@example.com");
|
|
Assert.equal(
|
|
associatedProfile,
|
|
null,
|
|
"Should not return the current profile."
|
|
);
|
|
|
|
// Case 2: The account email is in a different profile.
|
|
associatedProfile =
|
|
await channel._getProfileAssociatedWithAcct("other@example.com");
|
|
Assert.ok(
|
|
associatedProfile,
|
|
"Should return a profile when account email is in another profile."
|
|
);
|
|
Assert.equal(
|
|
associatedProfile.name,
|
|
"Profile2",
|
|
"Returned profile should be 'Profile2'."
|
|
);
|
|
});
|