Bug 1914974: Update Review Checker telemetry to record if a recommendation was sponsored. r=shopping-reviewers,kpatenio
- Adds an extra sponsored key to pass if the recommendation was sponsored or not to `surface_ads_placement`, `surface_ads_impression` and `surface_ads_clicked`. - Records `ads_exposure` for all recommendations. - Parses the JSON response in exposure telemetry tests. Differential Revision: https://phabricator.services.mozilla.com/D220302
This commit is contained in:
@@ -147,7 +147,7 @@ export class ShoppingSidebarChild extends RemotePageChild {
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
let aid;
|
||||
let aid, sponsored;
|
||||
switch (event.type) {
|
||||
case "ContentReady":
|
||||
this.updateContent();
|
||||
@@ -160,13 +160,15 @@ export class ShoppingSidebarChild extends RemotePageChild {
|
||||
break;
|
||||
case "AdClicked":
|
||||
aid = event.detail.aid;
|
||||
sponsored = event.detail.sponsored;
|
||||
ShoppingProduct.sendAttributionEvent("click", aid);
|
||||
Glean.shopping.surfaceAdsClicked.record();
|
||||
Glean.shopping.surfaceAdsClicked.record({ sponsored });
|
||||
break;
|
||||
case "AdImpression":
|
||||
aid = event.detail.aid;
|
||||
sponsored = event.detail.sponsored;
|
||||
ShoppingProduct.sendAttributionEvent("impression", aid);
|
||||
Glean.shopping.surfaceAdsImpression.record();
|
||||
Glean.shopping.surfaceAdsImpression.record({ sponsored });
|
||||
break;
|
||||
case "DisableShopping":
|
||||
this.sendAsyncMessage("DisableShopping");
|
||||
@@ -483,11 +485,16 @@ export class ShoppingSidebarChild extends RemotePageChild {
|
||||
// We tried to fetch an ad, but didn't get one.
|
||||
Glean.shopping.surfaceNoAdsAvailable.record();
|
||||
} else {
|
||||
let sponsored = recommendationData[0].sponsored;
|
||||
|
||||
ShoppingProduct.sendAttributionEvent(
|
||||
"placement",
|
||||
recommendationData[0].aid
|
||||
);
|
||||
Glean.shopping.surfaceAdsPlacement.record();
|
||||
|
||||
Glean.shopping.surfaceAdsPlacement.record({
|
||||
sponsored,
|
||||
});
|
||||
}
|
||||
|
||||
this.sendToContent("UpdateRecommendations", {
|
||||
|
||||
@@ -72,7 +72,7 @@ class RecommendedAd extends MozLitElement {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("AdImpression", {
|
||||
bubbles: true,
|
||||
detail: { aid: this.product.aid },
|
||||
detail: { aid: this.product.aid, sponsored: this.product.sponsored },
|
||||
})
|
||||
);
|
||||
|
||||
@@ -87,7 +87,7 @@ class RecommendedAd extends MozLitElement {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("AdClicked", {
|
||||
bubbles: true,
|
||||
detail: { aid: this.product.aid },
|
||||
detail: { aid: this.product.aid, sponsored: this.product.sponsored },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -572,6 +572,11 @@ shopping:
|
||||
- fx-desktop-shopping-eng@mozilla.com
|
||||
send_in_pings:
|
||||
- events
|
||||
extra_keys:
|
||||
sponsored:
|
||||
description: >
|
||||
Whether the ad was sponsored or not.
|
||||
type: boolean
|
||||
|
||||
surface_ads_impression:
|
||||
type: event
|
||||
@@ -589,6 +594,11 @@ shopping:
|
||||
- fx-desktop-shopping-eng@mozilla.com
|
||||
send_in_pings:
|
||||
- events
|
||||
extra_keys:
|
||||
sponsored:
|
||||
description: >
|
||||
Whether the ad was sponsored or not.
|
||||
type: boolean
|
||||
|
||||
surface_ads_placement:
|
||||
type: event
|
||||
@@ -606,6 +616,11 @@ shopping:
|
||||
- fx-desktop-shopping-eng@mozilla.com
|
||||
send_in_pings:
|
||||
- events
|
||||
extra_keys:
|
||||
sponsored:
|
||||
description: >
|
||||
Whether the ad was sponsored or not.
|
||||
type: boolean
|
||||
|
||||
surface_no_ads_available:
|
||||
type: event
|
||||
|
||||
@@ -7,7 +7,7 @@ const PRODUCT_PAGE = "https://example.com/product/B09TJGHL5F";
|
||||
|
||||
const ADS_JSON = `[{
|
||||
"name": "Test product name ftw",
|
||||
"url": ${PRODUCT_PAGE},
|
||||
"url": "${PRODUCT_PAGE}",
|
||||
"image_url": "https://i.fakespot.io/b6vx27xf3rgwr1a597q6qd3rutp6",
|
||||
"price": "249.99",
|
||||
"currency": "USD",
|
||||
@@ -15,7 +15,20 @@ const ADS_JSON = `[{
|
||||
"adjusted_rating": 4.6,
|
||||
"analysis_url": "https://www.fakespot.com/product/test-product",
|
||||
"sponsored": true,
|
||||
"aid": "a2VlcCBvbiByb2NraW4gdGhlIGZyZWUgd2ViIQ==",
|
||||
"aid": "a2VlcCBvbiByb2NraW4gdGhlIGZyZWUgd2ViIQ=="
|
||||
}]`;
|
||||
|
||||
const RECOMMENDATIONS_JSON = `[{
|
||||
"name": "Test product name ftw",
|
||||
"url": "${PRODUCT_TEST_URL_NOT_SPONSORED}",
|
||||
"image_url": "https://i.fakespot.io/b6vx27xf3rgwr1a597q6qd3rutp6",
|
||||
"price": "249.99",
|
||||
"currency": "USD",
|
||||
"grade": "A",
|
||||
"adjusted_rating": 4.6,
|
||||
"analysis_url": "https://www.fakespot.com/product/test-product",
|
||||
"sponsored": false,
|
||||
"aid": "z2VlcCBvbiByb2NraW4gdGhlIGZyZWUgd2ViIQ=="
|
||||
}]`;
|
||||
|
||||
// Verifies that, if the ads server returns an ad, but we have disabled
|
||||
@@ -54,7 +67,7 @@ add_task(async function test_ads_exposure_disabled_not_recorded() {
|
||||
product,
|
||||
"requestRecommendations"
|
||||
);
|
||||
productRequestAdsStub.resolves(adResponse);
|
||||
productRequestAdsStub.resolves(JSON.parse(adResponse));
|
||||
|
||||
let actor = content.windowGlobalChild.getActor("ShoppingSidebar");
|
||||
actor.productURI = productURI;
|
||||
@@ -174,7 +187,7 @@ add_task(async function test_ads_exposure_enabled_with_ad_recorded() {
|
||||
product,
|
||||
"requestRecommendations"
|
||||
);
|
||||
productRequestAdsStub.resolves(adResponse);
|
||||
productRequestAdsStub.resolves(JSON.parse(adResponse));
|
||||
|
||||
let actor = content.windowGlobalChild.getActor("ShoppingSidebar");
|
||||
actor.productURI = productURI;
|
||||
@@ -211,3 +224,72 @@ add_task(async function test_ads_exposure_enabled_with_ad_recorded() {
|
||||
);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
// Verifies that ads exposure will be recorded for recommendations
|
||||
// that are not sponsored.
|
||||
add_task(async function test_not_sponsored_exposure() {
|
||||
await Services.fog.testFlushAllChildren();
|
||||
Services.fog.testResetFOG();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.shopping.experience2023.ads.enabled", true],
|
||||
["browser.shopping.experience2023.ads.exposure", true],
|
||||
],
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
url: "about:shoppingsidebar",
|
||||
gBrowser,
|
||||
},
|
||||
async browser => {
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[PRODUCT_TEST_URL_NOT_SPONSORED, RECOMMENDATIONS_JSON],
|
||||
async (prodPage, adResponse) => {
|
||||
const { ShoppingProduct } = ChromeUtils.importESModule(
|
||||
"chrome://global/content/shopping/ShoppingProduct.mjs"
|
||||
);
|
||||
const { sinon } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/Sinon.sys.mjs"
|
||||
);
|
||||
|
||||
let productURI = Services.io.newURI(prodPage);
|
||||
let product = new ShoppingProduct(productURI);
|
||||
let productRequestAdsStub = sinon.stub(
|
||||
product,
|
||||
"requestRecommendations"
|
||||
);
|
||||
productRequestAdsStub.resolves(JSON.parse(adResponse));
|
||||
|
||||
let actor = content.windowGlobalChild.getActor("ShoppingSidebar");
|
||||
actor.productURI = productURI;
|
||||
actor.product = product;
|
||||
|
||||
actor.requestRecommendations(productURI);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
await Services.fog.testFlushAllChildren();
|
||||
|
||||
const events = Glean.shopping.adsExposure.testGetValue();
|
||||
Assert.equal(
|
||||
events.length,
|
||||
1,
|
||||
"Ads exposure should have been recorded if ads exposure was enabled and ads were returned"
|
||||
);
|
||||
Assert.equal(
|
||||
events[0].category,
|
||||
"shopping",
|
||||
"Glean event should have category 'shopping'"
|
||||
);
|
||||
Assert.equal(
|
||||
events[0].name,
|
||||
"ads_exposure",
|
||||
"Glean event should have name 'ads_exposure'"
|
||||
);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
@@ -82,6 +82,7 @@ add_task(async function test_ad_attribution() {
|
||||
Assert.equal(adsPlacementEvents.length, 1, "should have recorded an event");
|
||||
Assert.equal(adsPlacementEvents[0].category, "shopping");
|
||||
Assert.equal(adsPlacementEvents[0].name, "surface_ads_placement");
|
||||
Assert.equal(adsPlacementEvents[0].extra.sponsored, "true");
|
||||
|
||||
let impressionEvent = recommendedAdsEventListener("AdImpression", sidebar);
|
||||
|
||||
@@ -100,6 +101,7 @@ add_task(async function test_ad_attribution() {
|
||||
);
|
||||
Assert.equal(adsImpressionEvents[0].category, "shopping");
|
||||
Assert.equal(adsImpressionEvents[0].name, "surface_ads_impression");
|
||||
Assert.equal(adsImpressionEvents[0].extra.sponsored, "true");
|
||||
|
||||
//
|
||||
// Test that impression event is fired after switching to a tab that was
|
||||
@@ -229,8 +231,92 @@ add_task(async function test_ad_attribution() {
|
||||
Assert.equal(adsClickedEvents.length, 1, "should have recorded a click");
|
||||
Assert.equal(adsClickedEvents[0].category, "shopping");
|
||||
Assert.equal(adsClickedEvents[0].name, "surface_ads_clicked");
|
||||
Assert.equal(adsClickedEvents[0].extra.sponsored, "true");
|
||||
|
||||
gBrowser.removeTab(adTab);
|
||||
Services.fog.testResetFOG();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_non_sponsored_attribution() {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
PRODUCT_TEST_URL_NOT_SPONSORED,
|
||||
async browser => {
|
||||
let sidebar = gBrowser
|
||||
.getPanel(browser)
|
||||
.querySelector("shopping-sidebar");
|
||||
|
||||
info("Waiting for sidebar to update.");
|
||||
await promiseSidebarUpdated(sidebar, PRODUCT_TEST_URL_NOT_SPONSORED);
|
||||
await recommendedAdVisible(sidebar);
|
||||
|
||||
// Test placement was recorded by telemetry
|
||||
info("Verifying recommendation placement event.");
|
||||
await Services.fog.testFlushAllChildren();
|
||||
var adsPlacementEvents =
|
||||
Glean.shopping.surfaceAdsPlacement.testGetValue();
|
||||
Assert.equal(
|
||||
adsPlacementEvents.length,
|
||||
1,
|
||||
"should have recorded an event"
|
||||
);
|
||||
Assert.equal(adsPlacementEvents[0].category, "shopping");
|
||||
Assert.equal(adsPlacementEvents[0].name, "surface_ads_placement");
|
||||
Assert.equal(adsPlacementEvents[0].extra?.sponsored, "false");
|
||||
|
||||
let impressionEvent = recommendedAdsEventListener(
|
||||
"AdImpression",
|
||||
sidebar
|
||||
);
|
||||
|
||||
info("Waiting for ad impression event.");
|
||||
await impressionEvent;
|
||||
Assert.ok(true, "Got ad impression event");
|
||||
|
||||
// Test the impression was recorded by telemetry
|
||||
await Services.fog.testFlushAllChildren();
|
||||
var adsImpressionEvents =
|
||||
Glean.shopping.surfaceAdsImpression.testGetValue();
|
||||
Assert.equal(
|
||||
adsImpressionEvents.length,
|
||||
1,
|
||||
"should have recorded an event"
|
||||
);
|
||||
Assert.equal(adsImpressionEvents[0].category, "shopping");
|
||||
Assert.equal(adsImpressionEvents[0].name, "surface_ads_impression");
|
||||
Assert.equal(adsImpressionEvents[0].extra.sponsored, "false");
|
||||
|
||||
// Test ad clicked event
|
||||
let adOpenedTabPromise = BrowserTestUtils.waitForNewTab(
|
||||
gBrowser,
|
||||
PRODUCT_TEST_URL_NOT_SPONSORED,
|
||||
true
|
||||
);
|
||||
|
||||
let clickedEvent = recommendedAdsEventListener("AdClicked", sidebar);
|
||||
await SpecialPowers.spawn(sidebar.querySelector("browser"), [], () => {
|
||||
let shoppingContainer =
|
||||
content.document.querySelector("shopping-container").wrappedJSObject;
|
||||
let adEl = shoppingContainer.recommendedAdEl;
|
||||
adEl.linkEl.click();
|
||||
});
|
||||
|
||||
let adTab = await adOpenedTabPromise;
|
||||
|
||||
info("Waiting for ad clicked event.");
|
||||
await clickedEvent;
|
||||
Assert.ok(true, "Got ad clicked event");
|
||||
|
||||
// Test the click was recorded by telemetry
|
||||
await Services.fog.testFlushAllChildren();
|
||||
var adsClickedEvents = Glean.shopping.surfaceAdsClicked.testGetValue();
|
||||
Assert.equal(adsClickedEvents.length, 1, "should have recorded a click");
|
||||
Assert.equal(adsClickedEvents[0].category, "shopping");
|
||||
Assert.equal(adsClickedEvents[0].name, "surface_ads_clicked");
|
||||
Assert.equal(adsClickedEvents[0].extra.sponsored, "false");
|
||||
|
||||
gBrowser.removeTab(adTab);
|
||||
Services.fog.testResetFOG();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user