summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js
blob: 7e98570b9ef22cd73673565f3188b4c2b839ad6b (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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
// META: script=/resources/testdriver.js
// META: script=/common/utils.js
// META: script=/common/subset-tests.js
// META: script=resources/fledge-util.sub.js
// META: timeout=long
// META: variant=?1-5
// META: variant=?6-10
// META: variant=?11-15
// META: variant=?16-last

"use strict";

// Creates a tracker URL for a component ad. These are fetched from component ad URLs.
function createComponentAdTrackerURL(uuid, id) {
  return createTrackerURL(window.location.origin, uuid, 'track_get',
                          `component_ad_${id}`)
}

// Returns a component ad render URL that fetches the corresponding component ad
// tracker URL.
function createComponentAdRenderURL(uuid, id) {
  return createRenderURL(
      uuid,
      `fetch("${createComponentAdTrackerURL(uuid, id)}");`);
}

// Runs a generic component ad loading test. It joins an interest group with a
// "numComponentAdsInInterestGroup" component ads. The IG will make a bid that
// potentially includes some of them. Then an auction will be run, component
// ads potentially will be loaded in nested fenced frame within the main frame,
// and the test will make sure that each component ad render URL that should have
// been loaded in an iframe was indeed loaded.
//
// Joins an interest group that has "numComponentAdsInInterestGroup" component ads.
//
// "componentAdsInBid" is a list of 0-based indices of which of those ads will be
// included in the bid. It may contain duplicate component ads. If it's null then the
// bid will have no adComponents field, while if it is empty, the bid will have an empty
// adComponents field.
//
// "componentAdsToLoad" is another list of 0-based ad components, but it's the index of
// fenced frame configs in the top frame ad's getNestedConfigs(). It may also contain
// duplicates to load a particular ad twice.
//
// If "adMetadata" is true, metadata is added to each component ad. Only integer metadata
// is used, relying on renderURL tests to cover other types of renderURL metadata.
async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGroup,
                                         componentAdsInBid, componentAdsToLoad,
                                         adMetadata = false) {
  let interestGroupAdComponents = [];
  for (let i = 0; i < numComponentAdsInInterestGroup; ++i) {
    const componentRenderURL = createComponentAdRenderURL(uuid, i);
    let adComponent = {renderURL: componentRenderURL};
    if (adMetadata)
      adComponent.metadata = i;
    interestGroupAdComponents.push(adComponent);
  }

  const renderURL = createRenderURL(
      uuid,
      `// "status" is passed to the beacon URL, to be verified by waitForObservedRequests().
       let status = "ok";
       const componentAds = window.fence.getNestedConfigs()
       if (componentAds.length != 40)
         status = "unexpected getNestedConfigs() length";
       for (let i of ${JSON.stringify(componentAdsToLoad)}) {
         let fencedFrame = document.createElement("fencedframe");
         fencedFrame.mode = "opaque-ads";
         fencedFrame.config = componentAds[i];
         document.body.appendChild(fencedFrame);
       }

       window.fence.reportEvent({eventType: "beacon",
                                 eventData: status,
                                 destination: ["buyer"]});`);

  let bid = {bid:1, render: renderURL};
  if (componentAdsInBid) {
    bid.adComponents = [];
    for (let index of componentAdsInBid) {
      bid.adComponents.push(interestGroupAdComponents[index].renderURL);
    }
  }

  // In these tests, the bidder should always request a beacon URL.
  let expectedTrackerURLs = [`${createBidderBeaconURL(uuid)}, body: ok`];
  // Figure out which, if any, elements of "componentAdsToLoad" correspond to
  // component ads listed in bid.adComponents, and for those ads, add a tracker URL
  // to "expectedTrackerURLs".
  if (componentAdsToLoad && bid.adComponents) {
    for (let index of componentAdsToLoad) {
      if (index < componentAdsInBid.length)
        expectedTrackerURLs.push(createComponentAdTrackerURL(uuid, componentAdsInBid[index]));
    }
  }

  await joinInterestGroup(
      test, uuid,
      { biddingLogicURL:
          createBiddingScriptURL({
              generateBid:
                  `let expectedAdComponents = ${JSON.stringify(interestGroupAdComponents)};
                   let adComponents = interestGroup.adComponents;
                   if (adComponents.length !== expectedAdComponents.length)
                     throw "Unexpected adComponents";
                   for (let i = 0; i < adComponents.length; ++i) {
                    if (adComponents[i].renderURL !== expectedAdComponents[i].renderURL ||
                        adComponents[i].metadata !== expectedAdComponents[i].metadata) {
                      throw "Unexpected adComponents";
                    }
                   }
                   return ${JSON.stringify(bid)}`,
              reportWin:
                  `registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});` }),
        ads: [{renderURL: renderURL}],
        adComponents: interestGroupAdComponents});

  if (!bid.adComponents || bid.adComponents.length === 0) {
    await runBasicFledgeAuctionAndNavigate(
      test, uuid,
      {decisionLogicURL: createDecisionScriptURL(
        uuid,
        { scoreAd: `if (browserSignals.adComponents !== undefined)
                      throw "adComponents should be undefined"`})});
  } else {
    await runBasicFledgeAuctionAndNavigate(
      test, uuid,
      {decisionLogicURL: createDecisionScriptURL(
        uuid,
        { scoreAd:
              `if (JSON.stringify(browserSignals.adComponents) !==
                       '${JSON.stringify(bid.adComponents)}') {
                 throw "Unexpected adComponents: " + JSON.stringify(browserSignals.adComponents);
               }`})});
  }

  await waitForObservedRequests(uuid, expectedTrackerURLs);
}

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);

  const renderURL = createRenderURL(uuid, `let status = "ok";
     const nestedConfigsLength = window.fence.getNestedConfigs().length
     // "getNestedConfigs()" should return a list of 40 configs, to avoid leaking
     // whether there were any component URLs to the page.
     if (nestedConfigsLength != 40)
       status = "unexpected getNestedConfigs() length: " + nestedConfigsLength;
     window.fence.reportEvent({eventType: "beacon",
                               eventData: status,
                               destination: ["buyer"]});`);
  await joinInterestGroup(
      test, uuid,
      { biddingLogicURL:
          createBiddingScriptURL({
              generateBid:
                  'if (interestGroup.componentAds !== undefined) throw "unexpected componentAds"',
              reportWin:
                  `registerAdBeacon({beacon: "${createBidderBeaconURL(uuid)}"});` }),
        ads: [{renderUrl: renderURL}]});
  await runBasicFledgeAuctionAndNavigate(
      test, uuid,
      {decisionLogicURL: createDecisionScriptURL(
        uuid,
        { scoreAd: `if (browserSignals.adComponents !== undefined)
                      throw "adComponents should be undefined"`})});
  await waitForObservedRequests(uuid, [`${createBidderBeaconURL(uuid)}, body: ok`]);
}, 'Group has no component ads, no adComponents in bid.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);

  await joinGroupAndRunBasicFledgeTestExpectingNoWinner(
      test,
      {uuid: uuid,
       interestGroupOverrides: {
           biddingLogicURL:
           createBiddingScriptURL({
               generateBid:
                   `return {bid: 1,
                            render: interestGroup.ads[0].renderUrl,
                            adComponents: []};`})}});
}, 'Group has no component ads, adComponents in bid is empty array.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(
      test, uuid, /*numComponentAdsInInterestGroup=*/2, /*componentAdsInBid=*/null,
      // Try to load ad components, even though there are none. This should load
      // about:blank in those frames, though that's not testible.
      // The waitForObservedRequests() call may see extra requests, racily, if
      // component ads not found in the bid are used.
      /*componentAdsToLoad=*/[0, 1]);
}, 'Group has component ads, but not used in bid (no adComponents field).');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(
      test, uuid, /*numComponentAdsInInterestGroup=*/2, /*componentAdsInBid=*/[],
      // Try to load ad components, even though there are none. This should load
      // about:blank in those frames, though that's not testible.
      // The waitForObservedRequests() call may see extra requests, racily, if
      // component ads not found in the bid are used.
      /*componentAdsToLoad=*/[0, 1]);
}, 'Group has component ads, but not used in bid (adComponents field empty array).');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(
      test, uuid, /*numComponentAdsInInterestGroup=*/2, /*componentAdsInBid=*/null,
      // Try to load ad components, even though there are none. This should load
      // about:blank in those frames, though that's not testible.
      // The waitForObservedRequests() call may see extra requests, racily, if
      // component ads not found in the bid are used.
      /*componentAdsToLoad=*/[0, 1], /*adMetadata=*/true);
}, 'Unused component ads with metadata.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);

  await joinGroupAndRunBasicFledgeTestExpectingNoWinner(
      test,
      { uuid: uuid,
        interestGroupOverrides: {
            biddingLogicURL:
                createBiddingScriptURL({
                    generateBid:
                        `return {bid: 1,
                                 render: interestGroup.ads[0].renderUrl,
                                 adComponents: ["https://random.url.test/"]};`}),
            adComponents: [{renderURL: createComponentAdRenderURL(uuid, 0)}]}});
}, 'Unknown component ad URL in bid.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);

  await joinGroupAndRunBasicFledgeTestExpectingNoWinner(
      test,
      { uuid: uuid,
        interestGroupOverrides: {
            biddingLogicURL:
                createBiddingScriptURL({
                    generateBid:
                        `return {bid: 1,
                                 render: interestGroup.ads[0].renderUrl,
                                 adComponents: [interestGroup.ads[0].renderUrl]};`}),
            adComponents: [{renderURL: createComponentAdRenderURL(uuid, 0)}]}});
}, 'Render URL used as component ad URL in bid.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);

  await joinGroupAndRunBasicFledgeTestExpectingNoWinner(
      test,
      { uuid: uuid,
        interestGroupOverrides: {
            biddingLogicURL:
                createBiddingScriptURL({
                    generateBid:
                        `return {bid: 1, render: interestGroup.adComponents[0].renderURL};`}),
            adComponents: [{renderURL: createComponentAdRenderURL(uuid, 0)}]}});
}, 'Component ad URL used as render URL.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/2,
                                  /*componentAdsInBid=*/[0, 1], /*componentAdsToLoad=*/[0, 1]);
}, '2 of 2 component ads in bid and then shown.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/2,
                                  /*componentAdsInBid=*/[0, 1], /*componentAdsToLoad=*/[0, 1],
                                  /*adMetadata=*/true);
}, '2 of 2 component ads in bid and then shown, with metadata.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/20,
                                  /*componentAdsInBid=*/[3, 10], /*componentAdsToLoad=*/[0, 1]);
}, '2 of 20 component ads in bid and then shown.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  const intsUpTo19 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/20,
                                  /*componentAdsInBid=*/intsUpTo19,
                                  /*componentAdsToLoad=*/intsUpTo19);
}, '20 of 20 component ads in bid and then shown.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  const intsUpTo39 = [];
  for (let i = 0; i < 40; ++i) {
    intsUpTo39.push(i);
  }
  await runComponentAdLoadingTest(
      test, uuid, /*numComponentAdsInInterestGroup=*/ 40,
      /*componentAdsInBid=*/ intsUpTo39,
      /*componentAdsToLoad=*/ intsUpTo39);
}, '40 of 40 component ads in bid and then shown.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/20,
                                  /*componentAdsInBid=*/[1, 2, 3, 4, 5, 6],
                                  /*componentAdsToLoad=*/[1, 3]);
}, '6 of 20 component ads in bid, 2 shown.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  // It should be possible to load ads multiple times. Each loaded ad should request a new tracking
  // URLs, as they're fetched via XHRs, rather than reporting.
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/4,
                                  /*componentAdsInBid=*/[0, 1, 2, 3],
                                  /*componentAdsToLoad=*/[0, 1, 1, 0, 3, 3, 2, 2, 1, 0]);
}, '4 of 4 component ads shown multiple times.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/2,
                                  /*componentAdsInBid=*/[0, 0, 0, 0],
                                  /*componentAdsToLoad=*/[0, 1, 2, 3]);
}, 'Same component ad used multiple times in bid.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  // The bid only has one component ad, but the renderURL tries to load 5 component ads.
  // The others should all be about:blank. Can't test that, so just make sure there aren't
  // more requests than expected, and there's no crash.
  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/2,
                                  /*componentAdsInBid=*/[0],
                                  /*componentAdsToLoad=*/[4, 3, 2, 1, 0]);
}, 'Load component ads not in bid.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  const renderURL = createRenderURL(uuid);

  let adComponents = [];
  let adComponentsList = [];
  for (let i = 0; i < 41; ++i) {
    let componentRenderURL = createComponentAdTrackerURL(uuid, i);
    adComponents.push({renderURL: componentRenderURL});
    adComponentsList.push(componentRenderURL);
  }

  await joinGroupAndRunBasicFledgeTestExpectingNoWinner(
      test,
      { uuid: uuid,
        interestGroupOverrides: {
            biddingLogicURL:
                createBiddingScriptURL({
                    generateBid:
                        `return {bid: 1,
                                 render: "${renderURL}",
                                 adComponents: ${JSON.stringify(adComponentsList)}};`}),
            ads: [{renderURL: renderURL}],
            adComponents: adComponents}});
}, '41 component ads not allowed in bid.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);
  const renderURL = createRenderURL(uuid);

  let adComponents = [];
  let adComponentsList = [];
  for (let i = 0; i < 41; ++i) {
    let componentRenderURL = createComponentAdTrackerURL(uuid, i);
    adComponents.push({renderURL: componentRenderURL});
    adComponentsList.push(adComponents[0].renderURL);
  }

  await joinGroupAndRunBasicFledgeTestExpectingNoWinner(
      test,
      { uuid: uuid,
        interestGroupOverrides: {
            biddingLogicURL:
                createBiddingScriptURL({
            generateBid:
                `return {bid: 1,
                         render: "${renderURL}",
                         adComponents: ${JSON.stringify(adComponentsList)}};`}),
            ads: [{renderURL: renderURL}],
            adComponents: adComponents}});
}, 'Same component ad not allowed 41 times in bid.');

