fune/toolkit/components/messaging-system/test/unit/test_ExperimentAPI.js

280 lines
7 KiB
JavaScript

"use strict";
const { ExperimentAPI } = ChromeUtils.import(
"resource://messaging-system/experiments/ExperimentAPI.jsm"
);
const { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/MSTestUtils.jsm"
);
const { FileTestUtils } = ChromeUtils.import(
"resource://testing-common/FileTestUtils.jsm"
);
const { TestUtils } = ChromeUtils.import(
"resource://testing-common/TestUtils.jsm"
);
/**
* #getExperiment
*/
add_task(async function test_getExperiment_slug() {
const sandbox = sinon.createSandbox();
const manager = ExperimentFakes.manager();
const expected = ExperimentFakes.experiment("foo");
await manager.onStartup();
sandbox.stub(ExperimentAPI, "_store").get(() => ExperimentFakes.childStore());
manager.store.addExperiment(expected);
// Wait to sync to child
await TestUtils.waitForCondition(
() => ExperimentAPI.getExperiment({ slug: "foo" }),
"Wait for child to sync"
);
Assert.deepEqual(
ExperimentAPI.getExperiment({ slug: "foo" }),
expected,
"should return an experiment by slug"
);
sandbox.restore();
});
add_task(async function test_getExperiment_group() {
const sandbox = sinon.createSandbox();
const manager = ExperimentFakes.manager();
const expected = ExperimentFakes.experiment("foo", {
branch: { slug: "treatment", value: { title: "hi" }, groups: ["blue"] },
});
await manager.onStartup();
sandbox.stub(ExperimentAPI, "_store").get(() => ExperimentFakes.childStore());
manager.store.addExperiment(expected);
// Wait to sync to child
await TestUtils.waitForCondition(
() => ExperimentAPI.getExperiment({ group: "blue" }),
"Wait for child to sync"
);
Assert.deepEqual(
ExperimentAPI.getExperiment({ group: "blue" }),
expected,
"should return an experiment by slug"
);
sandbox.restore();
});
/**
* #getValue
*/
add_task(async function test_getValue() {
const sandbox = sinon.createSandbox();
const manager = ExperimentFakes.manager();
const value = { title: "hi" };
const expected = ExperimentFakes.experiment("foo", {
branch: { slug: "treatment", value },
});
await manager.onStartup();
sandbox.stub(ExperimentAPI, "_store").get(() => ExperimentFakes.childStore());
manager.store.addExperiment(expected);
await TestUtils.waitForCondition(
() => ExperimentAPI.getExperiment({ slug: "foo" }),
"Wait for child to sync"
);
Assert.deepEqual(
ExperimentAPI.getValue({ slug: "foo" }),
value,
"should return an experiment value by slug"
);
Assert.equal(
ExperimentAPI.getValue({ slug: "doesnotexist" }),
undefined,
"should return undefined if the experiment is not found"
);
sandbox.restore();
});
/**
* #getRecipe
*/
add_task(async function test_getRecipe() {
const sandbox = sinon.createSandbox();
const RECIPE = {
arguments: ExperimentFakes.recipe("foo"),
};
sandbox.stub(ExperimentAPI._remoteSettingsClient, "get").resolves([RECIPE]);
const recipe = await ExperimentAPI.getRecipe("foo");
Assert.deepEqual(
recipe,
RECIPE,
"should return an experiment recipe if found"
);
sandbox.restore();
});
add_task(async function test_getRecipe_Failure() {
const sandbox = sinon.createSandbox();
sandbox.stub(ExperimentAPI._remoteSettingsClient, "get").throws();
const recipe = await ExperimentAPI.getRecipe("foo");
Assert.equal(recipe, undefined, "should return undefined if RS throws");
sandbox.restore();
});
/**
* #getAllBranches
*/
add_task(async function test_getAllBranches() {
const sandbox = sinon.createSandbox();
const RECIPE = {
arguments: ExperimentFakes.recipe("foo"),
};
sandbox.stub(ExperimentAPI._remoteSettingsClient, "get").resolves([RECIPE]);
const branches = await ExperimentAPI.getAllBranches("foo");
Assert.deepEqual(
branches,
RECIPE.arguments.branches,
"should return all branches if found a recipe"
);
sandbox.restore();
});
add_task(async function test_getAllBranches_Failure() {
const sandbox = sinon.createSandbox();
sandbox.stub(ExperimentAPI._remoteSettingsClient, "get").throws();
const branches = await ExperimentAPI.getAllBranches("foo");
Assert.equal(branches, undefined, "should return undefined if RS throws");
sandbox.restore();
});
/**
* #on
* #off
*/
add_task(async function test_event_updates_content() {
const sandbox = sinon.createSandbox();
const manager = ExperimentFakes.manager();
const expected = ExperimentFakes.experiment("foo");
const updateEventCbStub = sandbox.stub();
// Setup ExperimentManager and child store for ExperimentAPI
await manager.onStartup();
sandbox.stub(ExperimentAPI, "_store").get(() => ExperimentFakes.childStore());
// Set update cb
ExperimentAPI.on("update:foo", updateEventCbStub);
// Add some data
manager.store.addExperiment(expected);
// Wait to sync
await TestUtils.waitForCondition(
() => ExperimentAPI.getExperiment({ slug: "foo" }),
"Wait for child to sync"
);
let baselineCallCount = updateEventCbStub.callCount;
// Trigger an update
manager.store.updateExperiment("foo", { active: false });
// Wait for update to child store
await TestUtils.waitForCondition(
() => updateEventCbStub.callCount === baselineCallCount + 1,
"An `update` event was not sent"
);
// Remove the update listener
ExperimentAPI.off("update:foo", updateEventCbStub);
// Trigger another change
manager.store.updateExperiment("foo", { active: true });
const [, cbExperimentValue] = updateEventCbStub.firstCall.args;
Assert.deepEqual(
expected.slug,
cbExperimentValue.slug,
"should return the updated experiment"
);
Assert.equal(
updateEventCbStub.callCount,
baselineCallCount + 1,
"Should only have seen 1 update"
);
sandbox.restore();
});
/**
* #on
* #off
*/
add_task(async function test_event_updates_main() {
const sandbox = sinon.createSandbox();
const manager = ExperimentFakes.manager();
const expected = ExperimentFakes.experiment("foo");
const updateEventCbStub = sandbox.stub();
// Setup ExperimentManager and child store for ExperimentAPI
await manager.onStartup();
sandbox.stub(ExperimentAPI, "_store").get(() => manager.store);
// Set update cb
ExperimentAPI.on("update:foo", updateEventCbStub);
// Add some data
manager.store.addExperiment(expected);
let baselineCallCount = updateEventCbStub.callCount;
// Trigger an update
manager.store.updateExperiment("foo", { active: false });
// Wait for update to child store
await TestUtils.waitForCondition(
() => updateEventCbStub.callCount === baselineCallCount + 1,
"An `update` event was not sent"
);
// Remove the update listener
ExperimentAPI.off("update:foo", updateEventCbStub);
// Trigger another change
manager.store.updateExperiment("foo", { active: true });
const [, cbExperimentValue] = updateEventCbStub.firstCall.args;
Assert.deepEqual(
expected.slug,
cbExperimentValue.slug,
"should return the updated experiment"
);
Assert.equal(
updateEventCbStub.callCount,
baselineCallCount + 1,
"Should only have seen 1 update"
);
sandbox.restore();
});