/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const { FirefoxLabs } = ChromeUtils.importESModule(
"resource://nimbus/FirefoxLabs.sys.mjs"
);
function setupTest({ ...ctx }) {
return NimbusTestUtils.setupTest({ ...ctx, clearTelemetry:
true });
}
add_setup(
function () {
Services.fog.initializeFOG();
});
add_task(async
function test_all() {
// This recipe conflicts with `feature-conflict`, defined below.
const preexisting = NimbusTestUtils.factories.recipe.withFeatureConfig(
"preexisting-rollout",
{ featureId:
"nimbus-qa-1" },
{ isRollout:
true }
);
const alreadyEnrolled = NimbusTestUtils.factories.recipe.withFeatureConfig(
"already-enrolled-opt-in",
{ featureId:
"nimbus-qa-2" },
{
isFirefoxLabsOptIn:
true,
isRollout:
true,
}
);
const preexistingPaused = NimbusTestUtils.factories.recipe.withFeatureConfig(
"preexisting-paused",
{ featureId:
"no-feature-firefox-desktop" },
{
isEnrollmentPaused:
true,
isFirefoxLabsOptIn:
true,
isRollout:
true,
}
);
const { cleanup } = await setupTest({
init:
false,
storePath: await NimbusTestUtils.createStoreWith(async store => {
await NimbusTestUtils.addEnrollmentForRecipe(preexisting, { store });
await NimbusTestUtils.addEnrollmentForRecipe(alreadyEnrolled, { store });
await NimbusTestUtils.addEnrollmentForRecipe(preexistingPaused, {
store,
});
}),
experiments: [
NimbusTestUtils.factories.recipe(
"opt-in-rollout", {
isFirefoxLabsOptIn:
true,
isRollout:
true,
}),
{
...NimbusTestUtils.factories.recipe(
"opt-in-experiment", {
branches: [
{
...NimbusTestUtils.factories.recipe.branches[
0],
firefoxLabsTitle:
"title",
},
],
}),
isFirefoxLabsOptIn:
true,
firefoxLabsTitle:
"title",
firefoxLabsDescription:
"description",
firefoxLabsDescriptionLinks:
null,
firefoxLabsGroup:
"group",
requiresRestart:
false,
},
NimbusTestUtils.factories.recipe(
"targeting-fail", {
isFirefoxLabsOptIn:
true,
isRollout:
true,
targeting:
"false",
}),
NimbusTestUtils.factories.recipe(
"bucketing-fail", {
bucketConfig: {
...NimbusTestUtils.factories.recipe.bucketConfig,
count:
0,
},
isFirefoxLabsOptIn:
true,
isRollout:
true,
}),
NimbusTestUtils.factories.recipe.withFeatureConfig(
"feature-does-not-exist",
{ featureId:
"bogus" },
{
isFirefoxLabsOptIn:
true,
isRollout:
true,
}
),
NimbusTestUtils.factories.recipe.withFeatureConfig(
"feature-conflict",
{ featureId:
"nimbus-qa-1" },
{ isRollout:
true }
),
NimbusTestUtils.factories.recipe.withFeatureConfig(
"experiment", {
featureId:
"no-feature-firefox-desktop",
}),
NimbusTestUtils.factories.recipe.withFeatureConfig(
"rollout",
{ featureId:
"no-feature-firefox-desktop" },
{ isRollout:
true }
),
NimbusTestUtils.factories.recipe.withFeatureConfig(
"paused",
{ featureId:
"no-feature-firefox-desktop" },
{
isEnrollmentPaused:
true,
isFirefoxLabsOptIn:
true,
isRollout:
true,
}
),
preexisting,
// Prevent unenrollment.
preexistingPaused,
alreadyEnrolled,
],
migrationState: NimbusTestUtils.migrationState.LATEST,
});
await ExperimentAPI.init();
const labs = await FirefoxLabs.create();
const availableSlugs = Array.from(labs.all(), recipe => recipe.slug).sort();
Assert.deepEqual(
availableSlugs,
[
"opt-in-rollout",
"opt-in-experiment",
"already-enrolled-opt-in",
"preexisting-paused",
].sort(),
"Should return all opt in recipes that match targeting and bucketing"
);
await NimbusTestUtils.cleanupManager([
"already-enrolled-opt-in",
"experiment",
"rollout",
"preexisting-rollout",
"preexisting-paused",
]);
await cleanup();
});
add_task(async
function test_enroll() {
const recipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
"opt-in",
{ featureId:
"nimbus-qa-1" },
{
isRollout:
true,
isFirefoxLabsOptIn:
true,
}
);
const { sandbox, manager, cleanup } = await setupTest({
experiments: [recipe],
init:
false,
});
const enrollSpy = sandbox.spy(manager,
"enroll");
await ExperimentAPI.init();
const labs = await FirefoxLabs.create();
await
Assert.rejects(
labs.enroll(),
/enroll: slug and branchSlug are required/,
"Should throw when enroll() is called without a slug"
);
await
Assert.rejects(
labs.enroll(
"opt-in"),
/enroll: slug and branchSlug are required/,
"Should throw when enroll() is called without a branch slug"
);
await labs.enroll(
"bogus",
"bogus");
Assert.ok(
enrollSpy.notCalled,
"ExperimentManager.enroll not called for unknown recipe"
);
await labs.enroll(recipe.slug,
"bogus");
Assert.ok(
enrollSpy.notCalled,
"ExperimentManager.enroll not called for invalid branch slug"
);
await labs.enroll(recipe.slug, recipe.branches[
0].slug);
Assert.ok(
enrollSpy.calledOnceWith(recipe,
"rs-loader", { branchSlug:
"control" }),
"ExperimentManager.enroll called"
);
Assert.deepEqual(
Glean.nimbusEvents.enrollmentStatus
.testGetValue(
"nimbus-targeting-context")
?.map(ev => ev.extra),
[
{
slug: recipe.slug,
branch:
"control",
status:
"Enrolled",
reason:
"OptIn",
},
]
);
Assert.ok(manager.store.get(recipe.slug)?.active,
"Active enrollment exists");
labs.unenroll(recipe.slug);
await cleanup();
});
add_task(async
function test_reenroll() {
const recipe = NimbusTestUtils.factories.recipe(
"opt-in", {
isFirefoxLabsOptIn:
true,
isRollout:
true,
});
const { manager, cleanup } = await setupTest({ experiments: [recipe] });
const labs = await FirefoxLabs.create();
Assert.strictEqual(
typeof manager.store.get(recipe.slug),
"undefined",
`No enrollment
for ${recipe.slug}`
);
await labs.enroll(recipe.slug,
"control");
Assert.ok(
manager.store.get(recipe.slug)?.active,
`Active enrollment
for ${recipe.slug}`
);
labs.unenroll(recipe.slug);
Assert.strictEqual(
manager.store.get(recipe.slug)?.active,
false,
`Inactive enrollment
for ${recipe.slug}`
);
await ExperimentAPI._rsLoader.updateRecipes();
Assert.strictEqual(
manager.store.get(recipe.slug)?.active,
false,
`Inactive enrollment
for ${recipe.slug} after updateRecipes()`
);
await labs.enroll(recipe.slug,
"control");
Assert.ok(
manager.store.get(recipe.slug)?.active,
`Active enrollment
for ${recipe.slug}`
);
labs.unenroll(recipe.slug);
await cleanup();
});
add_task(async
function test_unenroll() {
const { manager, cleanup } = await setupTest({
experiments: [
NimbusTestUtils.factories.recipe.withFeatureConfig(
"rollout",
{ featureId:
"nimbus-qa-1" },
{ isRollout:
true }
),
NimbusTestUtils.factories.recipe.withFeatureConfig(
"opt-in",
{ featureId:
"nimbus-qa-2" },
{
isRollout:
true,
isFirefoxLabsOptIn:
true,
}
),
],
});
const labs = await FirefoxLabs.create();
Assert.ok(manager.store.get(
"rollout")?.active,
"Enrolled in rollout");
Assert.strictEqual(
typeof manager.store.get(
"opt-in"),
"undefined",
"Did not enroll in rollout"
);
await labs.enroll(
"opt-in",
"control");
Assert.ok(manager.store.get(
"opt-in")?.active,
"Enrolled in opt-in");
// Should not throw.
labs.unenroll(
"bogus");
// Should not throw.
labs.unenroll(
"rollout");
Assert.ok(
manager.store.get(
"rollout").active,
"Enrolled in rollout after attempting to unenroll with incorrect API"
);
labs.unenroll(
"opt-in");
Assert.ok(!manager.store.get(
"opt-in").active,
"Unenrolled from opt-in");
// Should not throw.
labs.unenroll(
"opt-in");
Assert.deepEqual(
Glean.nimbusEvents.enrollmentStatus
.testGetValue(
"nimbus-targeting-context")
?.map(ev => ev.extra),
[
{
status:
"Enrolled",
slug:
"opt-in",
reason:
"OptIn",
branch:
"control",
},
{
branch:
"control",
reason:
"OptOut",
slug:
"opt-in",
status:
"Disqualified",
},
]
);
manager.unenroll(
"rollout");
await cleanup();
});
add_task(async
function test_reenroll_quickly() {
const { cleanup } = await setupTest({
experiments: [
NimbusTestUtils.factories.recipe.withFeatureConfig(
"optin",
{ featureId:
"nimbus-qa-2" },
{
isRollout:
true,
isFirefoxLabsOptIn:
true,
}
),
],
});
const labs = await FirefoxLabs.create();
Assert.equal(
await NimbusTestUtils.queryEnrollment(
"optin"),
null,
"Enrollment does not exist"
);
info(
"Enrolling in optin");
await labs.enroll(
"optin",
"control");
await NimbusTestUtils.flushStore();
{
const enrollment = await NimbusTestUtils.queryEnrollment(
"optin");
Assert.ok(enrollment,
"Enrollment exists in database");
Assert.ok(enrollment.active,
"Enrollment is active");
}
info(
"Unenrolling and re-enrolling");
labs.unenroll(
"optin");
await labs.enroll(
"optin",
"control");
await NimbusTestUtils.flushStore();
{
const enrollment = await NimbusTestUtils.queryEnrollment(
"optin");
Assert.ok(enrollment,
"Enrollment exists in database");
Assert.ok(enrollment.active,
"Enrollment is active");
}
labs.unenroll(
"optin");
await cleanup();
});