Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
ChromeUtils.defineESModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
TelemetryUtils: "resource://gre/modules/TelemetryUtils.sys.mjs",
});
const { Policy, TelemetryReportingPolicy } = ChromeUtils.importESModule(
"resource://gre/modules/TelemetryReportingPolicy.sys.mjs"
);
const { NimbusTestUtils } = ChromeUtils.importESModule(
);
NimbusTestUtils.init(this);
const TOU_ACCEPTED_VERSION_PREF = "termsofuse.acceptedVersion";
const TOU_ACCEPTED_DATE_PREF = "termsofuse.acceptedDate";
const TOU_BYPASS_NOTIFICATION_PREF = "termsofuse.bypassNotification";
const TOU_MINIMUM_VERSION_PREF = "termsofuse.minimumVersion";
const TOU_CURRENT_VERSION_PREF = "termsofuse.currentVersion";
const TOU_ROLLOUT_ENROLLED_PREF =
"browser.preonboarding.enrolledInOnTrainRollout";
const TOU_ROLLOUT_POPULATION_PREF =
"browser.preonboarding.onTrainRolloutPopulation";
const TOU_PREF_MIGRATION_CHECK = "browser.termsofuse.prefMigrationCheck";
const TOU_PREONBOARDING_ENABLED_PREF = "browser.preonboarding.enabled";
const RELEASE_DATES = TelemetryReportingPolicy.fullOnTrainReleaseDates;
const GENERIC_TOU_TIMESTAMP = 1745000000000;
const ORIGINAL_CHANNEL = TelemetryUtils.getUpdateChannel();
const DEFAULT_BRANCH = Services.prefs.getDefaultBranch("");
const MIGRATED_TOU_VERSION = 4;
const skipIfNotBrowser = () => ({
skip_if: () => AppConstants.MOZ_BUILD_APP != "browser",
});
function setupLegacyAndRolloutPrefs({
acceptedVersion,
notifiedTime,
minVersion,
currentVersion,
enrolledInRollout,
rolloutPopulation,
}) {
Services.prefs.setIntPref(
TelemetryUtils.Preferences.AcceptedPolicyVersion,
acceptedVersion
);
Services.prefs.setStringPref(
TelemetryUtils.Preferences.AcceptedPolicyDate,
notifiedTime.toString()
);
Services.prefs.setIntPref(
TelemetryUtils.Preferences.MinimumPolicyVersion,
minVersion
);
Services.prefs.setIntPref(
TelemetryUtils.Preferences.CurrentPolicyVersion,
currentVersion
);
if (enrolledInRollout) {
Services.prefs.setBoolPref(TOU_ROLLOUT_ENROLLED_PREF, enrolledInRollout);
}
if (rolloutPopulation) {
Services.prefs.setIntPref(TOU_ROLLOUT_POPULATION_PREF, rolloutPopulation);
}
}
async function checkPrefsAndTelemetryValuesAfterSuccessfulMigration(
expectedAcceptedDate
) {
// We started the default version as 4 to distinguish it from version numbers
// used in the original TOU experiments and rollouts. So, we can expect the
// TOU accepted, minimum, and current version to be the default (4) after
// migration.
const expectedTOUVersion =
TelemetryReportingPolicy.DEFAULT_TERMS_OF_USE_POLICY_VERSION;
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF),
expectedAcceptedDate.toString(),
"TOU acceptedDate matches expected"
);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF),
expectedTOUVersion,
"TOU acceptedVersion matches expected"
);
Assert.equal(
Services.prefs.getIntPref(TOU_MINIMUM_VERSION_PREF),
expectedTOUVersion,
"TOU minimumVersion matches expected"
);
Assert.equal(
Services.prefs.getIntPref(TOU_CURRENT_VERSION_PREF),
expectedTOUVersion,
"TOU currentVersion matches expected"
);
Assert.ok(
Services.prefs.getBoolPref(TOU_PREF_MIGRATION_CHECK, false),
"migration check pref should be true after migration"
);
Assert.equal(
Services.prefs.getIntPref(
TelemetryUtils.Preferences.AcceptedPolicyVersion,
0
),
0,
"Legacy datareporting.acceptedVersion cleared"
);
Assert.equal(
Services.prefs.getStringPref(
TelemetryUtils.Preferences.AcceptedPolicyDate,
"0"
),
"0",
"Legacy datareporting.acceptedDate cleared"
);
Assert.ok(
!Services.prefs.prefHasUserValue(
TelemetryUtils.Preferences.CurrentPolicyVersion
),
"Legacy current policy version should be cleared"
);
Assert.ok(
!Services.prefs.prefHasUserValue(
TelemetryUtils.Preferences.MinimumPolicyVersion
),
"Legacy minimum policy version should be cleared"
);
Assert.equal(
Services.prefs.getBoolPref(TOU_PREF_MIGRATION_CHECK, false),
true,
"Pref migration complete pref set to true"
);
const metricVersion = await Glean.termsofuse.version.testGetValue();
Assert.equal(
metricVersion,
MIGRATED_TOU_VERSION,
`Glean.termsofuse.version is ${metricVersion} and matches expected ${MIGRATED_TOU_VERSION}`
);
const rawMetricDate = await Glean.termsofuse.date.testGetValue();
// Compare only seconds
const metricSec = getDateInSeconds(rawMetricDate);
const expectedSec = getDateInSeconds(expectedAcceptedDate);
Assert.equal(
metricSec,
expectedSec,
`Glean.termsofuse.date (in seconds) is ${metricSec} and matches expected ${expectedSec}`
);
}
async function resetState() {
const legacyDataReportingPrefs = [
TelemetryUtils.Preferences.AcceptedPolicyVersion,
TelemetryUtils.Preferences.AcceptedPolicyDate,
TelemetryUtils.Preferences.MinimumPolicyVersion,
TelemetryUtils.Preferences.CurrentPolicyVersion,
TelemetryUtils.Preferences.BypassNotification,
];
const TOUPrefs = [
TOU_ACCEPTED_VERSION_PREF,
TOU_ACCEPTED_DATE_PREF,
TOU_BYPASS_NOTIFICATION_PREF,
TOU_MINIMUM_VERSION_PREF,
TOU_CURRENT_VERSION_PREF,
TOU_PREF_MIGRATION_CHECK,
];
const rolloutPrefs = [TOU_ROLLOUT_ENROLLED_PREF, TOU_ROLLOUT_POPULATION_PREF];
const prefsToClear = legacyDataReportingPrefs.concat(TOUPrefs, rolloutPrefs);
prefsToClear.forEach(pref => Services.prefs.clearUserPref(pref));
Assert.ok(
true,
`app.update.channel before being cleared - ${Services.prefs.getDefaultBranch("").getStringPref("app.update.channel")}`
);
// undo channel override
maybeUnlockAppUpdateChannelPref();
DEFAULT_BRANCH.setStringPref("app.update.channel", ORIGINAL_CHANNEL);
Assert.equal(
TelemetryUtils.getUpdateChannel(),
ORIGINAL_CHANNEL,
`update channel is reverted to original after being cleared ${TelemetryUtils.getUpdateChannel()} ${ORIGINAL_CHANNEL}`
);
}
async function runMigrationFlow() {
TelemetryReportingPolicy.reset();
await Policy.fakeSessionRestoreNotification();
}
add_setup(async function test_setup() {
// No bypass by default
Services.prefs.setBoolPref(
TelemetryUtils.Preferences.BypassNotification,
false
);
Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false);
do_get_profile();
Services.fog.initializeFOG();
TelemetryReportingPolicy.setup();
});
registerCleanupFunction(() => {
Services.prefs.clearUserPref(TelemetryUtils.Preferences.BypassNotification);
Services.prefs.clearUserPref(TOU_BYPASS_NOTIFICATION_PREF);
Services.prefs.clearUserPref(TOU_PREONBOARDING_ENABLED_PREF);
});
add_task(
skipIfNotBrowser(),
async function test_tou_pref_migration_for_user_who_accepted_in_nimbus_experiment() {
const timestamp = GENERIC_TOU_TIMESTAMP;
setupLegacyAndRolloutPrefs({
acceptedVersion: 3,
notifiedTime: timestamp,
minVersion: 3,
currentVersion: 3,
});
await runMigrationFlow();
await checkPrefsAndTelemetryValuesAfterSuccessfulMigration(timestamp);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF),
TelemetryReportingPolicy.DEFAULT_TERMS_OF_USE_POLICY_VERSION,
"Nimbus experiment - migrated TOU version"
);
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF),
timestamp.toString(),
"Nimbus experiment - migrated TOU date"
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_tou_pref_migration_for_user_who_accepted_during_partial_rollout_per_channel() {
const channels = ["nightly", "beta", "release"];
for (let channel of channels) {
const timestamp = parseInt(RELEASE_DATES[channel], 10) + 1;
setupLegacyAndRolloutPrefs({
acceptedVersion: 3,
notifiedTime: timestamp,
minVersion: 1,
currentVersion: 2,
enrolledInRollout: true,
});
maybeUnlockAppUpdateChannelPref();
DEFAULT_BRANCH.setStringPref("app.update.channel", channel);
Assert.equal(
TelemetryUtils.getUpdateChannel(),
channel,
`getUpdateChannel should reflect override to ${channel}`
);
await runMigrationFlow();
await checkPrefsAndTelemetryValuesAfterSuccessfulMigration(timestamp);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF),
TelemetryReportingPolicy.DEFAULT_TERMS_OF_USE_POLICY_VERSION,
`${channel} partial on-train rollout - migrated TOU version`
);
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF),
timestamp.toString(),
`${channel} partial on-train rollout - migrated TOU date`
);
await resetState();
}
}
);
add_task(
skipIfNotBrowser(),
async function test_tou_pref_migration_for_user_who_accepted_during_full_rollout_nightly() {
if (AppConstants.platform === "linux") {
info(
"Skipping full rollout TOU migration test on Linux. TOU flow was disabled by default for this OS during the full rollout."
);
return;
}
const timestamp = parseInt(RELEASE_DATES.nightly, 10) + 1;
setupLegacyAndRolloutPrefs({
acceptedVersion: 2,
notifiedTime: timestamp,
minVersion: 1,
currentVersion: 2,
enrolledInRollout: true,
});
maybeUnlockAppUpdateChannelPref();
DEFAULT_BRANCH.setStringPref("app.update.channel", "nightly");
Assert.equal(
TelemetryUtils.getUpdateChannel(),
"nightly",
"getUpdateChannel should reflect our override"
);
await runMigrationFlow();
await checkPrefsAndTelemetryValuesAfterSuccessfulMigration(timestamp);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF),
TelemetryReportingPolicy.DEFAULT_TERMS_OF_USE_POLICY_VERSION,
"Nightly full rollout - migrated TOU version"
);
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF),
timestamp.toString(),
"Nightly full rollout - migrated TOU date"
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_tou_pref_migration_for_user_who_accepted_during_full_rollout_beta() {
if (AppConstants.platform === "linux") {
info(
"Skipping full rollout TOU migration test on Linux. TOU flow was disabled by default for this OS during the full rollout."
);
return;
}
const timestamp = parseInt(RELEASE_DATES.beta, 10) + 1;
setupLegacyAndRolloutPrefs({
acceptedVersion: 2,
notifiedTime: timestamp,
minVersion: 1,
currentVersion: 2,
enrolledInRollout: true,
});
maybeUnlockAppUpdateChannelPref();
DEFAULT_BRANCH.setStringPref("app.update.channel", "beta");
Assert.equal(
TelemetryUtils.getUpdateChannel(),
"beta",
"getUpdateChannel should reflect our override"
);
await runMigrationFlow();
await checkPrefsAndTelemetryValuesAfterSuccessfulMigration(timestamp);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF),
TelemetryReportingPolicy.DEFAULT_TERMS_OF_USE_POLICY_VERSION,
"Beta full rollout - migrated TOU version"
);
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF),
timestamp.toString(),
"Beta full rollout - migrated TOU date"
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_tou_pref_migration_for_user_who_accepted_during_full_rollout_release() {
if (AppConstants.platform === "linux") {
info(
"Skipping full rollout TOU migration test on Linux. TOU flow was disabled by default for this OS during the full rollout."
);
return;
}
const timestamp = parseInt(RELEASE_DATES.release, 10) + 1;
setupLegacyAndRolloutPrefs({
acceptedVersion: 2,
notifiedTime: timestamp,
minVersion: 1,
currentVersion: 2,
enrolledInRollout: true,
});
maybeUnlockAppUpdateChannelPref();
DEFAULT_BRANCH.setStringPref("app.update.channel", "release");
Assert.equal(
TelemetryUtils.getUpdateChannel(),
"release",
"getUpdateChannel should reflect our override"
);
await runMigrationFlow();
await checkPrefsAndTelemetryValuesAfterSuccessfulMigration(timestamp);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF),
TelemetryReportingPolicy.DEFAULT_TERMS_OF_USE_POLICY_VERSION,
"Release full rollout - migrated TOU version"
);
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF),
timestamp.toString(),
"Release full rollout - migrated TOU date"
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function skip_both_tou_and_legacy_datareporting_flows_if_both_bypass_prefs_are_set() {
Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, true);
Services.prefs.setBoolPref(
TelemetryUtils.Preferences.BypassNotification,
true
);
Assert.ok(
!TelemetryReportingPolicy.testShouldNotify(),
"Legacy flow and TOU flow skipped when both bypass prefs are set"
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_no_migration_for_users_who_accepted_during_full_on_train_rollout_when_preonboarding_disabled() {
const timestamp = parseInt(RELEASE_DATES.nightly, 10) + 1;
setupLegacyAndRolloutPrefs({
acceptedVersion: 2,
notifiedTime: timestamp,
minVersion: 1,
currentVersion: 2,
enrolledInRollout: true,
});
maybeUnlockAppUpdateChannelPref();
DEFAULT_BRANCH.setStringPref("app.update.channel", "nightly");
Services.prefs.setBoolPref(TOU_PREONBOARDING_ENABLED_PREF, false);
await runMigrationFlow();
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF, "0"),
"0",
"When preonboarding disabled, acceptedDate stays unset"
);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF, 0),
0,
"When preonboarding disabled, acceptedVersion stays unset"
);
await resetState();
Services.prefs.clearUserPref(TOU_PREONBOARDING_ENABLED_PREF);
}
);
add_task(
skipIfNotBrowser(),
async function test_experiment_acceptance_ignores_preonboarding_disabled() {
// Experiment and partial rollout users (where accepted version is 3) should
// still migrate even if TOU flow disabled
const timestamp = GENERIC_TOU_TIMESTAMP;
setupLegacyAndRolloutPrefs({
acceptedVersion: 3,
notifiedTime: timestamp,
minVersion: 3,
currentVersion: 3,
enrolledInRollout: true,
});
Services.prefs.setBoolPref(TOU_PREONBOARDING_ENABLED_PREF, false);
await runMigrationFlow();
await checkPrefsAndTelemetryValuesAfterSuccessfulMigration(timestamp);
await resetState();
Services.prefs.clearUserPref(TOU_PREONBOARDING_ENABLED_PREF);
}
);
add_task(
skipIfNotBrowser(),
async function test_tou_bypass_pref_migrates_from_legacy_datareporting() {
Services.prefs.clearUserPref(TelemetryUtils.Preferences.BypassNotification);
Services.prefs.clearUserPref(TOU_BYPASS_NOTIFICATION_PREF);
Services.prefs.setBoolPref(
TelemetryUtils.Preferences.BypassNotification,
true
);
Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false);
// Simulate that user accepted as part of an on-train rollout
setupLegacyAndRolloutPrefs({
acceptedVersion: 3,
notifiedTime: GENERIC_TOU_TIMESTAMP,
minVersion: 3,
currentVersion: 3,
enrolledInRollout: true,
rolloutPopulation: 1000,
});
Assert.equal(
Services.prefs.getBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false),
false,
"termsofuse.bypassNotification is false before migration check"
);
await runMigrationFlow();
Assert.ok(
Services.prefs.getBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false),
"termsofuse.bypassNotification should be updated to true when legacy bypassNotification is true"
);
await checkPrefsAndTelemetryValuesAfterSuccessfulMigration(
GENERIC_TOU_TIMESTAMP
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_tou_bypass_not_set_when_legacy_false() {
Services.prefs.clearUserPref(TelemetryUtils.Preferences.BypassNotification);
Services.prefs.clearUserPref(TOU_BYPASS_NOTIFICATION_PREF);
Services.prefs.setBoolPref(
TelemetryUtils.Preferences.BypassNotification,
false
);
Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false);
// Simulate that user accepted as part of an on-train rollout
setupLegacyAndRolloutPrefs({
acceptedVersion: 3,
notifiedTime: GENERIC_TOU_TIMESTAMP,
minVersion: 3,
currentVersion: 3,
enrolledInRollout: true,
rolloutPopulation: 1000,
});
Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false);
Assert.equal(
Services.prefs.getBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false),
false,
"termsofuse.bypassNotification is false before migration check"
);
await runMigrationFlow();
Assert.equal(
Services.prefs.getBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false),
false,
"termsofuse.bypassNotification must stay false when legacy bypassNotification is false"
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_no_migration_for_users_who_accepted_legacy_flow() {
const timestamp = GENERIC_TOU_TIMESTAMP;
// User accepted via legacy flow
setupLegacyAndRolloutPrefs({
acceptedVersion: 2,
notifiedTime: timestamp,
minVersion: 2,
currentVersion: 2,
});
await runMigrationFlow();
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF, "0"),
"0",
"If user accepted legacy datareporting flow, TOU accepted date should remain unset"
);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF, 0),
0,
"If user accepted legacy datareporting flow, accepted version should remain unset"
);
Assert.ok(
Services.prefs.getBoolPref(TOU_PREF_MIGRATION_CHECK, false),
"If user accepted legacy datareporting flow, migration check pref should be true"
);
Assert.ok(
!TelemetryReportingPolicy.userHasAcceptedTOU(),
"If user accepted legacy datareporting flow, hasUserAcceptedCurrentTOU should be false"
);
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_no_migration_when_TOU_modal_seen_but_not_accepted() {
let showStub = sinon.stub(Policy, "showModal").resolves(false);
maybeUnlockAppUpdateChannelPref();
DEFAULT_BRANCH.setStringPref("app.update.channel", "release");
// User saw TOU modal during an experiment or on-train rollout, but has not
// accepted
setupLegacyAndRolloutPrefs({
acceptedVersion: 0,
notifiedTime: 0,
minVersion: 3,
currentVersion: 3,
});
await runMigrationFlow();
await Policy.delayedSetup();
Assert.equal(
Services.prefs.getStringPref(TOU_ACCEPTED_DATE_PREF, "0"),
"0",
"If user saw TOU, but didn't accept, TOU accepted date should remain unset"
);
Assert.equal(
Services.prefs.getIntPref(TOU_ACCEPTED_VERSION_PREF, 0),
0,
"If user saw TOU, but didn't accept, TOU accepted version should remain unset"
);
Assert.ok(
Services.prefs.getBoolPref(TOU_PREF_MIGRATION_CHECK, false),
"If user saw TOU, but didn't accept, migration check pref should be true"
);
showStub.restore();
await resetState();
}
);
add_task(
skipIfNotBrowser(),
async function test_will_not_notify_via_legacy_flow_after_TOU_accepted_via_migration() {
const timestamp = GENERIC_TOU_TIMESTAMP;
// Before migration, legacy flow would show
Assert.ok(
TelemetryReportingPolicy.testShouldNotify(),
"Legacy data reporting flow would show before migration"
);
// Simulate that user accepted as part of an on-train rollout
setupLegacyAndRolloutPrefs({
acceptedVersion: 3,
notifiedTime: timestamp,
minVersion: 3,
currentVersion: 3,
enrolledInRollout: true,
rolloutPopulation: 1000,
});
await runMigrationFlow();
Assert.ok(
TelemetryReportingPolicy.userHasAcceptedTOU(),
"TOU prefs migrated from legacy rollout"
);
Assert.ok(
!TelemetryReportingPolicy.testShouldNotify(),
"Legacy data reporting flow should not show once TOU is accepted via migration"
);
await resetState();
}
);