diff options
Diffstat (limited to 'testing/web-platform/tests/fledge/tentative/component-ads.https.window.js')
-rw-r--r-- | testing/web-platform/tests/fledge/tentative/component-ads.https.window.js | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js new file mode 100644 index 0000000000..7e98570b9e --- /dev/null +++ b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js @@ -0,0 +1,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.'); |