Source code
Revision control
Copy as Markdown
Other Tools
/* Any copyright is dedicated to the Public Domain.
Services.scriptloader.loadSubScript(
this
);
const DEFAULT_LABS_RECIPES = [
NimbusTestUtils.factories.recipe("nimbus-qa-1", {
targeting: "true",
isRollout: true,
isFirefoxLabsOptIn: true,
firefoxLabsTitle: "experimental-features-ime-search",
firefoxLabsDescription: "experimental-features-ime-search-description",
firefoxLabsDescriptionLinks: null,
firefoxLabsGroup: "experimental-features-group-customize-browsing",
requiresRestart: false,
branches: [
{
slug: "control",
ratio: 1,
features: [
{
featureId: "nimbus-qa-1",
value: {
value: "recipe-value-1",
},
},
],
},
],
}),
NimbusTestUtils.factories.recipe("nimbus-qa-2", {
targeting: "true",
isRollout: true,
isFirefoxLabsOptIn: true,
firefoxLabsTitle: "experimental-features-media-jxl",
firefoxLabsDescription: "experimental-features-media-jxl-description",
firefoxLabsDescriptionLinks: {
bugzilla: "https://example.com",
},
firefoxLabsGroup: "experimental-features-group-webpage-display",
branches: [
{
slug: "control",
ratio: 1,
features: [
{
featureId: "nimbus-qa-2",
value: {
value: "recipe-value-2",
},
},
],
},
],
}),
NimbusTestUtils.factories.recipe("targeting-false", {
targeting: "false",
isRollout: true,
isFirefoxLabsOptIn: true,
firefoxLabsTitle: "experimental-features-ime-search",
firefoxLabsDescription: "experimental-features-ime-search-description",
firefoxLabsDescriptionLinks: null,
firefoxLabsGroup: "experimental-features-group-developer-tools",
requiresRestart: false,
}),
NimbusTestUtils.factories.recipe("bucketing-false", {
bucketConfig: {
...NimbusTestUtils.factories.recipe.bucketConfig,
count: 0,
},
isRollout: true,
targeting: "true",
isFirefoxLabsOptIn: true,
firefoxLabsTitle: "experimental-features-ime-search",
firefoxLabsDescription: "experimental-features-ime-search-description",
firefoxLabsDescriptionLinks: null,
firefoxLabsGroup: "experimental-features-group-developer-tools",
requiresRestart: false,
}),
];
async function setupLabsTest(recipes) {
await SpecialPowers.pushPrefEnv({
set: [
["app.normandy.run_interval_seconds", 0],
["app.shield.optoutstudies.enabled", true],
["datareporting.healthreport.uploadEnabled", true],
["messaging-system.log", "debug"],
],
clear: [
["browser.preferences.experimental"],
["browser.preferences.experimental.hidden"],
],
});
// Initialize Nimbus and wait for the RemoteSettingsExperimentLoader to finish
// updating (with no recipes).
await ExperimentAPI.ready();
await ExperimentAPI._rsLoader.finishedUpdating();
// Inject some recipes into the Remote Settings client and call
// updateRecipes() so that we have available opt-ins.
await ExperimentAPI._rsLoader.remoteSettingsClients.experiments.db.importChanges(
{},
Date.now(),
recipes ?? DEFAULT_LABS_RECIPES,
{ clear: true }
);
await ExperimentAPI._rsLoader.remoteSettingsClients.secureExperiments.db.importChanges(
{},
Date.now(),
[],
{ clear: true }
);
await ExperimentAPI._rsLoader.updateRecipes("test");
return async function cleanup() {
await NimbusTestUtils.removeStore(ExperimentAPI.manager.store);
await SpecialPowers.popPrefEnv();
};
}
function promiseNimbusStoreUpdate(wantedSlug, wantedActive) {
const deferred = Promise.withResolvers();
const listener = (_event, { slug, active }) => {
info(
`promiseNimbusStoreUpdate: received update for ${slug} active=${active}`
);
if (slug === wantedSlug && active === wantedActive) {
ExperimentAPI._manager.store.off("update", listener);
deferred.resolve();
}
};
ExperimentAPI._manager.store.on("update", listener);
return deferred.promise;
}
function enrollByClick(el, wantedActive) {
const slug = el.dataset.nimbusSlug;
info(`Enrolling in ${slug}:${el.dataset.nimbusBranchSlug}...`);
const promise = promiseNimbusStoreUpdate(slug, wantedActive);
EventUtils.synthesizeMouseAtCenter(el.inputEl, {}, gBrowser.contentWindow);
return promise;
}
/**
* Clicks a checkbox and waits for the associated preference to change to the expected value.
*
* @param {Document} doc - The content document.
* @param {string} checkboxId - The checkbox element id.
* @param {string} prefName - The preference name.
* @param {boolean} expectedValue - The expected value after click.
* @returns {Promise<HTMLInputElement>}
*/