Bug 1765907 - PBM experiment message should override default promo message r=mviar

Differential Revision: https://phabricator.services.mozilla.com/D144871
This commit is contained in:
Punam Dahiya
2022-05-12 18:24:06 +00:00
parent 4c67b09182
commit 74fd8b3a3e
9 changed files with 173 additions and 94 deletions

View File

@@ -32,6 +32,13 @@ class AboutPrivateBrowsingChild extends RemotePageChild {
Cu.exportFunction(this.PrivateBrowsingRecordClick.bind(this), window, {
defineAs: "PrivateBrowsingRecordClick",
});
Cu.exportFunction(
this.PrivateBrowsingShouldHideDefault.bind(this),
window,
{
defineAs: "PrivateBrowsingShouldHideDefault",
}
);
Cu.exportFunction(
this.PrivateBrowsingExposureTelemetry.bind(this),
window,
@@ -47,6 +54,12 @@ class AboutPrivateBrowsingChild extends RemotePageChild {
if (experiment) {
Services.telemetry.recordEvent("aboutprivatebrowsing", "click", source);
}
return experiment;
}
PrivateBrowsingShouldHideDefault() {
const config = NimbusFeatures.pbNewtab.getAllVariables() || {};
return config?.content?.hideDefault;
}
PrivateBrowsingExposureTelemetry() {

View File

@@ -1621,14 +1621,26 @@ class _ASRouter {
return this.loadMessagesFromAllProviders();
}
async sendPBNewTabMessage({ tabId }) {
async sendPBNewTabMessage({ tabId, hideDefault }) {
let message = null;
await this.loadMessagesFromAllProviders();
// If message has hideDefault property set to true
// remove from state all pb_newtab messages with type default
if (hideDefault) {
await this.setState(state => ({
messages: state.messages.filter(
m => !(m.template === "pb_newtab" && m.type === "default")
),
}));
}
const telemetryObject = { tabId };
TelemetryStopwatch.start("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject);
message = await this.handleMessageRequest({ template: "pb_newtab" });
message = await this.handleMessageRequest({
template: "pb_newtab",
});
TelemetryStopwatch.finish("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject);
// Format urls if any are defined

View File

@@ -97,6 +97,7 @@ const ONBOARDING_MESSAGES = () => [
},
{
id: "PB_NEWTAB_FOCUS_PROMO",
type: "default",
template: "pb_newtab",
groups: ["pbNewtab"],
content: {
@@ -123,7 +124,6 @@ const ONBOARDING_MESSAGES = () => [
id: "FOCUS_PROMO",
template: "multistage",
modal: "tab",
metrics: "block",
backdrop: "transparent",
screens: [
{
@@ -206,6 +206,7 @@ const ONBOARDING_MESSAGES = () => [
},
{
id: "PB_NEWTAB_KLAR_PROMO",
type: "default",
template: "pb_newtab",
groups: ["pbNewtab"],
content: {
@@ -233,7 +234,6 @@ const ONBOARDING_MESSAGES = () => [
id: "KLAR_PROMO",
template: "multistage",
modal: "tab",
metrics: "block",
backdrop: "transparent",
screens: [
{

View File

@@ -119,7 +119,14 @@ async function renderPromo({
} else if (promoButton?.action?.type === "SHOW_SPOTLIGHT") {
linkEl.addEventListener("click", async event => {
event.preventDefault();
window.PrivateBrowsingRecordClick("promo_link");
// Record promo click telemetry and set metrics as allow for spotlight
// modal opened on promo click if user is enrolled in an experiment
let isExperiment = window.PrivateBrowsingRecordClick("promo_link");
const promoButtonData = promoButton?.action?.data;
if (promoButtonData?.content) {
promoButtonData.content.metrics = isExperiment ? "allow" : "block";
}
await RPMSendQuery("SpecialMessageActionDispatch", promoButton.action);
});
} else {
@@ -238,14 +245,17 @@ async function handlePromoOnPreload(message) {
async function setupFeatureConfig() {
let config = null;
let message = null;
try {
config = window.PrivateBrowsingFeatureConfig();
} catch (e) {}
if (!Object.keys(config).length) {
let hideDefault = window.PrivateBrowsingShouldHideDefault();
try {
let response = await window.ASRouterMessage({
type: "PBNEWTAB_MESSAGE_REQUEST",
data: {},
data: { hideDefault: !!hideDefault },
});
message = response?.message;
config = message?.content;

View File

@@ -10,6 +10,7 @@ add_task(async function test_experiment_messaging_system_dismiss() {
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
hideDefault: true,
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
@@ -57,3 +58,64 @@ add_task(async function test_experiment_messaging_system_dismiss() {
await BrowserTestUtils.closeWindow(win2);
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_show_default_on_dismiss() {
registerCleanupFunction(() => {
ASRouter.resetMessageState();
});
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
hideDefault: false,
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com",
promoLinkUrl: "http://bar.example.com",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [], async function() {
ok(
content.document.querySelector(".promo"),
"should render the promo experiment message"
);
content.document.querySelector("#dismiss-btn").click();
info("button clicked");
});
let telemetryEvent = await waitForTelemetryEvent("aboutprivatebrowsing");
ok(
telemetryEvent[2] == "click" && telemetryEvent[3] == "dismiss_button",
"recorded the dismiss button click"
);
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [], async function() {
const promoHeader = content.document.getElementById("promo-header");
ok(
content.document.querySelector(".promo"),
"should render the default promo message after dismissing experiment promo"
);
is(
promoHeader.textContent,
"Next-level privacy on mobile",
"Correct default values are shown"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await doExperimentCleanup();
});

View File

@@ -10,11 +10,17 @@
*/
add_task(async function test_experiment_messaging_system_impressions() {
registerCleanupFunction(() => {
ASRouter.resetMessageState();
});
const LOCALE = Services.locale.appLocaleAsBCP47;
let experimentId = `pb_newtab_${Math.random()}`;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
id: experimentId,
template: "pb_newtab",
content: {
hideDefault: true,
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
@@ -43,20 +49,19 @@ add_task(async function test_experiment_messaging_system_impressions() {
);
});
await waitForTelemetryEvent("normandy");
TelemetryTestUtils.assertEvents(
[
{
method: "expose",
extra: {
featureId: "pbNewtab",
},
},
],
{ category: "normandy" },
{ process: "content" }
let event = await waitForTelemetryEvent("normandy", experimentId);
ok(
event[1] == "normandy" &&
event[2] == "expose" &&
event[3] == "nimbus_experiment" &&
event[4].includes(experimentId) &&
event[5].featureId == "pbNewtab",
"recorded telemetry for expose"
);
Services.telemetry.clearEvents();
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [LOCALE], async function(locale) {
@@ -67,20 +72,19 @@ add_task(async function test_experiment_messaging_system_impressions() {
);
});
await waitForTelemetryEvent("normandy");
TelemetryTestUtils.assertEvents(
[
{
method: "expose",
extra: {
featureId: "pbNewtab",
},
},
],
{ category: "normandy" },
{ process: "content" }
let event2 = await waitForTelemetryEvent("normandy", experimentId);
ok(
event2[1] == "normandy" &&
event2[2] == "expose" &&
event2[3] == "nimbus_experiment" &&
event2[4].includes(experimentId) &&
event2[5].featureId == "pbNewtab",
"recorded telemetry for expose"
);
Services.telemetry.clearEvents();
let { win: win3, tab: tab3 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab3, [], async function() {
@@ -90,10 +94,15 @@ add_task(async function test_experiment_messaging_system_impressions() {
"should no longer render the experiment message after 2 impressions"
);
});
TelemetryTestUtils.assertNumberOfEvents(0, {
category: "normandy",
method: "expose",
});
let event3 = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
false
).content;
Assert.equal(!!event3, false, "Should not have promo expose");
Services.telemetry.clearEvents();
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);

View File

@@ -15,6 +15,7 @@ add_task(async function test_experiment_messaging_system() {
id: "PB_NEWTAB_MESSAGING_SYSTEM",
template: "pb_newtab",
content: {
hideDefault: true,
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
@@ -28,22 +29,6 @@ add_task(async function test_experiment_messaging_system() {
targeting: "true",
});
await TestUtils.waitForCondition(() => {
Services.telemetry.clearEvents();
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
).content;
info("waiting for events to clear, but:");
info(JSON.stringify(events));
return !events || !events.length;
}, "Waiting for telemetry events to get cleared");
Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
);
Services.telemetry.clearEvents();
let { win, tab } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab, [LOCALE], async function(locale) {
@@ -80,23 +65,6 @@ add_task(async function test_experiment_messaging_system() {
);
});
await waitForTelemetryEvent("normandy");
TelemetryTestUtils.assertEvents(
[
{
method: "expose",
extra: {
featureId: "pbNewtab",
},
},
],
{ category: "normandy" },
{ process: "content" }
);
Services.telemetry.clearEvents();
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});

View File

@@ -112,23 +112,30 @@ function _initTest() {
);
}
function waitForTelemetryEvent(category) {
function waitForTelemetryEvent(category, value) {
info("waiting for telemetry event");
return TestUtils.waitForCondition(() => {
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
false
).content;
if (!events) {
return null;
}
events = events.filter(e => e[1] == category);
info(JSON.stringify(events));
// Check for experimentId passed as value
// if exists return events only for specific experimentId
if (value) {
events = events.filter(e => e[4].includes(value));
}
if (events.length) {
return events[0];
}
return null;
}, "waiting for telemetry event");
}, "wait and retrieve telemetry event");
}
async function setupMSExperimentWithMessage(message) {
@@ -144,12 +151,6 @@ async function setupMSExperimentWithMessage(message) {
});
await SpecialPowers.pushPrefEnv({
set: [
// Disable onboarding, so we don't have default stuff showing up on the
// private browsing surface.
[
"browser.newtabpage.activity-stream.asrouter.providers.onboarding",
'{"id":"onboarding","type":"local","localProvider":"OnboardingMessageProvider","enabled":false,"exclude":[]}',
],
[
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments",
'{"id":"messaging-experiments","enabled":true,"type":"remote-experiments","messageGroups":["pbNewtab"],"updateCycleInMs":0}',

View File

@@ -222,9 +222,14 @@ const ExperimentFakes = {
{ manager = ExperimentManager } = {}
) {
await manager.store.ready();
let recipe = this.recipe(
`${featureConfig.featureId}-experiment-${Math.random()}`,
{
// Use id passed in featureConfig value to compute experimentId
// This help filter telemetry events (such as expose) in race conditions when telemetry
// from multiple experiments with same featureId co-exist in snapshot
let experimentId = `${featureConfig.featureId}${
featureConfig?.value?.id ? "-" + featureConfig?.value?.id : ""
}-experiment-${Math.random()}`;
let recipe = this.recipe(experimentId, {
bucketConfig: {
namespace: "mstest-utils",
randomizationUnit: "normandy_id",
@@ -239,8 +244,7 @@ const ExperimentFakes = {
features: [featureConfig],
},
],
}
);
});
let {
enrollmentPromise,
doExperimentCleanup,