Bug 1650104 - Enable reach event for Moments Page experiments r=andreio

Differential Revision: https://phabricator.services.mozilla.com/D82770
This commit is contained in:
Nan Jiang 2020-07-09 15:10:21 +00:00
Родитель 396a329ec4
Коммит dc03a7eeaf
4 изменённых файлов: 134 добавлений и 17 удалений

Просмотреть файл

@ -111,7 +111,7 @@ const USE_REMOTE_L10N_PREF =
// Experiment groups that need to report the reach event in Messaging-Experiments. // Experiment groups that need to report the reach event in Messaging-Experiments.
// If you're adding new groups to it, make sure they're also added in the // If you're adding new groups to it, make sure they're also added in the
// `messaging_experiments.reach.objects` defined in "toolkit/components/telemetry/Events.yaml" // `messaging_experiments.reach.objects` defined in "toolkit/components/telemetry/Events.yaml"
const REACH_EVENT_GROUPS = ["cfr"]; const REACH_EVENT_GROUPS = ["cfr", "moments-page"];
const REACH_EVENT_CATEGORY = "messaging_experiments"; const REACH_EVENT_CATEGORY = "messaging_experiments";
const REACH_EVENT_METHOD = "reach"; const REACH_EVENT_METHOD = "reach";
@ -383,7 +383,7 @@ const MessageLoaderUtils = {
) { ) {
experiments.push({ experiments.push({
group, group,
forReachEvent: true, forReachEvent: { sent: false },
experimentSlug: experimentData.slug, experimentSlug: experimentData.slug,
branchSlug: branch.slug, branchSlug: branch.slug,
...branch.value, ...branch.value,
@ -2045,7 +2045,10 @@ class _ASRouter {
const nonReachMessages = []; const nonReachMessages = [];
for (const message of messages) { for (const message of messages) {
if (message.forReachEvent) { if (message.forReachEvent) {
this._recordReachEvent(message); if (!message.forReachEvent.sent) {
this._recordReachEvent(message);
message.forReachEvent.sent = true;
}
} else { } else {
nonReachMessages.push(message); nonReachMessages.push(message);
} }

Просмотреть файл

@ -17,6 +17,13 @@ XPCOMUtils.defineLazyModuleGetters(this, {
const SYSTEM_TICK_INTERVAL = 5 * 60 * 1000; const SYSTEM_TICK_INTERVAL = 5 * 60 * 1000;
const HOMEPAGE_OVERRIDE_PREF = "browser.startup.homepage_override.once"; const HOMEPAGE_OVERRIDE_PREF = "browser.startup.homepage_override.once";
// For the "reach" event of Messaging Experiments
const REACH_EVENT_CATEGORY = "messaging_experiments";
const REACH_EVENT_METHOD = "reach";
// Note it's not "moments-page" as Telemetry Events only accepts understores
// for the event `object`
const REACH_EVENT_OBJECT = "moments_page";
class _MomentsPageHub { class _MomentsPageHub {
constructor() { constructor() {
this.id = "moments-page-hub"; this.id = "moments-page-hub";
@ -97,16 +104,42 @@ class _MomentsPageHub {
} }
} }
_recordReachEvent(message) {
const extra = { branches: message.branchSlug };
Services.telemetry.recordEvent(
REACH_EVENT_CATEGORY,
REACH_EVENT_METHOD,
REACH_EVENT_OBJECT,
message.experimentSlug,
extra
);
}
async messageRequest({ triggerId, template }) { async messageRequest({ triggerId, template }) {
const telemetryObject = { triggerId }; const telemetryObject = { triggerId };
TelemetryStopwatch.start("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject); TelemetryStopwatch.start("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject);
const message = await this._handleMessageRequest({ const messages = await this._handleMessageRequest({
triggerId, triggerId,
template, template,
returnAll: true,
}); });
TelemetryStopwatch.finish("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject); TelemetryStopwatch.finish("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject);
if (message) {
this.executeAction(message); // Record the "reach" event for all the messages with `forReachEvent`,
// only execute action for the first message without forReachEvent.
const nonReachMessages = [];
for (const message of messages) {
if (message.forReachEvent) {
if (!message.forReachEvent.sent) {
this._recordReachEvent(message);
message.forReachEvent.sent = true;
}
} else {
nonReachMessages.push(message);
}
}
if (nonReachMessages.length) {
this.executeAction(nonReachMessages[0]);
} }
} }

Просмотреть файл

@ -2333,7 +2333,7 @@ describe("ASRouter", () => {
let messages = [ let messages = [
{ {
id: "foo1", id: "foo1",
forReachEvent: true, forReachEvent: { sent: false },
experimentSlug: "exp01", experimentSlug: "exp01",
branchSlug: "branch01", branchSlug: "branch01",
group: "cfr", group: "cfr",
@ -2351,7 +2351,7 @@ describe("ASRouter", () => {
}, },
{ {
id: "foo3", id: "foo3",
forReachEvent: true, forReachEvent: { sent: false },
experimentSlug: "exp02", experimentSlug: "exp02",
branchSlug: "branch02", branchSlug: "branch02",
group: "cfr", group: "cfr",
@ -2371,6 +2371,30 @@ describe("ASRouter", () => {
await Router.onMessage(msg); await Router.onMessage(msg);
assert.calledTwice(Services.telemetry.recordEvent); assert.calledTwice(Services.telemetry.recordEvent);
}); });
it("should not record the Reach event if it's already sent", async () => {
let messages = [
{
id: "foo1",
forReachEvent: { sent: true },
experimentSlug: "exp01",
branchSlug: "branch01",
group: "cfr",
template: "simple_template",
trigger: { id: "foo" },
content: { title: "Foo1", body: "Foo123-1" },
},
];
sandbox.stub(Router, "handleMessageRequest").resolves(messages);
sandbox.spy(Services.telemetry, "recordEvent");
const msg = fakeAsyncMessage({
type: "TRIGGER",
data: { trigger: { id: "foo" } },
});
await Router.onMessage(msg);
assert.notCalled(Services.telemetry.recordEvent);
});
}); });
describe(".includeBundle", () => { describe(".includeBundle", () => {
@ -4109,7 +4133,7 @@ describe("ASRouter", () => {
assert.equal(result.messages[1].group, "cfr"); assert.equal(result.messages[1].group, "cfr");
assert.equal(result.messages[1].experimentSlug, "exp01"); assert.equal(result.messages[1].experimentSlug, "exp01");
assert.equal(result.messages[1].branchSlug, "branch02"); assert.equal(result.messages[1].branchSlug, "branch02");
assert.ok(result.messages[1].forReachEvent); assert.deepEqual(result.messages[1].forReachEvent, { sent: false });
}); });
it("should fetch json from url", async () => { it("should fetch json from url", async () => {
let result = await MessageLoaderUtils.loadMessagesForProvider({ let result = await MessageLoaderUtils.loadMessagesForProvider({

Просмотреть файл

@ -20,10 +20,10 @@ describe("MomentsPageHub", () => {
globals = new GlobalOverrider(); globals = new GlobalOverrider();
sandbox = sinon.createSandbox(); sandbox = sinon.createSandbox();
instance = new _MomentsPageHub(); instance = new _MomentsPageHub();
const [msg] = (await PanelTestProvider.getMessages()).filter( const messages = (await PanelTestProvider.getMessages()).filter(
({ template }) => template === "update_action" ({ template }) => template === "update_action"
); );
handleMessageRequestStub = sandbox.stub().resolves(msg); handleMessageRequestStub = sandbox.stub().resolves(messages);
addImpressionStub = sandbox.stub(); addImpressionStub = sandbox.stub();
blockMessageByIdStub = sandbox.stub(); blockMessageByIdStub = sandbox.stub();
getStringPrefStub = sandbox.stub(); getStringPrefStub = sandbox.stub();
@ -39,6 +39,9 @@ describe("MomentsPageHub", () => {
getStringPref: getStringPrefStub, getStringPref: getStringPrefStub,
setStringPref: setStringPrefStub, setStringPref: setStringPrefStub,
}, },
telemetry: {
recordEvent: () => {},
},
}, },
}); });
}); });
@ -101,12 +104,13 @@ describe("MomentsPageHub", () => {
assert.calledWithExactly(handleMessageRequestStub, { assert.calledWithExactly(handleMessageRequestStub, {
triggerId: "trigger", triggerId: "trigger",
template: "template", template: "template",
returnAll: true,
}); });
}); });
it("shouldn't do anything if no message is provided", async () => { it("shouldn't do anything if no message is provided", async () => {
// Reset the call from `instance.init` // Reset the call from `instance.init`
setStringPrefStub.reset(); setStringPrefStub.reset();
handleMessageRequestStub.resolves(null); handleMessageRequestStub.resolves([]);
await instance.messageRequest({ triggerId: "trigger" }); await instance.messageRequest({ triggerId: "trigger" });
assert.notCalled(setStringPrefStub); assert.notCalled(setStringPrefStub);
@ -136,6 +140,59 @@ describe("MomentsPageHub", () => {
{ triggerId: "trigger" } { triggerId: "trigger" }
); );
}); });
it("should record Reach event for the Moments page experiment", async () => {
const momentsMessages = (await PanelTestProvider.getMessages()).filter(
({ template }) => template === "update_action"
);
const messages = [
{
forReachEvent: { sent: false },
experimentSlug: "foo",
branchSlug: "bar",
},
...momentsMessages,
];
handleMessageRequestStub.resolves(messages);
sandbox.spy(global.Services.telemetry, "recordEvent");
sandbox.spy(instance, "executeAction");
await instance.messageRequest({ triggerId: "trigger" });
assert.calledOnce(global.Services.telemetry.recordEvent);
assert.calledOnce(instance.executeAction);
});
it("should not record the Reach event if it's already sent", async () => {
const messages = [
{
forReachEvent: { sent: true },
experimentSlug: "foo",
branchSlug: "bar",
},
];
handleMessageRequestStub.resolves(messages);
sandbox.spy(global.Services.telemetry, "recordEvent");
await instance.messageRequest({ triggerId: "trigger" });
assert.notCalled(global.Services.telemetry.recordEvent);
});
it("should not trigger the action if it's only for the Reach event", async () => {
const messages = [
{
forReachEvent: { sent: false },
experimentSlug: "foo",
branchSlug: "bar",
},
];
handleMessageRequestStub.resolves(messages);
sandbox.spy(global.Services.telemetry, "recordEvent");
sandbox.spy(instance, "executeAction");
await instance.messageRequest({ triggerId: "trigger" });
assert.calledOnce(global.Services.telemetry.recordEvent);
assert.notCalled(instance.executeAction);
});
}); });
describe("executeAction", () => { describe("executeAction", () => {
beforeEach(async () => { beforeEach(async () => {
@ -147,7 +204,7 @@ describe("MomentsPageHub", () => {
}); });
}); });
it("should set HOMEPAGE_OVERRIDE_PREF on `moments-wnp` action", async () => { it("should set HOMEPAGE_OVERRIDE_PREF on `moments-wnp` action", async () => {
const msg = await handleMessageRequestStub(); const [msg] = await handleMessageRequestStub();
sandbox.useFakeTimers(); sandbox.useFakeTimers();
instance.executeAction(msg); instance.executeAction(msg);
@ -165,7 +222,7 @@ describe("MomentsPageHub", () => {
); );
}); });
it("should block after taking the action", async () => { it("should block after taking the action", async () => {
const msg = await handleMessageRequestStub(); const [msg] = await handleMessageRequestStub();
instance.executeAction(msg); instance.executeAction(msg);
assert.calledOnce(blockMessageByIdStub); assert.calledOnce(blockMessageByIdStub);
@ -174,7 +231,7 @@ describe("MomentsPageHub", () => {
it("should compute expire based on expireDelta", async () => { it("should compute expire based on expireDelta", async () => {
sandbox.spy(instance, "getExpirationDate"); sandbox.spy(instance, "getExpirationDate");
const msg = await handleMessageRequestStub(); const [msg] = await handleMessageRequestStub();
instance.executeAction(msg); instance.executeAction(msg);
assert.calledOnce(instance.getExpirationDate); assert.calledOnce(instance.getExpirationDate);
@ -186,7 +243,7 @@ describe("MomentsPageHub", () => {
it("should compute expire based on expireDelta", async () => { it("should compute expire based on expireDelta", async () => {
sandbox.spy(instance, "getExpirationDate"); sandbox.spy(instance, "getExpirationDate");
const msg = await handleMessageRequestStub(); const [msg] = await handleMessageRequestStub();
const msgWithExpire = { const msgWithExpire = {
...msg, ...msg,
content: { content: {
@ -212,7 +269,7 @@ describe("MomentsPageHub", () => {
); );
}); });
it("should send user telemetry", async () => { it("should send user telemetry", async () => {
const msg = await handleMessageRequestStub(); const [msg] = await handleMessageRequestStub();
const sendUserEventTelemetrySpy = sandbox.spy( const sendUserEventTelemetrySpy = sandbox.spy(
instance, instance,
"sendUserEventTelemetry" "sendUserEventTelemetry"