Bug 1961629 - Use ExperimentFeature.getEnrollmentMetadata() in browser/components/asrouter r=omc-reviewers,aminomancer

Differential Revision: https://phabricator.services.mozilla.com/D246149
This commit is contained in:
Beth Rennie
2025-04-29 11:28:57 +00:00
parent 10407ee946
commit c8b5015aca
8 changed files with 80 additions and 89 deletions

View File

@@ -356,16 +356,11 @@ export const MessageLoaderUtils = {
let experiments = [];
for (const featureId of featureIds) {
const featureAPI = lazy.NimbusFeatures[featureId];
const experimentData = lazy.ExperimentAPI.getExperimentMetaData({
featureId,
});
const enrollmentData = featureAPI.getEnrollmentMetadata();
// We are not enrolled in any experiment or rollout for this feature, so
// we can skip the feature.
if (
!experimentData &&
!lazy.ExperimentAPI.getRolloutMetaData({ featureId })
) {
if (!enrollmentData) {
continue;
}
@@ -393,7 +388,7 @@ export const MessageLoaderUtils = {
if (
NO_REACH_EVENT_GROUPS.includes(featureId) ||
!MESSAGING_EXPERIMENTS_DEFAULT_FEATURES.includes(featureId) ||
!experimentData
enrollmentData.isRollout
) {
continue;
}
@@ -402,10 +397,10 @@ export const MessageLoaderUtils = {
// if found any. The `forReachEvent` label is used to identify those
// branches so that they would only be used to record the Reach event.
const branches =
(await lazy.ExperimentAPI.getAllBranches(experimentData.slug)) || [];
(await lazy.ExperimentAPI.getAllBranches(enrollmentData.slug)) || [];
for (const branch of branches) {
let branchValue = branch[featureId].value;
if (!branchValue || branch.slug === experimentData.branch.slug) {
if (!branchValue || branch.slug === enrollmentData.branch) {
continue;
}
const branchMessages =
@@ -419,7 +414,7 @@ export const MessageLoaderUtils = {
}
experiments.push({
forReachEvent: { sent: false, group: featureId },
experimentSlug: experimentData.slug,
experimentSlug: enrollmentData.slug,
branchSlug: branch.slug,
...message,
});

View File

@@ -24,7 +24,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
ClientID: "resource://gre/modules/ClientID.sys.mjs",
AboutWelcomeTelemetry:
"resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs",
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
EnrollmentType: "resource://nimbus/ExperimentAPI.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
TelemetrySession: "resource://gre/modules/TelemetrySession.sys.mjs",
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
});
@@ -76,14 +77,9 @@ export class ASRouterTelemetry {
* @return {bool}
*/
get isInCFRCohort() {
const experimentData = lazy.ExperimentAPI.getExperimentMetaData({
featureId: "cfr",
});
if (experimentData && experimentData.slug) {
return true;
}
return false;
return !!lazy.NimbusFeatures.cfr.getEnrollmentMetadata(
lazy.EnrollmentType.EXPERIMENT
);
}
/**

View File

@@ -7,7 +7,8 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
EnrollmentType: "resource://nimbus/ExperimentAPI.sys.mjs",
RemoteL10n: "resource:///modules/asrouter/RemoteL10n.sys.mjs",
});
@@ -51,17 +52,13 @@ export const ToastNotification = {
let { tag } = content;
let experimentMetadata =
lazy.ExperimentAPI.getExperimentMetaData({
featureId: "backgroundTaskMessage",
}) || {};
lazy.NimbusFeatures.backgroundTaskMessage.getEnrollmentMetadata(
lazy.EnrollmentType.EXPERIMENT
) ?? {};
if (
experimentMetadata?.active &&
experimentMetadata?.slug &&
experimentMetadata?.branch?.slug
) {
if (experimentMetadata) {
// Like `my-experiment:my-branch`.
tag = `${experimentMetadata?.slug}:${experimentMetadata?.branch?.slug}`;
tag = `${experimentMetadata.slug}:${experimentMetadata.branch}`;
}
// There are two events named `IMPRESSION` the first one refers to telemetry

View File

@@ -236,7 +236,7 @@ add_task(async function test_loading_experimentsAPI_rollout() {
await setup(rollout);
await RemoteSettingsExperimentLoader.updateRecipes();
await BrowserTestUtils.waitForCondition(() =>
ExperimentAPI.getRolloutMetaData({ featureId: "cfr" })
NimbusFeatures.cfr.getEnrollmentMetadata("rollout")
);
await assertMessageInState("xman_test_message");

View File

@@ -231,7 +231,12 @@ describe("ASRouter", () => {
"pbNewtab",
].reduce((features, featureId) => {
features[featureId] = {
getAllVariables: sandbox.stub().returns(null),
getEnrollmentMetadata: sandbox.stub().returns({
slug: "experiment-slug",
branch: "experiment-branch-slug",
isRollout: false,
}),
getAllVariables: sandbox.stub().returns(undefined),
recordExposureEvent: sandbox.stub(),
};
return features;
@@ -271,11 +276,6 @@ describe("ASRouter", () => {
},
NimbusFeatures: fakeNimbusFeatures,
ExperimentAPI: {
getExperimentMetaData: sandbox.stub().returns({
slug: "experiment-slug",
active: true,
branch: { slug: "experiment-branch-slug" },
}),
getAllBranches: sandbox.stub().resolves([]),
ready: sandbox.stub().resolves(),
},
@@ -1578,17 +1578,15 @@ describe("ASRouter", () => {
let experimentAPIStub;
let featureIds = ["cfr", "moments-page", "infobar", "spotlight"];
beforeEach(() => {
let getExperimentMetaDataStub = sandbox.stub();
let getAllBranchesStub = sandbox.stub();
featureIds.forEach(feature => {
global.NimbusFeatures[feature].getAllVariables.returns({
id: `message-${feature}`,
});
getExperimentMetaDataStub.withArgs({ featureId: feature }).returns({
global.NimbusFeatures[feature].getEnrollmentMetadata.returns({
slug: `slug-${feature}`,
branch: {
slug: `branch-${feature}`,
},
branch: `branch-${feature}`,
isRollout: false,
});
getAllBranchesStub.withArgs(`slug-${feature}`).resolves([
{
@@ -1598,7 +1596,6 @@ describe("ASRouter", () => {
]);
});
experimentAPIStub = {
getExperimentMetaData: getExperimentMetaDataStub,
getAllBranches: getAllBranchesStub,
};
globals.set("ExperimentAPI", experimentAPIStub);
@@ -2461,11 +2458,8 @@ describe("ASRouter", () => {
await MessageLoaderUtils.loadMessagesForProvider(args);
assert.calledOnce(global.NimbusFeatures.spotlight.getEnrollmentMetadata);
assert.calledOnce(global.NimbusFeatures.spotlight.getAllVariables);
assert.calledOnce(global.ExperimentAPI.getExperimentMetaData);
assert.calledWithExactly(global.ExperimentAPI.getExperimentMetaData, {
featureId: "spotlight",
});
});
it("should handle the case of no experiments in the ExperimentAPI", async () => {
const args = {
@@ -2483,6 +2477,7 @@ describe("ASRouter", () => {
featureIds: ["infobar"],
};
const enrollment = {
slug: "enrollment01",
branch: {
slug: "branch01",
infobar: {
@@ -2495,8 +2490,10 @@ describe("ASRouter", () => {
global.NimbusFeatures.infobar.getAllVariables.returns(
enrollment.branch.infobar.value
);
global.ExperimentAPI.getExperimentMetaData.returns({
branch: { slug: enrollment.branch.slug },
global.NimbusFeatures.infobar.getEnrollmentMetadata.returns({
slug: enrollment.slug,
branch: enrollment.branch.slug,
isRollout: false,
});
global.ExperimentAPI.getAllBranches.returns([
enrollment.branch,
@@ -2544,10 +2541,10 @@ describe("ASRouter", () => {
global.NimbusFeatures.cfr.getAllVariables.returns(
enrollment.branch.cfr.value
);
global.ExperimentAPI.getExperimentMetaData.returns({
global.NimbusFeatures.cfr.getEnrollmentMetadata.returns({
slug: enrollment.slug,
active: true,
branch: { slug: enrollment.branch.slug },
branch: enrollment.branch.slug,
isRollout: false,
});
global.ExperimentAPI.getAllBranches.resolves([
enrollment.branch,
@@ -2596,15 +2593,15 @@ describe("ASRouter", () => {
},
};
// Nedds to match the `featureIds` value to return an enrollment
// Needs to match the `featureIds` value to return an enrollment
// for that feature
global.NimbusFeatures.cfr.getAllVariables.returns(
enrollment.branch.cfr.value
);
global.ExperimentAPI.getExperimentMetaData.returns({
global.NimbusFeatures.cfr.getEnrollmentMetadata.returns({
slug: enrollment.slug,
active: true,
branch: { slug: enrollment.branch.slug },
branch: enrollment.branch.slug,
isRollout: false,
});
global.ExperimentAPI.getAllBranches.resolves([
enrollment.branch,

View File

@@ -4,6 +4,7 @@ import {
GlobalOverrider,
FakeConsoleAPI,
FakeLogger,
FakeNimbusFeatures,
} from "tests/unit/utils";
import Adapter from "enzyme-adapter-react-16";
import chaiJsonSchema from "chai-json-schema";
@@ -13,6 +14,7 @@ import {
MESSAGE_TYPE_LIST,
MESSAGE_TYPE_HASH,
} from "modules/ActorConstants.mjs";
import { MESSAGING_EXPERIMENTS_DEFAULT_FEATURES } from "modules/MessagingExperimentConstants.sys.mjs";
enzyme.configure({ adapter: new Adapter() });
@@ -515,30 +517,14 @@ const TEST_GLOBAL = {
},
},
FX_MONITOR_OAUTH_CLIENT_ID: "fake_client_id",
ExperimentAPI: {
getExperimentMetaData() {},
getRolloutMetaData() {},
},
NimbusFeatures: {
glean: {
getVariable() {},
},
newtab: {
getVariable() {},
getAllVariables() {},
onUpdate() {},
offUpdate() {},
},
pocketNewtab: {
getVariable() {},
getAllVariables() {},
onUpdate() {},
offUpdate() {},
},
cookieBannerHandling: {
getVariable() {},
},
},
ExperimentAPI: {},
NimbusFeatures: FakeNimbusFeatures([
...MESSAGING_EXPERIMENTS_DEFAULT_FEATURES,
"glean",
"newtab",
"pocketNewtab",
"cookieBannerHandling",
]),
TelemetryEnvironment: {
setExperimentActive() {},
currentEnvironment: {

View File

@@ -268,6 +268,22 @@ export class FakeConsoleAPI {
}
}
export function FakeNimbusFeature() {
return {
getEnrollmentMetadata() {},
getVariable() {},
getAllVariables() {},
onUpdate() {},
offUpdate() {},
};
}
export function FakeNimbusFeatures(featureIds) {
return Object.fromEntries(
featureIds.map(featureId => [featureId, FakeNimbusFeature()])
);
}
export class FakeLogger extends FakeConsoleAPI {
constructor() {
super({

View File

@@ -14,7 +14,7 @@ const { ASRouterTelemetry } = ChromeUtils.importESModule(
ChromeUtils.defineESModuleGetters(this, {
AboutWelcomeTelemetry:
"resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs",
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
JsonSchemaValidator:
"resource://gre/modules/components-utils/JsonSchemaValidator.sys.mjs",
sinon: "resource://testing-common/Sinon.sys.mjs",
@@ -126,8 +126,10 @@ add_task(async function test_applyCFRPolicy_experiment_release() {
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
sandbox.stub(NimbusFeatures.cfr, "getEnrollmentMetadata").returns({
slug: "SOME-CFR-EXP",
branch: "branch-slug",
isRollout: false,
});
let instance = new ASRouterTelemetry();
@@ -191,8 +193,10 @@ add_task(
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
sandbox.stub(NimbusFeatures.cfr, "getEnrollmentMetadata").returns({
slug: "SOME-CFR-EXP",
branch: "branch-slug",
isRollout: false,
});
let instance = new ASRouterTelemetry();
@@ -345,8 +349,10 @@ add_task(async function test_applyMomentsPolicy_experiment_release() {
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
sandbox.stub(NimbusFeatures.cfr, "getEnrollmentMetadata").returns({
slug: "SOME-CFR-EXP",
branch: "branch-slug",
isRollout: false,
});
let instance = new ASRouterTelemetry();
@@ -663,15 +669,13 @@ add_task(
let sandbox = sinon.createSandbox();
let instance = new ASRouterTelemetry();
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
sandbox.stub(NimbusFeatures.cfr, "getEnrollmentMetadata").returns({
slug: "SOME-CFR-EXP",
branch: "branch-slug",
isRollout: false,
});
Assert.ok(instance.isInCFRCohort, "Should be in a CFR cohort");
Assert.equal(
ExperimentAPI.getExperimentMetaData.firstCall.args[0].featureId,
"cfr"
);
sandbox.restore();
}