// META: script=/resources/testdriver.js // META: script=/resources/testdriver-vendor.js // META: script=/common/utils.js // META: script=resources/ba-fledge-util.sub.js // META: script=resources/fledge-util.sub.js // META: script=third_party/cbor-js/cbor.js // META: script=/common/subset-tests.js // META: timeout=long // META: variant=?1-4 // META: variant=?5-8 // META: variant=?9-12 // META: variant=?13-16 // META: variant=?17-last "use strict"; // These tests focus on the navigator.getInterestGroupAdAuctionData() method. // Checks various fields for decoded InterestGroupAdAuctionData that's // one IG owned by this origin, and returns that IG's info. function validateWithOneIg(decoded) { assert_equals(decoded.message.version, 0); assert_equals(decoded.message.publisher, window.location.hostname); assert_equals(typeof decoded.message.generationId, 'string'); let igMapKeys = Object.getOwnPropertyNames(decoded.message.interestGroups); assert_array_equals(igMapKeys, [window.location.origin]); let igInfo = decoded.message.interestGroups[window.location.origin]; assert_true(igInfo instanceof Array); assert_equals(igInfo.length, 1, 'number of IGs'); return igInfo[0]; } subsetTest(promise_test, async test => { const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length === 0); }, 'getInterestGroupAdAuctionData() with no interest groups returns a zero length result.'); async function testInvalidConfig(test, configObj, desc) { if (!configObj.coordinatorOrigin) { configObj.coordinatorOrigin = await BA.configureCoordinator(); } await promise_rejects_js( test, TypeError, navigator.getInterestGroupAdAuctionData(configObj), desc); } subsetTest(promise_test, async test => { await testInvalidConfig(test, {}, 'no seller'); await testInvalidConfig(test, {seller: 'example'}, 'invalid seller 1'); await testInvalidConfig( test, {seller: 'http://example.org'}, 'invalid seller 2'); await testInvalidConfig( test, {seller: 'https://example.org', coordinatorOrigin: 'example'}, 'invalid coordinator 1'); await testInvalidConfig( test, {seller: 'https://example.org', coordinatorOrigin: 'example.org'}, 'invalid coordinator 2'); await testInvalidConfig( test, {seller: 'https://example.org', perBuyerConfig: {'a': {}}}, 'invalid buyer 1'); await testInvalidConfig( test, {seller: 'https://example.org', perBuyerConfig: {'http://a.com': {}}}, 'invalid buyer 2'); await testInvalidConfig( test, { seller: 'https://example.org', perBuyerConfig: {'https://a.com': {}, 'http://b.com': {}} }, 'invalid buyer 3'); await testInvalidConfig( test, { seller: 'https://example.org', perBuyerConfig: {'https://a.com': {}, 'https://b.com': {}} }, 'missing size info w/per-buyer config 1'); await testInvalidConfig( test, { seller: 'https://example.org', perBuyerConfig: {'https://a.com': {targetSize: 400}, 'https://b.com': {}} }, 'missing size info w/per-buyer config 2'); // These two actually succeed. let result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: 'https://example.org', perBuyerConfig: {'https://a.com': {targetSize: 400}, 'https://b.com': {targetSize: 500}} }); assert_true(result.requestId !== null); result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: 'https://example.org', perBuyerConfig: {'https://a.com': {targetSize: 400}, 'https://b.com': {}}, requestSize: 5000 }); assert_true(result.requestId !== null); }, 'getInterestGroupAdAuctionData() config checks'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); await joinInterestGroup(test, uuid); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); // Check that the required field and one IG (but no ad data) are here, // since we neither provided adRenderIds or asked for full data to be // included. let ig = validateWithOneIg(decoded); assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_array_equals(ig.ads, []); assert_equals(ig.browserSignals.joinCount, 1, 'joinCount'); assert_equals(ig.browserSignals.bidCount, 0, 'bidCount'); assert_array_equals(ig.browserSignals.prevWins, []); }, 'getInterestGroupAdAuctionData() with one interest group returns a valid result.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); await joinInterestGroup(test, uuid, { ads: [ {renderURL: createRenderURL(uuid) + '&a', adRenderId: 'a'}, {renderURL: createRenderURL(uuid) + '&a', adRenderId: 'b'} ] }); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); // This ig should have two ads with adRenderIds, but not URLs. assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_array_equals(ig.ads, ['a', 'b']); assert_equals(ig.browserSignals.joinCount, 1, 'joinCount'); assert_equals(ig.browserSignals.bidCount, 0, 'bidCount'); assert_array_equals(ig.browserSignals.prevWins, []); }, 'getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); await joinInterestGroup(test, uuid, { auctionServerRequestFlags: ['omit-ads'], ads: [ {renderURL: createRenderURL(uuid) + '&a', adRenderId: 'a'}, {renderURL: createRenderURL(uuid) + '&a', adRenderId: 'b'} ] }); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_not_own_property(ig, 'ads', 'no ads expected'); assert_equals(ig.browserSignals.joinCount, 1, 'joinCount'); }, 'getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds and omit-ads.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const adsArray = [ {renderURL: createRenderURL(uuid) + '&a', adRenderId: 'a'}, {renderURL: createRenderURL(uuid) + '&a', adRenderId: 'b'} ]; await joinInterestGroup( test, uuid, {auctionServerRequestFlags: ['include-full-ads'], ads: adsArray}); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); // Since include-full-ads is on, this gets entire objects, not just // adRenderId. assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_equals(ig.ads.length, 2, '# of ads'); assert_equals(ig.ads[0].renderURL, adsArray[0].renderURL, 'renderURL 0'); assert_equals(ig.ads[1].renderURL, adsArray[1].renderURL, 'renderURL 1'); assert_equals(ig.ads[0].adRenderId, adsArray[0].adRenderId, 'adRenderId 0'); assert_equals(ig.ads[1].adRenderId, adsArray[1].adRenderId, 'adRenderId 1'); assert_equals(ig.browserSignals.joinCount, 1, 'joinCount'); }, 'getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds and include-full-ads.'); // Returns an AuctionAdInterestGroup that sets all fields that can be exported // via getInterestGroupAdAuctionData(). function makeTemplateIgConfig(uuid) { const adsArray = [ { renderURL: createRenderURL(uuid) + '&a', adRenderId: 'a', metadata: 'ada', sizeGroup: 'small' }, { renderURL: createRenderURL(uuid) + '&b', adRenderId: 'b', metadata: 'adb', sizeGroup: 'big' } ]; const adComponentsArray = [ { renderURL: 'https://example.org/ca', adRenderId: 'ca', metadata: 'compa', sizeGroup: 'big' }, { renderURL: 'https://example.org/cb', adRenderId: 'cb', metadata: 'compb', sizeGroup: 'small' }, { renderURL: 'https://example.org/cc', adRenderId: 'cc', metadata: 'compc', sizeGroup: 'big' }, ]; return { ads: adsArray, adComponents: adComponentsArray, adSizes: { 's': {width: '100px', height: '30px'}, 'xl': {width: '1000px', height: '300px'} }, sizeGroups: {'small': ['s'], 'big': ['xl']}, trustedBiddingSignalsKeys: ['alpha', 'beta'], userBiddingSignals: 14 }; } subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); igConfig.auctionServerRequestFlags = ['include-full-ads']; await joinInterestGroup(test, uuid, igConfig); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_true(ig.ads instanceof Array); assert_equals(ig.ads.length, 2, '# of ads'); assert_equals(ig.ads[0].renderURL, igConfig.ads[0].renderURL, 'renderURL 0'); assert_equals(ig.ads[1].renderURL, igConfig.ads[1].renderURL, 'renderURL 1'); assert_equals(ig.ads[0].adRenderId, 'a', 'adRenderId 0'); assert_equals(ig.ads[1].adRenderId, 'b', 'adRenderId 1'); assert_equals(ig.ads[0].metadata, '"ada"', 'metadata 0'); assert_equals(ig.ads[1].metadata, '"adb"', 'metadata 1'); assert_equals(ig.ads[0].sizeGroup, 'small', 'sizegroup 0'); assert_equals(ig.ads[1].sizeGroup, 'big', 'sizegroup 1'); assert_true(ig.components instanceof Array); assert_equals(ig.components.length, 3, '# of component ads'); assert_equals( ig.components[0].renderURL, igConfig.adComponents[0].renderURL, 'component renderURL 0'); assert_equals( ig.components[1].renderURL, igConfig.adComponents[1].renderURL, 'component renderURL 1'); assert_equals( ig.components[2].renderURL, igConfig.adComponents[2].renderURL, 'component renderURL 2'); assert_equals(ig.components[0].adRenderId, 'ca', 'component adRenderId 0'); assert_equals(ig.components[1].adRenderId, 'cb', 'component adRenderId 1'); assert_equals(ig.components[2].adRenderId, 'cc', 'component adRenderId 2'); assert_equals(ig.components[0].metadata, '"compa"', 'component metadata 0'); assert_equals(ig.components[1].metadata, '"compb"', 'component metadata 1'); assert_equals(ig.components[2].metadata, '"compc"', 'component metadata 2'); assert_equals(ig.components[0].sizeGroup, 'big', 'component sizegroup 0'); assert_equals(ig.components[1].sizeGroup, 'small', 'component sizegroup 1'); assert_equals(ig.components[2].sizeGroup, 'big', 'component sizegroup 2'); assert_true(ig.biddingSignalsKeys instanceof Array); assert_array_equals(ig.biddingSignalsKeys, ['alpha', 'beta']); assert_equals(ig.userBiddingSignals, '14'); }, 'getInterestGroupAdAuctionData() all IG data fields, with include-full-ads'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_array_equals(ig.ads, ['a', 'b']); assert_true(ig.components instanceof Array); assert_array_equals(ig.ads, ['a', 'b']); assert_array_equals(ig.components, ['ca', 'cb', 'cc']); assert_array_equals(ig.biddingSignalsKeys, ['alpha', 'beta']); assert_equals(ig.userBiddingSignals, '14'); }, 'getInterestGroupAdAuctionData() all IG data fields, w/o include-full-ads'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); igConfig.auctionServerRequestFlags = ['omit-user-bidding-signals']; await joinInterestGroup(test, uuid, igConfig); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_array_equals(ig.ads, ['a', 'b']); assert_true(ig.components instanceof Array); assert_array_equals(ig.ads, ['a', 'b']); assert_array_equals(ig.components, ['ca', 'cb', 'cc']); assert_array_equals(ig.biddingSignalsKeys, ['alpha', 'beta']); assert_false('userBiddingSignals' in ig, 'userBiddingSignals'); }, 'getInterestGroupAdAuctionData() all IG data fields, with omit-user-bidding-signals'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); // Join twice. await joinInterestGroup(test, uuid, igConfig); await joinInterestGroup(test, uuid, igConfig); // And run an auction. This is a local auction, not a B&A one, run to update // bid/win stats. await runBasicFledgeAuctionAndNavigate(test, uuid); await waitForObservedRequests( uuid, [createBidderReportURL(uuid), createSellerReportURL(uuid)]); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); assert_equals(ig.browserSignals.joinCount, 2, 'joinCount'); assert_equals(ig.browserSignals.bidCount, 1, 'bidCount'); // RecencyMs is the # of milliseconds since the join. We can't exactly say // what it is, but it shouldn't be too huge. assert_true(typeof ig.browserSignals.recencyMs === 'number'); assert_between_inclusive( ig.browserSignals.recencyMs, 0, 60000, 'RecencyMs is between 0 and 60 seconds'); // It's also supposed to be an integer. assert_equals( ig.browserSignals.recencyMs, Math.round(ig.browserSignals.recencyMs), 'RecencyMs is an integer'); // One win. The format here depends highly on whether full ads are used or // not. assert_true( ig.browserSignals.prevWins instanceof Array, 'prevWins is an array'); assert_equals(ig.browserSignals.prevWins.length, 1, 'prevWins length'); assert_true( ig.browserSignals.prevWins[0] instanceof Array, 'prevWins[0] is an array'); assert_equals(ig.browserSignals.prevWins[0].length, 2, 'prevWins[0] length'); // prevWins[0][0] is the time delta in second again. let prevWinTime = ig.browserSignals.prevWins[0][0]; assert_true(typeof prevWinTime === 'number'); assert_between_inclusive( prevWinTime, 0, 60, 'prevWinTime is between 0 and 60 seconds'); // It's also supposed to be an integer. assert_equals( prevWinTime, Math.round(prevWinTime), 'prevWinTime is an integer'); // prevWins[0][1] is the adRenderId of the winner. assert_equals(ig.browserSignals.prevWins[0][1], 'a'); }, 'getInterestGroupAdAuctionData() browserSignals'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); igConfig.auctionServerRequestFlags = ['include-full-ads']; // Join twice. await joinInterestGroup(test, uuid, igConfig); await joinInterestGroup(test, uuid, igConfig); // And run an auction. This is a local auction, not a B&A one, run to update // bid/win stats. await runBasicFledgeAuctionAndNavigate(test, uuid); await waitForObservedRequests( uuid, [createBidderReportURL(uuid), createSellerReportURL(uuid)]); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); let ig = validateWithOneIg(decoded); assert_equals(ig.browserSignals.joinCount, 2, 'joinCount'); assert_equals(ig.browserSignals.bidCount, 1, 'bidCount'); // RecencyMs is the # of milliseconds since the join. We can't exactly say // what it is, but it shouldn't be too huge. assert_true(typeof ig.browserSignals.recencyMs === 'number'); assert_between_inclusive( ig.browserSignals.recencyMs, 0, 60000, 'RecencyMs is between 0 and 60 seconds'); // It's also supposed to be an integer. assert_equals( ig.browserSignals.recencyMs, Math.round(ig.browserSignals.recencyMs), 'RecencyMs is an integer'); // One win. The format here depends highly on whether full ads are used or // not. assert_true( ig.browserSignals.prevWins instanceof Array, 'prevWins is an array'); assert_equals(ig.browserSignals.prevWins.length, 1, 'prevWins length'); assert_true( ig.browserSignals.prevWins[0] instanceof Array, 'prevWins[0] is an array'); assert_equals(ig.browserSignals.prevWins[0].length, 2, 'prevWins[0] length'); // prevWins[0][0] is the time delta in second again. let prevWinTime = ig.browserSignals.prevWins[0][0]; assert_true(typeof prevWinTime === 'number'); assert_between_inclusive( prevWinTime, 0, 60, 'prevWinTime is between 0 and 60 seconds'); // It's also supposed to be an integer. assert_equals( prevWinTime, Math.round(prevWinTime), 'prevWinTime is an integer'); // prevWins[0][1] is an ad object w/include-full-ads on (with renderURL, // metadata, and adRenderId). let prevWinAd = ig.browserSignals.prevWins[0][1]; assert_equals( prevWinAd.renderURL, igConfig.ads[0].renderURL, 'prevWin ad renderURL'); assert_equals(prevWinAd.metadata, '"ada"', 'prevWin ad metadata'); assert_equals(prevWinAd.adRenderId, 'a', 'prevWin ad adRenderId'); }, 'getInterestGroupAdAuctionData() browserSignals with include-full-ads'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igTemplate = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, {...igTemplate, name: 'first'}); await joinInterestGroup(test, uuid, {...igTemplate, name: 'second'}); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); assert_equals(decoded.message.version, 0); assert_equals(decoded.message.publisher, window.location.hostname); assert_equals(typeof decoded.message.generationId, 'string'); let origin = window.location.origin; let igMapKeys = Object.getOwnPropertyNames(decoded.message.interestGroups); assert_array_equals(igMapKeys, [origin]); assert_equals(decoded.message.interestGroups[origin].length, 2); let names = [ decoded.message.interestGroups[origin][0].name, decoded.message.interestGroups[origin][1].name ]; assert_array_equals(names.sort(), ['first', 'second']); }, 'getInterestGroupAdAuctionData() with multiple interest groups'); async function joinCrossOriginIG(test, uuid, origin, name) { let iframe = await createIframe(test, origin, 'join-ad-interest-group'); await runInFrame( test, iframe, `await joinInterestGroup(test_instance, "${uuid}", {name: "${name}"});`); } subsetTest(promise_test, async test => { const uuid = generateUuid(test); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN1, 'o1'); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN2, 'o2'); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN3, 'o3'); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN4, 'o4'); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); assert_equals(decoded.message.version, 0); assert_equals(decoded.message.publisher, window.location.hostname); assert_equals(typeof decoded.message.generationId, 'string'); let igMapKeys = Object.getOwnPropertyNames(decoded.message.interestGroups); assert_array_equals( igMapKeys.sort(), [OTHER_ORIGIN1, OTHER_ORIGIN2, OTHER_ORIGIN3, OTHER_ORIGIN4].sort()); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN1].length, 1); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN1][0].name, 'o1'); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN2].length, 1); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN2][0].name, 'o2'); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN3].length, 1); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN3][0].name, 'o3'); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN4].length, 1); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN4][0].name, 'o4'); }, 'getInterestGroupAdAuctionData() with multiple buyers'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); let renderURL = createRenderURL(uuid); let bidderDebugReportURL = createBidderReportURL(uuid, /*id=*/ 'forDebuggingOnly'); let biddingLogicURL = createBiddingScriptURL({ generateBid: ` forDebuggingOnly.reportAdAuctionWin('${bidderDebugReportURL}'); return {bid: 1, render: '${renderURL}'};` }); await Promise.all([ joinInterestGroup( test, uuid, {name: 'o1', ads: [{renderURL: renderURL}], biddingLogicURL}), joinInterestGroup( test, uuid, {name: 'o2', ads: [{renderURL: renderURL}], biddingLogicURL}) ]); // Run an auction to put the origin to cooldown, or lockout. await runBasicFledgeAuctionAndNavigate(test, uuid); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); assert_equals(decoded.message.version, 0); assert_equals(decoded.message.publisher, window.location.hostname); assert_equals(typeof decoded.message.generationId, 'string'); assert_equals( decoded.message.interestGroups[window.location.origin].length, 2); assert_equals( decoded.message.interestGroups[window.location.origin][0] .inCooldownOrLockout, true); assert_equals( decoded.message.interestGroups[window.location.origin][1] .inCooldownOrLockout, true); }, 'getInterestGroupAdAuctionData() InCooldownOrLockout in each IG'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN1, 'o1'); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN2, 'o2'); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN3, 'o3'); await joinCrossOriginIG(test, uuid, OTHER_ORIGIN4, 'o4'); let config = { coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, perBuyerConfig: {}, requestSize: 5000 }; config.perBuyerConfig[OTHER_ORIGIN2] = {}; config.perBuyerConfig[OTHER_ORIGIN3] = {}; const result = await navigator.getInterestGroupAdAuctionData(config); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); assert_equals(decoded.message.version, 0); assert_equals(decoded.message.publisher, window.location.hostname); assert_equals(typeof decoded.message.generationId, 'string'); let igMapKeys = Object.getOwnPropertyNames(decoded.message.interestGroups); assert_array_equals(igMapKeys.sort(), [OTHER_ORIGIN2, OTHER_ORIGIN3].sort()); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN2].length, 1); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN2][0].name, 'o2'); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN3].length, 1); assert_equals(decoded.message.interestGroups[OTHER_ORIGIN3][0].name, 'o3'); }, 'getInterestGroupAdAuctionData() uses perBuyerConfig to select buyers'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); await joinInterestGroup(test, uuid); const result = await navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin }); assert_true(result.requestId !== null); assert_true(result.request.length > 0); let decoded = await BA.decodeInterestGroupData(result.request); assert_own_property(decoded.message, 'enforceKAnon'); assert_equals(decoded.message.enforceKAnon, true); }, 'getInterestGroupAdAuctionData() requests k-anon.'); /************************************************************************* * Tests for the multi-seller variant of the API *************************************************************************/ async function validateForSeller(adAuctionData, seller) { assert_equals(adAuctionData.seller, seller); assert_not_own_property(adAuctionData, 'error'); assert_own_property(adAuctionData, 'request'); assert_true(adAuctionData.request.length > 0); let decoded = await BA.decodeInterestGroupData(adAuctionData.request); let ig = validateWithOneIg(decoded); assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME); assert_array_equals(ig.ads, ['a', 'b']); assert_true(ig.components instanceof Array); assert_array_equals(ig.components, ['ca', 'cb', 'cc']); assert_array_equals(ig.biddingSignalsKeys, ['alpha', 'beta']); assert_equals(ig.userBiddingSignals, '14'); } subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); const result = await navigator.getInterestGroupAdAuctionData({ sellers: [{ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, }] }); assert_true(result.requestId !== null); assert_own_property(result, 'requests'); assert_equals(result.requests.length, 1); await validateForSeller(result.requests[0], window.location.origin); }, 'getInterestGroupAdAuctionData() multi-seller with single seller'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); const result = await navigator.getInterestGroupAdAuctionData({ sellers: [{ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, }, { coordinatorOrigin: await BA.configureCoordinator(), seller: OTHER_ORIGIN1, }] }); assert_true(result.requestId !== null); assert_own_property(result, 'requests'); assert_equals(result.requests.length, 2, JSON.stringify(result)); result.requests.sort((a,b)=> (a.seller < b.seller ? -1 : +(a.seller > b.seller))); await validateForSeller(result.requests[0], OTHER_ORIGIN1); await validateForSeller(result.requests[1], window.location.origin); assert_not_equals(result.requests[0].request.toString(), result.requests[1].request.toString()); }, 'getInterestGroupAdAuctionData() multi-seller with multiple sellers - valid'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); const result = await navigator.getInterestGroupAdAuctionData({ sellers: [{ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, }, { coordinatorOrigin: "https://invalid.coordinator.test/", seller: OTHER_ORIGIN1, }] }); assert_true(result.requestId !== null); assert_own_property(result, 'requests'); assert_equals(result.requests.length, 2, JSON.stringify(result)); result.requests.sort((a,b)=> (a.seller < b.seller ? -1 : +(a.seller > b.seller))); assert_equals(result.requests[0].seller, OTHER_ORIGIN1); assert_own_property(result.requests[0], 'error'); assert_not_own_property(result.requests[0], 'request'); await validateForSeller(result.requests[1], window.location.origin); }, 'getInterestGroupAdAuctionData() multi-seller with multiple sellers - one invalid coordinator'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); await promise_rejects_js(test, TypeError, navigator.getInterestGroupAdAuctionData({ sellers: [{ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, }, { coordinatorOrigin: await BA.configureCoordinator(), seller: "http://not.secure.test/", }] })); }, 'getInterestGroupAdAuctionData() multi-seller with multiple sellers - one invalid seller'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); await promise_rejects_js(test, TypeError, navigator.getInterestGroupAdAuctionData({ })); }, 'getInterestGroupAdAuctionData() one of "seller" and "sellers" is required'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); await promise_rejects_js(test, TypeError, navigator.getInterestGroupAdAuctionData({ seller: window.location.origin, sellers: [{ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, }] })); }, 'getInterestGroupAdAuctionData() doesn\'t allow "seller" and "sellers" fields'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); await promise_rejects_js(test, TypeError, navigator.getInterestGroupAdAuctionData({ coordinatorOrigin: window.location.origin, sellers: [{ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, }] })); }, 'getInterestGroupAdAuctionData() doesn\'t allow "coordinatorOrigin" and "sellers" fields'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); await promise_rejects_js(test, TypeError, navigator.getInterestGroupAdAuctionData({ sellers: [{ coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, }, { coordinatorOrigin: await BA.configureCoordinator(), seller: window.location.origin, } ] })); }, 'getInterestGroupAdAuctionData() doesn\'t allow duplicate sellers in "sellers" field'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); const igConfig = makeTemplateIgConfig(uuid); await joinInterestGroup(test, uuid, igConfig); const result = await navigator.getInterestGroupAdAuctionData({ sellers: [] }); assert_equals(result.requestId, ""); assert_array_equals(result.requests, []); }, 'getInterestGroupAdAuctionData() with no sellers');