subsetTest(promise_test, async test => {
  const uuid = generateUuid(test);

  // The component ad's render URL will try to send buyer and seller reports,
  // which should not be sent (but not throw an exception), and then request a
  // a tracker URL via fetch, which should be requested from the server.
  const componentRenderURL =
      createRenderURL(
        uuid,
        `window.fence.reportEvent({eventType: "beacon",
                                   eventData: "Should not be sent",
                                   destination: ["buyer", "seller"]});
         fetch("${createComponentAdTrackerURL(uuid, 0)}");`);

  const renderURL = createRenderURL(
      uuid,
      `let fencedFrame = document.createElement("fencedframe");
       fencedFrame.mode = "opaque-ads";
       fencedFrame.config = window.fence.getNestedConfigs()[0];
       document.body.appendChild(fencedFrame);

       async function waitForRequestAndSendBeacons() {
         // Wait for the nested fenced frame to request its tracker URL.
         await waitForObservedRequests("${uuid}", ["${createComponentAdTrackerURL(uuid, 0)}"]);

         // Now that the tracker URL has been received, the component ad has tried to
         // send a beacon, so have the main renderURL send a beacon, which should succeed
         // and should hopefully be sent after the component ad's beacon, if it was
         // going to (incorrectly) send one.
         window.fence.reportEvent({eventType: "beacon",
                                   eventData: "top-ad",
                                   destination: ["buyer", "seller"]});
       }
       waitForRequestAndSendBeacons();`);

  await joinInterestGroup(
      test, uuid,
      { biddingLogicURL:
          createBiddingScriptURL({
              generateBid:
                  `return {bid: 1,
                           render: "${renderURL}",
                           adComponents: ["${componentRenderURL}"]};`,
              reportWin:
                  `registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});` }),
        ads: [{renderURL: renderURL}],
        adComponents: [{renderURL: componentRenderURL}]});

  await runBasicFledgeAuctionAndNavigate(
    test, uuid,
    {decisionLogicURL: createDecisionScriptURL(
        uuid,
        { reportResult: `registerAdBeacon({beacon: '${createSellerBeaconURL(uuid)}'});` }) });

  // Only the renderURL should have sent any beacons, though the component ad should have sent
  // a tracker URL fetch request.
  await waitForObservedRequests(uuid, [createComponentAdTrackerURL(uuid, 0),
                                       `${createBidderBeaconURL(uuid)}, body: top-ad`,
                                       `${createSellerBeaconURL(uuid)}, body: top-ad`]);


}, 'Reports not sent from component ad.');