293 lines
8.3 KiB
JavaScript
293 lines
8.3 KiB
JavaScript
"use strict";
|
|
|
|
const { IndexedDB } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/IndexedDB.sys.mjs"
|
|
);
|
|
|
|
const { NormandyTestUtils } = ChromeUtils.importESModule(
|
|
"resource://testing-common/NormandyTestUtils.sys.mjs"
|
|
);
|
|
const { addonStudyFactory, branchedAddonStudyFactory } =
|
|
NormandyTestUtils.factories;
|
|
|
|
// Initialize test utils
|
|
AddonTestUtils.initMochitest(this);
|
|
|
|
decorate_task(AddonStudies.withStudies(), async function testGetMissing() {
|
|
ok(
|
|
!(await AddonStudies.get("does-not-exist")),
|
|
"get returns null when the requested study does not exist"
|
|
);
|
|
});
|
|
|
|
decorate_task(
|
|
AddonStudies.withStudies([addonStudyFactory({ slug: "test-study" })]),
|
|
async function testGet({ addonStudies: [study] }) {
|
|
const storedStudy = await AddonStudies.get(study.recipeId);
|
|
Assert.deepEqual(study, storedStudy, "get retrieved a study from storage.");
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
AddonStudies.withStudies([addonStudyFactory(), addonStudyFactory()]),
|
|
async function testGetAll({ addonStudies }) {
|
|
const storedStudies = await AddonStudies.getAll();
|
|
Assert.deepEqual(
|
|
new Set(storedStudies),
|
|
new Set(addonStudies),
|
|
"getAll returns every stored study."
|
|
);
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
AddonStudies.withStudies([addonStudyFactory({ slug: "test-study" })]),
|
|
async function testHas({ addonStudies: [study] }) {
|
|
let hasStudy = await AddonStudies.has(study.recipeId);
|
|
ok(hasStudy, "has returns true for a study that exists in storage.");
|
|
|
|
hasStudy = await AddonStudies.has("does-not-exist");
|
|
ok(
|
|
!hasStudy,
|
|
"has returns false for a study that doesn't exist in storage."
|
|
);
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
AddonStudies.withStudies([
|
|
addonStudyFactory({ slug: "test-study1" }),
|
|
addonStudyFactory({ slug: "test-study2" }),
|
|
]),
|
|
async function testClear({ addonStudies: [study1, study2] }) {
|
|
const hasAll =
|
|
(await AddonStudies.has(study1.recipeId)) &&
|
|
(await AddonStudies.has(study2.recipeId));
|
|
ok(hasAll, "Before calling clear, both studies are in storage.");
|
|
|
|
await AddonStudies.clear();
|
|
const hasAny =
|
|
(await AddonStudies.has(study1.recipeId)) ||
|
|
(await AddonStudies.has(study2.recipeId));
|
|
ok(!hasAny, "After calling clear, all studies are removed from storage.");
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
AddonStudies.withStudies([addonStudyFactory({ slug: "foo" })]),
|
|
async function testUpdate({ addonStudies: [study] }) {
|
|
Assert.deepEqual(await AddonStudies.get(study.recipeId), study);
|
|
|
|
const updatedStudy = {
|
|
...study,
|
|
slug: "bar",
|
|
};
|
|
await AddonStudies.update(updatedStudy);
|
|
|
|
Assert.deepEqual(await AddonStudies.get(study.recipeId), updatedStudy);
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
AddonStudies.withStudies([
|
|
addonStudyFactory({
|
|
active: true,
|
|
addonId: "does.not.exist@example.com",
|
|
studyEndDate: null,
|
|
}),
|
|
addonStudyFactory({ active: true, addonId: "installed@example.com" }),
|
|
addonStudyFactory({
|
|
active: false,
|
|
addonId: "already.gone@example.com",
|
|
studyEndDate: new Date(2012, 1),
|
|
}),
|
|
]),
|
|
withSendEventSpy(),
|
|
withInstalledWebExtension(
|
|
{ id: "installed@example.com" },
|
|
{ expectUninstall: true }
|
|
),
|
|
async function testInit({
|
|
addonStudies: [activeUninstalledStudy, activeInstalledStudy, inactiveStudy],
|
|
sendEventSpy,
|
|
installedWebExtension: { addonId },
|
|
}) {
|
|
await AddonStudies.init();
|
|
|
|
const newActiveStudy = await AddonStudies.get(
|
|
activeUninstalledStudy.recipeId
|
|
);
|
|
ok(
|
|
!newActiveStudy.active,
|
|
"init marks studies as inactive if their add-on is not installed."
|
|
);
|
|
ok(
|
|
newActiveStudy.studyEndDate,
|
|
"init sets the study end date if a study's add-on is not installed."
|
|
);
|
|
let events = Services.telemetry.snapshotEvents(
|
|
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
|
false
|
|
);
|
|
events = (events.parent || []).filter(e => e[1] == "normandy");
|
|
Assert.deepEqual(
|
|
events[0].slice(2), // strip timestamp and "normandy"
|
|
[
|
|
"unenroll",
|
|
"addon_study",
|
|
activeUninstalledStudy.slug,
|
|
{
|
|
addonId: activeUninstalledStudy.addonId,
|
|
addonVersion: activeUninstalledStudy.addonVersion,
|
|
reason: "uninstalled-sideload",
|
|
branch: AddonStudies.NO_BRANCHES_MARKER,
|
|
},
|
|
],
|
|
"AddonStudies.init() should send the correct telemetry event"
|
|
);
|
|
|
|
const newInactiveStudy = await AddonStudies.get(inactiveStudy.recipeId);
|
|
is(
|
|
newInactiveStudy.studyEndDate.getFullYear(),
|
|
2012,
|
|
"init does not modify inactive studies."
|
|
);
|
|
|
|
const newActiveInstalledStudy = await AddonStudies.get(
|
|
activeInstalledStudy.recipeId
|
|
);
|
|
Assert.deepEqual(
|
|
activeInstalledStudy,
|
|
newActiveInstalledStudy,
|
|
"init does not modify studies whose add-on is still installed."
|
|
);
|
|
|
|
// Only activeUninstalledStudy should have generated any events
|
|
ok(sendEventSpy.calledOnce, "no extra events should be generated");
|
|
|
|
// Clean up
|
|
const addon = await AddonManager.getAddonByID(addonId);
|
|
await addon.uninstall();
|
|
await TestUtils.topicObserved("shield-study-ended", (subject, message) => {
|
|
return message === `${activeInstalledStudy.recipeId}`;
|
|
});
|
|
}
|
|
);
|
|
|
|
// init should register telemetry experiments
|
|
decorate_task(
|
|
AddonStudies.withStudies([
|
|
branchedAddonStudyFactory({
|
|
active: true,
|
|
addonId: "installed1@example.com",
|
|
}),
|
|
branchedAddonStudyFactory({
|
|
active: true,
|
|
addonId: "installed2@example.com",
|
|
}),
|
|
]),
|
|
withInstalledWebExtensionSafe({ id: "installed1@example.com" }),
|
|
withInstalledWebExtension({ id: "installed2@example.com" }),
|
|
withStub(TelemetryEnvironment, "setExperimentActive"),
|
|
async function testInit({ addonStudies, setExperimentActiveStub }) {
|
|
await AddonStudies.init();
|
|
Assert.deepEqual(
|
|
setExperimentActiveStub.args,
|
|
[
|
|
[
|
|
addonStudies[0].slug,
|
|
addonStudies[0].branch,
|
|
{
|
|
type: "normandy-addonstudy",
|
|
},
|
|
],
|
|
[
|
|
addonStudies[1].slug,
|
|
addonStudies[1].branch,
|
|
{
|
|
type: "normandy-addonstudy",
|
|
},
|
|
],
|
|
],
|
|
"Add-on studies are registered in Telemetry by AddonStudies.init"
|
|
);
|
|
}
|
|
);
|
|
|
|
// Test that AddonStudies.init() ends studies that have been uninstalled
|
|
decorate_task(
|
|
AddonStudies.withStudies([
|
|
addonStudyFactory({
|
|
active: true,
|
|
addonId: "installed@example.com",
|
|
studyEndDate: null,
|
|
}),
|
|
]),
|
|
withInstalledWebExtension(
|
|
{ id: "installed@example.com" },
|
|
{ expectUninstall: true }
|
|
),
|
|
async function testInit({
|
|
addonStudies: [study],
|
|
installedWebExtension: { addonId },
|
|
}) {
|
|
const addon = await AddonManager.getAddonByID(addonId);
|
|
await addon.uninstall();
|
|
await TestUtils.topicObserved("shield-study-ended", (subject, message) => {
|
|
return message === `${study.recipeId}`;
|
|
});
|
|
|
|
const newStudy = await AddonStudies.get(study.recipeId);
|
|
ok(
|
|
!newStudy.active,
|
|
"Studies are marked as inactive when their add-on is uninstalled."
|
|
);
|
|
ok(
|
|
newStudy.studyEndDate,
|
|
"The study end date is set when the add-on for the study is uninstalled."
|
|
);
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
AddonStudies.withStudies([
|
|
NormandyTestUtils.factories.addonStudyFactory({ active: true }),
|
|
NormandyTestUtils.factories.branchedAddonStudyFactory(),
|
|
]),
|
|
async function testRemoveOldAddonStudies({
|
|
addonStudies: [noBranchStudy, branchedStudy],
|
|
}) {
|
|
// pre check, both studies are active
|
|
const preActiveIds = (await AddonStudies.getAllActive()).map(
|
|
addon => addon.recipeId
|
|
);
|
|
Assert.deepEqual(
|
|
preActiveIds,
|
|
[noBranchStudy.recipeId, branchedStudy.recipeId],
|
|
"Both studies should be active"
|
|
);
|
|
|
|
// run the migration
|
|
await AddonStudies.migrations.migration02RemoveOldAddonStudyAction();
|
|
|
|
// The unbrached study should end
|
|
const postActiveIds = (await AddonStudies.getAllActive()).map(
|
|
addon => addon.recipeId
|
|
);
|
|
Assert.deepEqual(
|
|
postActiveIds,
|
|
[branchedStudy.recipeId],
|
|
"The unbranched study should end"
|
|
);
|
|
|
|
// But both studies should still be present
|
|
const postAllIds = (await AddonStudies.getAll()).map(
|
|
addon => addon.recipeId
|
|
);
|
|
Assert.deepEqual(
|
|
postAllIds,
|
|
[noBranchStudy.recipeId, branchedStudy.recipeId],
|
|
"Both studies should still be present"
|
|
);
|
|
}
|
|
);
|