зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1631456 - Create a CFR message loader for ExperimentAPI r=k88hudson
Differential Revision: https://phabricator.services.mozilla.com/D71766
This commit is contained in:
Родитель
8f02f0cbfa
Коммит
5cfefdf0ed
|
@ -1287,6 +1287,7 @@ pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "{\
|
|||
// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
|
||||
// repackager of this code using an alternate snippet url, please keep your users safe
|
||||
pref("browser.newtabpage.activity-stream.asrouter.providers.snippets", "{\"id\":\"snippets\",\"enabled\":true,\"type\":\"remote\",\"url\":\"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/\",\"updateCycleInMs\":14400000}");
|
||||
pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"messageGroups\":[\"cfr\",\"whats-new-panel\",\"moments-page\",\"snippets\",\"cfr-fxa\"],\"updateCycleInMs\":3600000}");
|
||||
|
||||
// The pref that controls if ASRouter uses the remote fluent files.
|
||||
// It's enabled by default, but could be disabled to force ASRouter to use the local files.
|
||||
|
|
|
@ -35,6 +35,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
Downloader: "resource://services-settings/Attachments.jsm",
|
||||
RemoteL10n: "resource://activity-stream/lib/RemoteL10n.jsm",
|
||||
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
|
||||
ExperimentAPI: "resource://messaging-system/experiments/ExperimentAPI.jsm",
|
||||
});
|
||||
XPCOMUtils.defineLazyServiceGetters(this, {
|
||||
BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
|
||||
|
@ -336,6 +337,31 @@ const MessageLoaderUtils = {
|
|||
return RemoteSettings(bucket).get();
|
||||
},
|
||||
|
||||
async _experimentsAPILoader(provider, options) {
|
||||
try {
|
||||
await ExperimentAPI.ready();
|
||||
} catch (e) {
|
||||
MessageLoaderUtils.reportError(e);
|
||||
return [];
|
||||
}
|
||||
return provider.messageGroups
|
||||
.map(group => {
|
||||
let experimentData;
|
||||
try {
|
||||
experimentData = ExperimentAPI.getExperiment({ group });
|
||||
} catch (e) {
|
||||
MessageLoaderUtils.reportError(e);
|
||||
return [];
|
||||
}
|
||||
if (experimentData && experimentData.branch) {
|
||||
return experimentData.branch.value;
|
||||
}
|
||||
|
||||
return [];
|
||||
})
|
||||
.flat();
|
||||
},
|
||||
|
||||
_handleRemoteSettingsUndesiredEvent(event, providerId, dispatchToAS) {
|
||||
if (dispatchToAS) {
|
||||
dispatchToAS(
|
||||
|
@ -363,6 +389,8 @@ const MessageLoaderUtils = {
|
|||
return this._remoteSettingsLoader;
|
||||
case "json":
|
||||
return this._localJsonLoader;
|
||||
case "remote-experiments":
|
||||
return this._experimentsAPILoader;
|
||||
case "local":
|
||||
default:
|
||||
return this._localLoader;
|
||||
|
|
|
@ -39,3 +39,4 @@ skip-if = (os == "linux") # Test setup only implemented for OSX and Windows
|
|||
tags = remote-settings
|
||||
[browser_asrouter_momentspagehub.js]
|
||||
tags = remote-settings
|
||||
[browser_asrouter_experimentsAPILoader.js]
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
const { RemoteSettings } = ChromeUtils.import(
|
||||
"resource://services-settings/remote-settings.js"
|
||||
);
|
||||
const { ASRouter } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/ASRouter.jsm"
|
||||
);
|
||||
const { RemoteSettingsExperimentLoader } = ChromeUtils.import(
|
||||
"resource://messaging-system/lib/RemoteSettingsExperimentLoader.jsm"
|
||||
);
|
||||
const { ExperimentAPI } = ChromeUtils.import(
|
||||
"resource://messaging-system/experiments/ExperimentAPI.jsm"
|
||||
);
|
||||
|
||||
const EXPERIMENT_PAYLOAD = {
|
||||
enabled: true,
|
||||
arguments: {
|
||||
slug: "test_xman_cfr",
|
||||
branches: [
|
||||
{
|
||||
slug: "control",
|
||||
ratio: 1,
|
||||
value: [
|
||||
{
|
||||
id: "xman_test_message",
|
||||
content: {
|
||||
text: "This is a test CFR",
|
||||
addon: {
|
||||
id: "954390",
|
||||
icon:
|
||||
"resource://activity-stream/data/content/assets/cfr_fb_container.png",
|
||||
title: "Facebook Container",
|
||||
users: 1455872,
|
||||
author: "Mozilla",
|
||||
rating: 4.5,
|
||||
amo_url:
|
||||
"https://addons.mozilla.org/firefox/addon/facebook-container/",
|
||||
},
|
||||
buttons: {
|
||||
primary: {
|
||||
label: {
|
||||
string_id: "cfr-doorhanger-extension-ok-button",
|
||||
},
|
||||
action: {
|
||||
data: {
|
||||
url: null,
|
||||
},
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
},
|
||||
},
|
||||
secondary: [
|
||||
{
|
||||
label: {
|
||||
string_id: "cfr-doorhanger-extension-cancel-button",
|
||||
},
|
||||
action: {
|
||||
type: "CANCEL",
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
string_id:
|
||||
"cfr-doorhanger-extension-never-show-recommendation",
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
string_id:
|
||||
"cfr-doorhanger-extension-manage-settings-button",
|
||||
},
|
||||
action: {
|
||||
data: {
|
||||
origin: "CFR",
|
||||
category: "general-cfraddons",
|
||||
},
|
||||
type: "OPEN_PREFERENCES_PAGE",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
category: "cfrAddons",
|
||||
bucket_id: "CFR_M1",
|
||||
info_icon: {
|
||||
label: {
|
||||
string_id: "cfr-doorhanger-extension-sumo-link",
|
||||
},
|
||||
sumo_path: "extensionrecommendations",
|
||||
},
|
||||
heading_text: "Welcome to the experiment",
|
||||
notification_text: {
|
||||
string_id: "cfr-doorhanger-extension-notification2",
|
||||
},
|
||||
},
|
||||
trigger: {
|
||||
id: "openURL",
|
||||
params: [
|
||||
"www.facebook.com",
|
||||
"facebook.com",
|
||||
"www.instagram.com",
|
||||
"instagram.com",
|
||||
"www.whatsapp.com",
|
||||
"whatsapp.com",
|
||||
"web.whatsapp.com",
|
||||
"www.messenger.com",
|
||||
"messenger.com",
|
||||
],
|
||||
},
|
||||
template: "cfr_doorhanger",
|
||||
frequency: {
|
||||
lifetime: 3,
|
||||
},
|
||||
targeting: "true",
|
||||
},
|
||||
],
|
||||
groups: ["cfr"],
|
||||
},
|
||||
],
|
||||
isHighVolume: "false,",
|
||||
userFacingName: "About:Welcome Pull Factor Reinforcement",
|
||||
isEnrollmentPaused: false,
|
||||
experimentDocumentUrl:
|
||||
"https://experimenter.services.mozilla.com/experiments/aboutwelcome-pull-factor-reinforcement/",
|
||||
userFacingDescription:
|
||||
"This study uses 4 different variants of about:welcome with a goal of testing new experiment framework and get insights on whether reinforcing pull-factors improves retention.",
|
||||
},
|
||||
filter_expression: "true",
|
||||
id: "test_xman_cfr",
|
||||
};
|
||||
|
||||
add_task(async function test_loading_experimentsAPI() {
|
||||
// Force the WNPanel provider cache to 0 by modifying updateCycleInMs
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[
|
||||
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments",
|
||||
`{"id":"messaging-experiments","enabled":true,"type":"remote-experiments","messageGroups":["cfr","whats-new-panel","moments-page","snippets","cfr-fxa"],"frequency":{"custom":[{"period":"daily","cap":1}]},"updateCycleInMs":0}`,
|
||||
],
|
||||
],
|
||||
});
|
||||
const client = RemoteSettings("messaging-experiments");
|
||||
await client.db.clear();
|
||||
await client.db.create(
|
||||
// Modify targeting to ensure the messages always show up
|
||||
{ ...EXPERIMENT_PAYLOAD }
|
||||
);
|
||||
await client.db.saveLastModified(42); // Prevent from loading JSON dump.
|
||||
|
||||
// Fetch the new recipe from RS
|
||||
await RemoteSettingsExperimentLoader.updateRecipes();
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => ExperimentAPI.getExperiment({ group: "cfr" }),
|
||||
"ExperimentAPI should return an experiment"
|
||||
);
|
||||
|
||||
// Reload the provider
|
||||
await ASRouter._updateMessageProviders();
|
||||
// Wait to load the messages from the messaging-experiments provider
|
||||
await ASRouter.loadMessagesFromAllProviders();
|
||||
|
||||
Assert.ok(ASRouter.state.messages.find(m => m.id === "xman_test_message"));
|
||||
|
||||
await client.db.clear();
|
||||
await SpecialPowers.popPrefEnv();
|
||||
// Reload the provider
|
||||
await ASRouter._updateMessageProviders();
|
||||
});
|
|
@ -206,6 +206,10 @@ describe("ASRouter", () => {
|
|||
return Promise.resolve("/path/to/download");
|
||||
}
|
||||
},
|
||||
ExperimentAPI: {
|
||||
getExperiment: sandbox.stub().returns({ branch: { value: [] } }),
|
||||
ready: sandbox.stub().resolves(),
|
||||
},
|
||||
});
|
||||
await createRouterAndInit();
|
||||
});
|
||||
|
@ -4161,6 +4165,71 @@ describe("ASRouter", () => {
|
|||
});
|
||||
});
|
||||
describe("#loadMessagesForProvider", () => {
|
||||
it("should fetch messages from the ExperimentAPI", async () => {
|
||||
const args = {
|
||||
type: "remote-experiments",
|
||||
messageGroups: ["asrouter"],
|
||||
};
|
||||
|
||||
await MessageLoaderUtils.loadMessagesForProvider(args);
|
||||
|
||||
assert.calledOnce(global.ExperimentAPI.getExperiment);
|
||||
assert.calledWithExactly(global.ExperimentAPI.getExperiment, {
|
||||
group: "asrouter",
|
||||
});
|
||||
});
|
||||
it("should handle the case of no experiments in the ExperimentAPI", async () => {
|
||||
const args = {
|
||||
type: "remote-experiments",
|
||||
messageGroups: ["asrouter"],
|
||||
};
|
||||
|
||||
global.ExperimentAPI.getExperiment.throws();
|
||||
const stub = sandbox.stub(MessageLoaderUtils, "reportError");
|
||||
|
||||
await MessageLoaderUtils.loadMessagesForProvider(args);
|
||||
|
||||
assert.calledOnce(stub);
|
||||
});
|
||||
it("should handle the case of no experiments in the ExperimentAPI", async () => {
|
||||
const args = {
|
||||
type: "remote-experiments",
|
||||
messageGroups: ["asrouter"],
|
||||
};
|
||||
|
||||
global.ExperimentAPI.getExperiment.returns(null);
|
||||
|
||||
const result = await MessageLoaderUtils.loadMessagesForProvider(args);
|
||||
|
||||
assert.lengthOf(result.messages, 0);
|
||||
});
|
||||
it("should normally load ExperimentAPI messages", async () => {
|
||||
const args = {
|
||||
type: "remote-experiments",
|
||||
messageGroups: ["asrouter"],
|
||||
};
|
||||
|
||||
global.ExperimentAPI.getExperiment.returns({
|
||||
branch: { value: ["foo", "bar"] },
|
||||
});
|
||||
|
||||
const result = await MessageLoaderUtils.loadMessagesForProvider(args);
|
||||
|
||||
assert.lengthOf(result.messages, 2);
|
||||
});
|
||||
it("should fetch messages from the ExperimentAPI", async () => {
|
||||
global.ExperimentAPI.ready.throws();
|
||||
const args = {
|
||||
type: "remote-experiments",
|
||||
messageGroups: ["asrouter"],
|
||||
};
|
||||
const stub = sandbox.stub(MessageLoaderUtils, "reportError");
|
||||
|
||||
await MessageLoaderUtils.loadMessagesForProvider(args);
|
||||
|
||||
assert.notCalled(global.ExperimentAPI.getExperiment);
|
||||
assert.calledOnce(stub);
|
||||
});
|
||||
it("should fetch json from url", async () => {
|
||||
let result = await MessageLoaderUtils.loadMessagesForProvider({
|
||||
location: "http://fake.com/endpoint",
|
||||
|
|
Загрузка…
Ссылка в новой задаче