summaryrefslogtreecommitdiffstats
path: root/toolkit/components/shopping/test/browser/browser_shopping_ads_test.js
blob: 05426a08ff5d2240174c2a0e83ae263f77559507 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { sinon } = ChromeUtils.importESModule(
  "resource://testing-common/Sinon.sys.mjs"
);

function recommendedAdsEventListener(eventName, sidebar) {
  return SpecialPowers.spawn(
    sidebar.querySelector("browser"),
    [eventName],
    name => {
      let shoppingContainer =
        content.document.querySelector("shopping-container").wrappedJSObject;
      let adEl = shoppingContainer.recommendedAdEl;
      return ContentTaskUtils.waitForEvent(adEl, name, false, null, true).then(
        () => null
      );
    }
  );
}

function recommendedAdVisible(sidebar) {
  return SpecialPowers.spawn(sidebar.querySelector("browser"), [], async () => {
    await ContentTaskUtils.waitForCondition(() => {
      let shoppingContainer =
        content.document.querySelector("shopping-container").wrappedJSObject;
      return (
        shoppingContainer?.recommendedAdEl &&
        ContentTaskUtils.isVisible(shoppingContainer?.recommendedAdEl)
      );
    });
  });
}

add_setup(async function () {
  await Services.fog.testFlushAllChildren();
  Services.fog.testResetFOG();

  await SpecialPowers.pushPrefEnv({
    set: [
      ["toolkit.shopping.ohttpRelayURL", ""],
      ["toolkit.shopping.ohttpConfigURL", ""],
      ["browser.shopping.experience2023.ads.enabled", true],
      ["browser.shopping.experience2023.ads.userEnabled", true],
    ],
  });
});

add_task(async function test_ad_attribution() {
  await BrowserTestUtils.withNewTab(PRODUCT_TEST_URL, async browser => {
    // Test that impression event is fired when opening sidebar
    let sidebar = gBrowser.getPanel(browser).querySelector("shopping-sidebar");
    Assert.ok(sidebar, "Sidebar should exist");
    Assert.ok(
      BrowserTestUtils.isVisible(sidebar),
      "Sidebar should be visible."
    );
    let shoppingButton = document.getElementById("shopping-sidebar-button");
    ok(
      BrowserTestUtils.isVisible(shoppingButton),
      "Shopping Button should be visible on a product page"
    );

    info("Waiting for sidebar to update.");
    await promiseSidebarUpdated(sidebar, PRODUCT_TEST_URL);
    await recommendedAdVisible(sidebar);

    info("Verifying product info for initial product.");
    await verifyProductInfo(sidebar, {
      productURL: PRODUCT_TEST_URL,
      adjustedRating: "4.1",
      letterGrade: "B",
    });

    // Test placement was recorded by telemetry
    info("Verifying ad 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");

    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");

    //
    // Test that impression event is fired after switching to a tab that was
    // opened in the background

    let tab = BrowserTestUtils.addTab(gBrowser, PRODUCT_TEST_URL);
    await BrowserTestUtils.browserLoaded(tab.linkedBrowser);

    let tabSidebar = gBrowser
      .getPanel(tab.linkedBrowser)
      .querySelector("shopping-sidebar");
    Assert.ok(tabSidebar, "Sidebar should exist");

    info("Waiting for sidebar to update.");
    await promiseSidebarUpdated(tabSidebar, PRODUCT_TEST_URL);
    await recommendedAdVisible(tabSidebar);

    // Need to wait the impression timeout to confirm that no impression event
    // has been dispatched
    // Bug 1859029 should update this to use sinon fake timers instead of using
    // setTimeout
    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    await new Promise(r => setTimeout(r, 2000));

    let hasImpressed = await SpecialPowers.spawn(
      tabSidebar.querySelector("browser"),
      [],
      () => {
        let shoppingContainer =
          content.document.querySelector("shopping-container").wrappedJSObject;
        let adEl = shoppingContainer.recommendedAdEl;
        return adEl.hasImpressed;
      }
    );
    Assert.ok(!hasImpressed, "We haven't seend the ad yet");

    impressionEvent = recommendedAdsEventListener("AdImpression", tabSidebar);
    await BrowserTestUtils.switchTab(gBrowser, tab);
    await recommendedAdVisible(tabSidebar);

    info("Waiting for ad impression event.");
    await impressionEvent;
    Assert.ok(true, "Got ad impression event");

    //
    // Test that the impression event is fired after opening foreground tab,
    // switching away and the event is not fired, then switching back and the
    // event does fire

    gBrowser.removeTab(tab);

    tab = BrowserTestUtils.addTab(gBrowser, PRODUCT_TEST_URL);
    await BrowserTestUtils.browserLoaded(tab.linkedBrowser);

    tabSidebar = gBrowser
      .getPanel(tab.linkedBrowser)
      .querySelector("shopping-sidebar");
    Assert.ok(tabSidebar, "Sidebar should exist");

    info("Waiting for sidebar to update.");
    await promiseSidebarUpdated(tabSidebar, PRODUCT_TEST_URL);
    await recommendedAdVisible(tabSidebar);

    // Switch to new sidebar tab
    await BrowserTestUtils.switchTab(gBrowser, tab);
    // switch back to original tab
    await BrowserTestUtils.switchTab(
      gBrowser,
      gBrowser.getTabForBrowser(browser)
    );

    // Need to wait the impression timeout to confirm that no impression event
    // has been dispatched
    // Bug 1859029 should update this to use sinon fake timers instead of using
    // setTimeout
    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    await new Promise(r => setTimeout(r, 2000));

    hasImpressed = await SpecialPowers.spawn(
      tabSidebar.querySelector("browser"),
      [],
      () => {
        let shoppingContainer =
          content.document.querySelector("shopping-container").wrappedJSObject;
        let adEl = shoppingContainer.recommendedAdEl;
        return adEl.hasImpressed;
      }
    );
    Assert.ok(!hasImpressed, "We haven't seend the ad yet");

    impressionEvent = recommendedAdsEventListener("AdImpression", tabSidebar);
    await BrowserTestUtils.switchTab(gBrowser, tab);
    await recommendedAdVisible(tabSidebar);

    info("Waiting for ad impression event.");
    await impressionEvent;
    Assert.ok(true, "Got ad impression event");

    gBrowser.removeTab(tab);

    //
    // Test ad clicked event

    let adOpenedTabPromise = BrowserTestUtils.waitForNewTab(
      gBrowser,
      PRODUCT_TEST_URL,
      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");

    gBrowser.removeTab(adTab);
    Services.fog.testResetFOG();
  });
});