1 // META: script=/resources/testdriver.js
2 // META: script=/common/utils.js
3 // META: script=resources/fledge-util.js
8 // These tests are focused on joinAdInterestGroup() and leaveAdInterestGroup().
9 // Most join tests do not run auctions, but instead only check the result of
10 // the returned promise, since testing that interest groups are actually
11 // joined, and that each interestGroup field behaves as intended, are covered
14 // Minimal fields needed for a valid interest group. Used in most test cases.
15 const BASE_INTEREST_GROUP = {
16 owner: window.location.origin,
20 // Each test case attempts to join and then leave an interest group, checking
21 // if any exceptions are thrown from either operation.
22 const SIMPLE_JOIN_LEAVE_TEST_CASES = [
23 { expectJoinSucces: false,
24 expectLeaveSucces: false,
27 { expectJoinSucces: false,
28 expectLeaveSucces: false,
32 // Basic success test case.
33 { expectJoinSucces: true,
34 expectLeaveSucces: true,
35 interestGroup: BASE_INTEREST_GROUP
39 { expectJoinSucces: false,
40 expectLeaveSucces: false,
41 interestGroup: { name: 'default name' }
43 { expectJoinSucces: false,
44 expectLeaveSucces: false,
45 interestGroup: { ...BASE_INTEREST_GROUP,
48 { expectJoinSucces: false,
49 expectLeaveSucces: false,
50 interestGroup: { ...BASE_INTEREST_GROUP,
51 owner: window.location.origin.replace('https', 'http')}
53 { expectJoinSucces: false,
54 expectLeaveSucces: false,
55 interestGroup: { ...BASE_INTEREST_GROUP,
56 owner: window.location.origin.replace('https', 'wss')}
58 // Cross-origin joins and leaves are not allowed without .well-known
60 { expectJoinSucces: false,
61 expectLeaveSucces: false,
62 interestGroup: { ...BASE_INTEREST_GROUP,
63 owner: '{{hosts[][www]}}' }
67 { expectJoinSucces: false,
68 expectLeaveSucces: false,
69 interestGroup: { owner: window.location.origin }
71 { expectJoinSucces: true,
72 expectLeaveSucces: true,
73 interestGroup: { ...BASE_INTEREST_GROUP,
78 { expectJoinSucces: true,
79 expectLeaveSucces: true,
80 interestGroup: { ...BASE_INTEREST_GROUP,
83 { expectJoinSucces: true,
84 expectLeaveSucces: true,
85 interestGroup: { ...BASE_INTEREST_GROUP,
88 { expectJoinSucces: true,
89 expectLeaveSucces: true,
90 interestGroup: { ...BASE_INTEREST_GROUP,
94 // "priorityVector" tests
95 { expectJoinSucces: false,
96 expectLeaveSucces: true,
97 interestGroup: { ...BASE_INTEREST_GROUP,
100 { expectJoinSucces: false,
101 expectLeaveSucces: true,
102 interestGroup: { ...BASE_INTEREST_GROUP,
105 { expectJoinSucces: false,
106 expectLeaveSucces: true,
107 interestGroup: { ...BASE_INTEREST_GROUP,
108 priorityVector: {a: 'apple'}}
110 { expectJoinSucces: true,
111 expectLeaveSucces: true,
112 interestGroup: { ...BASE_INTEREST_GROUP,
115 { expectJoinSucces: true,
116 expectLeaveSucces: true,
117 interestGroup: { ...BASE_INTEREST_GROUP,
118 priorityVector: {a: 1}}
120 { expectJoinSucces: true,
121 expectLeaveSucces: true,
122 interestGroup: { ...BASE_INTEREST_GROUP,
123 priorityVector: {'a': 1, 'b': -4.5, 'a.b': 0}}
126 // "prioritySignalsOverrides" tests
127 { expectJoinSucces: false,
128 expectLeaveSucces: true,
129 interestGroup: { ...BASE_INTEREST_GROUP,
130 prioritySignalsOverrides: null}
132 { expectJoinSucces: false,
133 expectLeaveSucces: true,
134 interestGroup: { ...BASE_INTEREST_GROUP,
135 prioritySignalsOverrides: 1}
137 { expectJoinSucces: false,
138 expectLeaveSucces: true,
139 interestGroup: { ...BASE_INTEREST_GROUP,
140 prioritySignalsOverrides: {a: 'apple'}}
142 { expectJoinSucces: true,
143 expectLeaveSucces: true,
144 interestGroup: { ...BASE_INTEREST_GROUP,
145 prioritySignalsOverrides: {}}
147 { expectJoinSucces: true,
148 expectLeaveSucces: true,
149 interestGroup: { ...BASE_INTEREST_GROUP,
150 prioritySignalsOverrides: {a: 1}}
152 { expectJoinSucces: true,
153 expectLeaveSucces: true,
154 interestGroup: { ...BASE_INTEREST_GROUP,
155 prioritySignalsOverrides: {'a': 1, 'b': -4.5, 'a.b': 0}}
158 // "enableBiddingSignalsPrioritization" tests
159 { expectJoinSucces: true,
160 expectLeaveSucces: true,
161 interestGroup: { ...BASE_INTEREST_GROUP,
162 enableBiddingSignalsPrioritization: true}
164 { expectJoinSucces: true,
165 expectLeaveSucces: true,
166 interestGroup: { ...BASE_INTEREST_GROUP,
167 enableBiddingSignalsPrioritization: false}
170 // "biddingLogicUrl" tests
171 { expectJoinSucces: true,
172 expectLeaveSucces: true,
173 interestGroup: { ...BASE_INTEREST_GROUP,
174 biddingLogicUrl: null }
176 { expectJoinSucces: false,
177 expectLeaveSucces: true,
178 interestGroup: { ...BASE_INTEREST_GROUP,
179 biddingLogicUrl: 'https://{{hosts[][www]}}/foo.js' }
181 { expectJoinSucces: false,
182 expectLeaveSucces: true,
183 interestGroup: { ...BASE_INTEREST_GROUP,
184 biddingLogicUrl: 'data:text/javascript,Foo' }
186 { expectJoinSucces: true,
187 expectLeaveSucces: true,
188 interestGroup: { ...BASE_INTEREST_GROUP,
189 biddingLogicUrl: `${window.location.origin}/foo.js`}
191 { expectJoinSucces: true,
192 expectLeaveSucces: true,
193 interestGroup: { ...BASE_INTEREST_GROUP,
194 biddingLogicUrl: 'relative/path' }
197 // "biddingWasmHelperUrl" tests
198 { expectJoinSucces: true,
199 expectLeaveSucces: true,
200 interestGroup: { ...BASE_INTEREST_GROUP,
201 biddingWasmHelperUrl: null }
203 { expectJoinSucces: false,
204 expectLeaveSucces: true,
205 interestGroup: { ...BASE_INTEREST_GROUP,
206 biddingWasmHelperUrl: 'https://{{hosts[][www]}}/foo.js' }
208 { expectJoinSucces: false,
209 expectLeaveSucces: true,
210 interestGroup: { ...BASE_INTEREST_GROUP,
211 biddingWasmHelperUrl: 'data:application/wasm,Foo' }
213 { expectJoinSucces: true,
214 expectLeaveSucces: true,
215 interestGroup: { ...BASE_INTEREST_GROUP,
216 biddingWasmHelperUrl: `${window.location.origin}/foo.js`}
218 { expectJoinSucces: true,
219 expectLeaveSucces: true,
220 interestGroup: { ...BASE_INTEREST_GROUP,
221 biddingWasmHelperUrl: 'relative/path' }
224 // "dailyUpdateUrl" tests
225 { expectJoinSucces: true,
226 expectLeaveSucces: true,
227 interestGroup: { ...BASE_INTEREST_GROUP,
228 dailyUpdateUrl: null }
230 { expectJoinSucces: false,
231 expectLeaveSucces: true,
232 interestGroup: { ...BASE_INTEREST_GROUP,
233 dailyUpdateUrl: 'https://{{hosts[][www]}}/foo.js' }
235 { expectJoinSucces: false,
236 expectLeaveSucces: true,
237 interestGroup: { ...BASE_INTEREST_GROUP,
238 dailyUpdateUrl: 'data:application/wasm,Foo' }
240 { expectJoinSucces: true,
241 expectLeaveSucces: true,
242 interestGroup: { ...BASE_INTEREST_GROUP,
243 dailyUpdateUrl: `${window.location.origin}/foo.js`}
245 { expectJoinSucces: true,
246 expectLeaveSucces: true,
247 interestGroup: { ...BASE_INTEREST_GROUP,
248 dailyUpdateUrl: 'relative/path' }
251 // "executionMode" tests
252 { expectJoinSucces: true,
253 expectLeaveSucces: true,
254 interestGroup: { ...BASE_INTEREST_GROUP,
255 executionMode: 'compatibility' }
257 { expectJoinSucces: true,
258 expectLeaveSucces: true,
259 interestGroup: { ...BASE_INTEREST_GROUP,
260 executionMode: 'groupByOrigin' }
262 { expectJoinSucces: true,
263 expectLeaveSucces: true,
264 interestGroup: { ...BASE_INTEREST_GROUP,
265 executionMode: 'unknownValuesAreValid' }
268 // "trustedBiddingSignalsUrl" tests
269 { expectJoinSucces: true,
270 expectLeaveSucces: true,
271 interestGroup: { ...BASE_INTEREST_GROUP,
272 trustedBiddingSignalsUrl: null }
274 { expectJoinSucces: false,
275 expectLeaveSucces: true,
276 interestGroup: { ...BASE_INTEREST_GROUP,
277 trustedBiddingSignalsUrl: 'https://{{hosts[][www]}}/foo.js' }
279 { expectJoinSucces: false,
280 expectLeaveSucces: true,
281 interestGroup: { ...BASE_INTEREST_GROUP,
282 trustedBiddingSignalsUrl: 'data:application/json,{}' }
284 { expectJoinSucces: true,
285 expectLeaveSucces: true,
286 interestGroup: { ...BASE_INTEREST_GROUP,
287 trustedBiddingSignalsUrl: `${window.location.origin}/foo.js`}
289 { expectJoinSucces: true,
290 expectLeaveSucces: true,
291 interestGroup: { ...BASE_INTEREST_GROUP,
292 trustedBiddingSignalsUrl: 'relative/path' }
295 // "trustedBiddingSignalsKeys" tests
296 { expectJoinSucces: false,
297 expectLeaveSucces: true,
298 interestGroup: { ...BASE_INTEREST_GROUP,
299 trustedBiddingSignalsKeys: null }
301 { expectJoinSucces: false,
302 expectLeaveSucces: true,
303 interestGroup: { ...BASE_INTEREST_GROUP,
304 trustedBiddingSignalsKeys: {}}
306 { expectJoinSucces: true,
307 expectLeaveSucces: true,
308 interestGroup: { ...BASE_INTEREST_GROUP,
309 trustedBiddingSignalsKeys: []}
311 { expectJoinSucces: true,
312 expectLeaveSucces: true,
313 interestGroup: { ...BASE_INTEREST_GROUP,
314 trustedBiddingSignalsKeys: ['a', 4, 'Foo']}
317 // "userBiddingSignals" tests
318 { expectJoinSucces: true,
319 expectLeaveSucces: true,
320 interestGroup: { ...BASE_INTEREST_GROUP,
321 userBiddingSignals: null }
323 { expectJoinSucces: true,
324 expectLeaveSucces: true,
325 interestGroup: { ...BASE_INTEREST_GROUP,
326 userBiddingSignals: 'foo' }
328 { expectJoinSucces: true,
329 expectLeaveSucces: true,
330 interestGroup: { ...BASE_INTEREST_GROUP,
331 userBiddingSignals: 15 }
333 { expectJoinSucces: true,
334 expectLeaveSucces: true,
335 interestGroup: { ...BASE_INTEREST_GROUP,
336 userBiddingSignals: [5, 'foo', [-6.4, {a: 'b'}]] }
338 { expectJoinSucces: true,
339 expectLeaveSucces: true,
340 interestGroup: { ...BASE_INTEREST_GROUP,
341 userBiddingSignals: {a: [5, 'foo', {b: -6.4}] }}
345 { expectJoinSucces: false,
346 expectLeaveSucces: true,
347 interestGroup: { ...BASE_INTEREST_GROUP,
350 { expectJoinSucces: false,
351 expectLeaveSucces: true,
352 interestGroup: { ...BASE_INTEREST_GROUP,
355 { expectJoinSucces: false,
356 expectLeaveSucces: true,
357 interestGroup: { ...BASE_INTEREST_GROUP,
360 { expectJoinSucces: true,
361 expectLeaveSucces: true,
362 interestGroup: { ...BASE_INTEREST_GROUP,
365 { expectJoinSucces: false,
366 expectLeaveSucces: true,
367 interestGroup: { ...BASE_INTEREST_GROUP,
370 { expectJoinSucces: false,
371 expectLeaveSucces: true,
372 interestGroup: { ...BASE_INTEREST_GROUP,
373 ads: [{metadata: [{a:'b'}, 'c'], 1:[2,3]}] }
375 { expectJoinSucces: true,
376 expectLeaveSucces: true,
377 interestGroup: { ...BASE_INTEREST_GROUP,
378 ads: [{renderUrl: 'https://somewhere.test/'}] }
380 { expectJoinSucces: true,
381 expectLeaveSucces: true,
382 interestGroup: { ...BASE_INTEREST_GROUP,
383 ads: [{renderUrl: 'https://somewhere.test/'},
384 {renderUrl: 'https://somewhere-else.test/'}] }
386 { expectJoinSucces: true,
387 expectLeaveSucces: true,
388 interestGroup: { ...BASE_INTEREST_GROUP,
389 ads: [{renderUrl: 'https://somewhere.test/',
392 { expectJoinSucces: true,
393 expectLeaveSucces: true,
394 interestGroup: { ...BASE_INTEREST_GROUP,
395 ads: [{renderUrl: 'https://somewhere.test/',
397 someOtherField: 'foo'}] }
400 // "adComponents" tests
401 { expectJoinSucces: false,
402 expectLeaveSucces: true,
403 interestGroup: { ...BASE_INTEREST_GROUP,
406 { expectJoinSucces: false,
407 expectLeaveSucces: true,
408 interestGroup: { ...BASE_INTEREST_GROUP,
411 { expectJoinSucces: false,
412 expectLeaveSucces: true,
413 interestGroup: { ...BASE_INTEREST_GROUP,
416 { expectJoinSucces: true,
417 expectLeaveSucces: true,
418 interestGroup: { ...BASE_INTEREST_GROUP,
421 { expectJoinSucces: false,
422 expectLeaveSucces: true,
423 interestGroup: { ...BASE_INTEREST_GROUP,
426 { expectJoinSucces: false,
427 expectLeaveSucces: true,
428 interestGroup: { ...BASE_INTEREST_GROUP,
429 adComponents: [{metadata: [{a:'b'}, 'c'], 1:[2,3]}] }
431 { expectJoinSucces: true,
432 expectLeaveSucces: true,
433 interestGroup: { ...BASE_INTEREST_GROUP,
434 adComponents: [{renderUrl: 'https://somewhere.test/'}] }
436 { expectJoinSucces: true,
437 expectLeaveSucces: true,
438 interestGroup: { ...BASE_INTEREST_GROUP,
439 adComponents: [{renderUrl: 'https://somewhere.test/'},
440 {renderUrl: 'https://elsewhere.test/'}] }
442 { expectJoinSucces: true,
443 expectLeaveSucces: true,
444 interestGroup: { ...BASE_INTEREST_GROUP,
445 adComponents: [{renderUrl: 'https://somewhere.test/',
448 { expectJoinSucces: true,
449 expectLeaveSucces: true,
450 interestGroup: { ...BASE_INTEREST_GROUP,
451 adComponents: [{renderUrl: 'https://somewhere.test/',
453 someOtherField: 'foo'}] }
456 // Miscellaneous tests.
457 { expectJoinSucces: true,
458 expectLeaveSucces: true,
459 interestGroup: { ...BASE_INTEREST_GROUP,
466 // Interest group dictionaries must be less than 1 MB (1048576 bytes), so
467 // test that here by using a large name on an otherwise valid interest group
468 // dictionary. The first case is the largest name value that still results in
469 // a valid dictionary, whereas the second test case produces a dictionary
470 // that's one byte too large.
471 { expectJoinSucces: true,
472 expectLeaveSucces: true,
473 interestGroup: { ...BASE_INTEREST_GROUP,
474 name: 'a'.repeat(1048528) },
475 testCaseName: "Largest possible interest group dictionary",
477 { expectJoinSucces: false,
478 expectLeaveSucces: true,
479 interestGroup: { ...BASE_INTEREST_GROUP,
480 name: 'a'.repeat(1048529) },
481 testCaseName: "Oversized interest group dictionary",
485 for (testCase of SIMPLE_JOIN_LEAVE_TEST_CASES) {
486 var test_name = 'Join and leave interest group: ';
487 if ('testCaseName' in testCase) {
488 test_name += testCase.testCaseName;
490 test_name += JSON.stringify(testCase);
493 promise_test((async (testCase) => {
494 const INTEREST_GROUP_LIFETIME_SECS = 1;
496 let join_promise = navigator.joinAdInterestGroup(testCase.interestGroup,
497 INTEREST_GROUP_LIFETIME_SECS);
498 assert_true(join_promise instanceof Promise, "join should return a promise");
499 if (testCase.expectJoinSucces) {
500 assert_equals(await join_promise, undefined);
502 let joinExceptionThrown = false;
506 joinExceptionThrown = true;
508 assert_true(joinExceptionThrown, 'Exception not thrown on join.');
511 let leave_promise = navigator.leaveAdInterestGroup(testCase.interestGroup);
512 assert_true(leave_promise instanceof Promise, "leave should return a promise");
513 if (testCase.expectLeaveSucces) {
514 assert_equals(await leave_promise, undefined);
516 let leaveExceptionThrown = false;
520 leaveExceptionThrown = true;
522 assert_true(leaveExceptionThrown, 'Exception not thrown on leave.');
524 }).bind(undefined, testCase), test_name);
527 promise_test(async test => {
528 const uuid = generateUuid(test);
530 // Joining an interest group without a bidding script and run an auction.
531 // There should be no winner.
532 await joinInterestGroup(test, uuid, { biddingLogicUrl: null });
533 assert_equals(null, await runBasicFledgeAuction(test, uuid),
534 'Auction unexpectedly had a winner');
536 // Joining an interest group with a bidding script and the same owner/name as
537 // the previously joined interest group, and re-run the auction. There should
538 // be a winner this time.
539 await joinInterestGroup(test, uuid);
540 let config = await runBasicFledgeAuction(test, uuid);
541 assert_true(config instanceof FencedFrameConfig,
542 'Wrong value type returned from auction: ' +
543 config.constructor.name);
545 // Re-join the first interest group, and re-run the auction. The interest
546 // group should be overwritten again, and there should be no winner.
547 await joinInterestGroup(test, uuid, { biddingLogicUrl: null });
548 assert_equals(null, await runBasicFledgeAuction(test, uuid),
549 'Auction unexpectedly had a winner');
550 }, 'Join same interest group overwrites old matching group.');
552 promise_test(async test => {
553 const uuid = generateUuid(test);
555 // Join an interest group, run an auction to make sure it was joined.
556 await joinInterestGroup(test, uuid);
557 let config = await runBasicFledgeAuction(test, uuid);
558 assert_true(config instanceof FencedFrameConfig,
559 'Wrong value type returned from auction: ' +
560 config.constructor.name);
562 // Leave the interest group, re-run the auction. There should be no winner.
563 await leaveInterestGroup();
564 assert_equals(null, await runBasicFledgeAuction(test, uuid),
565 'Auction unexpectedly had a winner');
566 }, 'Leaving interest group actually leaves interest group.');
568 promise_test(async test => {
569 // This should not throw.
570 await leaveInterestGroup({ name: 'Never join group' });
571 }, 'Leave an interest group that was never joined.');
573 ///////////////////////////////////////////////////////////////////////////////
575 ///////////////////////////////////////////////////////////////////////////////
577 promise_test(async test => {
578 const uuid = generateUuid(test);
580 // Joins the default interest group, with a 0.2 second duration.
581 await joinInterestGroup(test, uuid, {}, 0.2);
583 // Keep on running auctions until interest group duration expires.
584 // Unfortunately, there's no duration that's guaranteed to be long enough to
585 // be be able to win an auction once, but short enough to prevent this test
586 // from running too long, so can't check the interest group won at least one
588 while (await runBasicFledgeAuction(test, uuid) !== null);
589 }, 'Interest group duration.');
591 promise_test(async test => {
592 const uuid = generateUuid(test);
594 // Join interest group with a duration of -600. The interest group should
595 // immediately expire, and not be allowed to participate in auctions.
596 await joinInterestGroup(test, uuid, {}, -600);
597 assert_true(await runBasicFledgeAuction(test, uuid) === null);
598 }, 'Interest group duration of -600.');
600 promise_test(async test => {
601 const uuid = generateUuid(test);
603 // Join a long-lived interest group.
604 await joinInterestGroup(test, uuid, {}, 600);
606 // Make sure interest group with a non-default timeout was joined.
607 assert_true(await runBasicFledgeAuction(test, uuid) !== null);
609 // Re-join interest group with a duration value of 0.2 seconds.
610 await joinInterestGroup(test, uuid, {}, 0.2);
612 // Keep on running auctions until interest group expires.
613 while (await runBasicFledgeAuction(test, uuid) !== null);
614 }, 'Interest group test with overwritten duration.');
616 promise_test(async test => {
617 const uuid = generateUuid(test);
619 // Join a long-lived interest group.
620 await joinInterestGroup(test, uuid, {}, 600);
622 // Re-join interest group with a duration value of 0.2 seconds. The new
623 // duration should take precedence, and the interest group should immediately
625 await joinInterestGroup(test, uuid, {}, -600);
626 assert_true(await runBasicFledgeAuction(test, uuid) === null);
627 }, 'Interest group test with overwritten duration of -600.');