diff --git a/README.md b/README.md index 40df62ccee4..b408d0942ad 100644 --- a/README.md +++ b/README.md @@ -112,11 +112,11 @@ prebid.requestBids({ $ git clone https://github.com/prebid/Prebid.js.git $ cd Prebid.js - $ npm install + $ npm ci *Note:* You need to have `NodeJS` 12.16.1 or greater installed. -*Note:* In the 1.24.0 release of Prebid.js we have transitioned to using gulp 4.0 from using gulp 3.9.1. To comply with gulp's recommended setup for 4.0, you'll need to have `gulp-cli` installed globally prior to running the general `npm install`. This shouldn't impact any other projects you may work on that use an earlier version of gulp in its setup. +*Note:* In the 1.24.0 release of Prebid.js we have transitioned to using gulp 4.0 from using gulp 3.9.1. To comply with gulp's recommended setup for 4.0, you'll need to have `gulp-cli` installed globally prior to running the general `npm ci`. This shouldn't impact any other projects you may work on that use an earlier version of gulp in its setup. If you have a previous version of `gulp` installed globally, you'll need to remove it before installing `gulp-cli`. You can check if this is installed by running `gulp -v` and seeing the version that's listed in the `CLI` field of the output. If you have the `gulp` package installed globally, it's likely the same version that you'll see in the `Local` field. If you already have `gulp-cli` installed, it should be a lower major version (it's at version `2.0.1` at the time of the transition). @@ -202,6 +202,11 @@ To run the unit tests: gulp test ``` +To run the unit tests for a perticular file (example for pubmaticBidAdapter_spec.js): +```bash +gulp test --file "test/spec/modules/pubmaticBidAdapter_spec.js" +``` + To generate and view the code coverage reports: ```bash diff --git a/gulpfile.js b/gulpfile.js index b7a9e442a8c..d7b91db4ade 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -31,7 +31,7 @@ const execa = require('execa'); var prebid = require('./package.json'); var dateString = 'Updated : ' + (new Date()).toISOString().substring(0, 10); -var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + ' */\n'; +var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + '\nModules: <%= modules %> */\n'; var port = 9999; const FAKE_SERVER_HOST = argv.host ? argv.host : 'localhost'; const FAKE_SERVER_PORT = 4444; @@ -157,15 +157,20 @@ function makeWebpackPkg() { const analyticsSources = helpers.getAnalyticsSources(); const moduleSources = helpers.getModulePaths(externalModules); + const modulesString = getModulesListToAddInBanner(externalModules); return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(cloned, webpack)) .pipe(uglify()) - .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid }))) + .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid, modules: modulesString }))) .pipe(gulp.dest('build/dist')); } +function getModulesListToAddInBanner(modules){ + return (modules.length > 0) ? modules.join(', ') : 'All available modules in current version.'; +} + function gulpBundle(dev) { return bundle(dev).pipe(gulp.dest('build/' + (dev ? 'dev' : 'dist'))); } @@ -215,6 +220,8 @@ function bundle(dev, moduleArr) { return gulp.src( entries ) + // Need to uodate the "Modules: ..." section in comment with the current modules list + .pipe(replace(/(Modules: )(.*?)(\*\/)/, ('$1' + getModulesListToAddInBanner(helpers.getArgModules()) + ' $3'))) .pipe(gulpif(dev, sourcemaps.init({ loadMaps: true }))) .pipe(concat(outputFileName)) .pipe(gulpif(!argv.manualEnable, footer('\n<%= global %>.processQueue();', { diff --git a/integrationExamples/gpt/adUnitFloors.html b/integrationExamples/gpt/adUnitFloors.html new file mode 100644 index 00000000000..bb48a20ef78 --- /dev/null +++ b/integrationExamples/gpt/adUnitFloors.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + + diff --git a/integrationExamples/gpt/creative_rendering.html b/integrationExamples/gpt/creative_rendering.html index aef8b7f1654..04d4736c631 100644 --- a/integrationExamples/gpt/creative_rendering.html +++ b/integrationExamples/gpt/creative_rendering.html @@ -1,9 +1,4 @@ - - - - + + + + + + + + + + + +Prebid + +

ID Import Library Example

+

Steps before logging in:

+ + + + + + + + + + + diff --git a/integrationExamples/gpt/jwplayerRtdProvider_example.html b/integrationExamples/gpt/jwplayerRtdProvider_example.html index 75eb85a2d8c..41c27b70ece 100644 --- a/integrationExamples/gpt/jwplayerRtdProvider_example.html +++ b/integrationExamples/gpt/jwplayerRtdProvider_example.html @@ -10,32 +10,30 @@ var PREBID_TIMEOUT = 1000; var adUnits = [{ - code: 'div-gpt-ad-1460505748561-0', - fpd: { - context: { - data: { - jwTargeting: { - // Note: the following Ids are placeholders and should be replaced with your Ids. - playerID: '123', - mediaID: 'abc' + code: 'div-gpt-ad-1460505748561-0', + ortb2Imp: { + ext: { + data: { + jwTargeting: { + // Note: the following Ids are placeholders and should be replaced with your Ids. + playerID: '123', + mediaID: 'abc' + } + } } - }, - } - }, - - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]], - } - }, - // Replace this object to test a new Adapter! - bids: [{ - bidder: 'appnexus', - params: { - placementId: 13144370 - } - }] - + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]], + } + }, + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }] }]; var pbjs = pbjs || {}; diff --git a/integrationExamples/gpt/permutiveRtdProvider_example.html b/integrationExamples/gpt/permutiveRtdProvider_example.html new file mode 100644 index 00000000000..a06430bcdfa --- /dev/null +++ b/integrationExamples/gpt/permutiveRtdProvider_example.html @@ -0,0 +1,232 @@ + + + + + + + + + + + +

+

Basic Prebid.js Example

+
Div-1
+
+ +
+ +
+ +
Div-2
+
+ +
+ + + + diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index fa9b1e3b5fd..fae5ca2b539 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -236,6 +236,7 @@ }, { name: "sharedId", + // bidders: ["rubicon", "sampleBidders"], // to allow this ID for specific bidders params: { syncTime: 60 // in seconds, default is 24 hours }, @@ -276,6 +277,9 @@ { name: "criteo" }, + { + name: "uid2" + } ], syncDelay: 5000, auctionDelay: 1000 diff --git a/integrationExamples/mass/index.html b/integrationExamples/mass/index.html new file mode 100644 index 00000000000..80fe4cfb934 --- /dev/null +++ b/integrationExamples/mass/index.html @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + +
+

Note: for this example to work, you need access to a bid simulation tool from your MASS enabled Exchange partner.

+
+ +
+
+ + diff --git a/integrationExamples/postbid/postbid_prebidServer_example.html b/integrationExamples/postbid/postbid_prebidServer_example.html new file mode 100644 index 00000000000..3af7b010872 --- /dev/null +++ b/integrationExamples/postbid/postbid_prebidServer_example.html @@ -0,0 +1,86 @@ + + + + + + + + + + + diff --git a/modules/.submodules.json b/modules/.submodules.json index fc69dd276a3..0f62627822a 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -20,7 +20,9 @@ "fabrickIdSystem", "verizonMediaIdSystem", "pubProvidedIdSystem", - "tapadIdSystem" + "mwOpenLinkIdSystem", + "tapadIdSystem", + "novatiqIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js index b7d8722e9f9..1961dba1f10 100644 --- a/modules/a4gBidAdapter.js +++ b/modules/a4gBidAdapter.js @@ -70,8 +70,7 @@ export const spec = { if (response.cpm > 0) { const bidResponse = { requestId: response.id, - creativeId: response.id, - adId: response.id, + creativeId: response.crid || response.id, cpm: response.cpm, width: response.width, height: response.height, diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 6f7feec59c9..892411837dd 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -8,16 +8,16 @@ import sha256 from 'crypto-js/sha256.js'; import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { createEidsArray } from './userId/eids.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; export const BIDDER_CODE = 'adagio'; export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.6.0'; +export const VERSION = '2.7.0'; export const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; -export const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; +export const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; export const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; export const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; export const GVLID = 617; @@ -687,6 +687,112 @@ function _renderer(bid) { }); } +function _parseNativeBidResponse(bid) { + if (!bid.admNative || !Array.isArray(bid.admNative.assets)) { + utils.logError(`${LOG_PREFIX} Invalid native response`); + return; + } + + const native = {} + + function addAssetDataValue(data) { + const map = { + 1: 'sponsoredBy', // sponsored + 2: 'body', // desc + 3: 'rating', + 4: 'likes', + 5: 'downloads', + 6: 'price', + 7: 'salePrice', + 8: 'phone', + 9: 'address', + 10: 'body2', // desc2 + 11: 'displayUrl', + 12: 'cta' + } + if (map.hasOwnProperty(data.type) && typeof data.value === 'string') { + native[map[data.type]] = data.value; + } + } + + // assets + bid.admNative.assets.forEach(asset => { + if (asset.title) { + native.title = asset.title.text + } else if (asset.data) { + addAssetDataValue(asset.data) + } else if (asset.img) { + switch (asset.img.type) { + case 1: + native.icon = { + url: asset.img.url, + width: asset.img.w, + height: asset.img.h + }; + break; + default: + native.image = { + url: asset.img.url, + width: asset.img.w, + height: asset.img.h + }; + break; + } + } + }); + + if (bid.admNative.link) { + if (bid.admNative.link.url) { + native.clickUrl = bid.admNative.link.url; + } + if (Array.isArray(bid.admNative.link.clickTrackers)) { + native.clickTrackers = bid.admNative.link.clickTrackers + } + } + + if (Array.isArray(bid.admNative.eventtrackers)) { + native.impressionTrackers = []; + bid.admNative.eventtrackers.forEach(tracker => { + // Only Impression events are supported. Prebid does not support Viewability events yet. + if (tracker.event !== 1) { + return; + } + + // methods: + // 1: image + // 2: js + // note: javascriptTrackers is a string. If there's more than one JS tracker in bid response, the last script will be used. + switch (tracker.method) { + case 1: + native.impressionTrackers.push(tracker.url); + break; + case 2: + native.javascriptTrackers = ``; + break; + } + }); + } else { + native.impressionTrackers = Array.isArray(bid.admNative.imptrackers) ? bid.admNative.imptrackers : []; + if (bid.admNative.jstracker) { + native.javascriptTrackers = bid.admNative.jstracker; + } + } + + if (bid.admNative.privacy) { + native.privacyLink = bid.admNative.privacy; + } + + if (bid.admNative.ext) { + native.ext = {} + + if (bid.admNative.ext.bvw) { + native.ext.adagio_bvw = bid.admNative.ext.bvw; + } + } + + bid.native = native +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -873,6 +979,10 @@ export const spec = { } } + if (bidObj.mediaType === NATIVE) { + _parseNativeBidResponse(bidObj); + } + bidObj.site = bidReq.params.site; bidObj.placement = bidReq.params.placement; bidObj.pagetype = bidReq.params.pagetype; diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index aa79338d79e..46656d88d37 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -38,8 +38,8 @@ Connects to Adagio demand source to fetch bids. category: 'sport', // Recommended. Category of the content displayed in the page. subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. postBid: false, // Optional. Use it in case of Post-bid integration only. - useAdUnitCodeAsAdUnitElementId: false // Optional. Use it by-pass adUnitElementId and use the adUnit code as value - useAdUnitCodeAsPlacement: false // Optional. Use it to by-pass placement and use the adUnit code as value + useAdUnitCodeAsAdUnitElementId: false, // Optional. Use it by-pass adUnitElementId and use the adUnit code as value + useAdUnitCodeAsPlacement: false, // Optional. Use it to by-pass placement and use the adUnit code as value // Optional debug mode, used to get a bid response with expected cpm. debug: { enabled: true, @@ -78,8 +78,8 @@ Connects to Adagio demand source to fetch bids. category: 'sport', // Recommended. Category of the content displayed in the page. subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. postBid: false, // Optional. Use it in case of Post-bid integration only. - useAdUnitCodeAsAdUnitElementId: false // Optional. Use it by-pass adUnitElementId and use the adUnit code as value - useAdUnitCodeAsPlacement: false // Optional. Use it to by-pass placement and use the adUnit code as value + useAdUnitCodeAsAdUnitElementId: false, // Optional. Use it by-pass adUnitElementId and use the adUnit code as value + useAdUnitCodeAsPlacement: false, // Optional. Use it to by-pass placement and use the adUnit code as value video: { skip: 0 // OpenRTB 2.5 video options defined here override ones defined in mediaTypes. @@ -91,6 +91,58 @@ Connects to Adagio demand source to fetch bids. } } }] + }, + { + code: 'article_native', + mediaTypes: { + native: { + // generic Prebid options + title: { + required: true, + len: 80 + }, + // … + // Custom Adagio data assets + ext: { + adagio_bvw: { + required: false + } + } + } + }, + bids: [{ + bidder: 'adagio', // Required + params: { + organizationId: '1002', // Required - Organization ID provided by Adagio. + site: 'adagio-io', // Required - Site Name provided by Adagio. + placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. + adUnitElementId: 'article_native', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. + + // The following params are limited to 30 characters, + // and can only contain the following characters: + // - alphanumeric (A-Z+a-z+0-9, case-insensitive) + // - dashes `-` + // - underscores `_` + // Also, each param can have at most 50 unique active values (case-insensitive). + pagetype: 'article', // Highly recommended. The pagetype describes what kind of content will be present in the page. + environment: 'mobile', // Recommended. Environment where the page is displayed. + category: 'sport', // Recommended. Category of the content displayed in the page. + subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. + postBid: false, // Optional. Use it in case of Post-bid integration only. + useAdUnitCodeAsAdUnitElementId: false, // Optional. Use it by-pass adUnitElementId and use the adUnit code as value + useAdUnitCodeAsPlacement: false, // Optional. Use it to by-pass placement and use the adUnit code as value + // Optional OpenRTB Native 1.2 request object. Only `context`, `plcmttype` fields are supported. + native: { + context: 1, + plcmttype: 2 + }, + // Optional debug mode, used to get a bid response with expected cpm. + debug: { + enabled: true, + cpm: 3.00 // default to 1.00 + } + } + }] } ]; diff --git a/modules/adformBidAdapter.js b/modules/adformBidAdapter.js index 05e45a428d3..dc30e152861 100644 --- a/modules/adformBidAdapter.js +++ b/modules/adformBidAdapter.js @@ -5,6 +5,7 @@ import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import * as utils from '../src/utils.js'; +import includes from 'core-js-pure/features/array/includes.js'; const OUTSTREAM_RENDERER_URL = 'https://s2.adform.net/banners/scripts/video/outstream/render.js'; @@ -24,13 +25,32 @@ export const spec = { var request = []; var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'fd', 1 ], [ 'url', null ], [ 'tid', null ], [ 'eids', eids ] ]; + const targetingParams = { mkv: [], mkw: [], msw: [] }; + var bids = JSON.parse(JSON.stringify(validBidRequests)); var bidder = (bids[0] && bids[0].bidder) || BIDDER_CODE; + + // set common targeting options as query params + if (bids.length > 1) { + for (let key in targetingParams) { + if (targetingParams.hasOwnProperty(key)) { + const collection = bids.map(bid => ((bid.params[key] && bid.params[key].split(',')) || [])); + targetingParams[key] = collection.reduce(intersects); + if (targetingParams[key].length) { + bids.forEach((bid, index) => { + bid.params[key] = collection[index].filter(item => !includes(targetingParams[key], item)); + }); + } + } + } + } + for (i = 0, l = bids.length; i < l; i++) { bid = bids[i]; if ((bid.params.priceType === 'net') || (bid.params.pt === 'net')) { netRevenue = 'net'; } + for (j = 0, k = globalParams.length; j < k; j++) { _key = globalParams[j][0]; _value = bid[_key] || bid.params[_key]; @@ -65,6 +85,12 @@ export const spec = { request.push('us_privacy=' + bidderRequest.uspConsent); } + for (let key in targetingParams) { + if (targetingParams.hasOwnProperty(key)) { + globalParams.push([ key, targetingParams[key].join(',') ]); + } + } + for (i = 1, l = globalParams.length; i < l; i++) { _key = globalParams[i][0]; _value = globalParams[i][1]; @@ -114,6 +140,10 @@ export const spec = { return result; }, {}); } + + function intersects(col1, col2) { + return col1.filter(item => includes(col2, item)); + } }, interpretResponse: function (serverResponse, bidRequest) { const VALID_RESPONSES = { diff --git a/modules/adhashBidAdapter.js b/modules/adhashBidAdapter.js new file mode 100644 index 00000000000..b30f9cebbc1 --- /dev/null +++ b/modules/adhashBidAdapter.js @@ -0,0 +1,102 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const VERSION = '1.0'; + +export const spec = { + code: 'adhash', + url: 'https://bidder.adhash.org/rtb?version=' + VERSION + '&prebid=true', + supportedMediaTypes: [ BANNER ], + + isBidRequestValid: (bid) => { + try { + const { publisherId, platformURL } = bid.params; + return ( + includes(Object.keys(bid.mediaTypes), BANNER) && + typeof publisherId === 'string' && + publisherId.length === 42 && + typeof platformURL === 'string' && + platformURL.length >= 13 + ); + } catch (error) { + return false; + } + }, + + buildRequests: (validBidRequests, bidderRequest) => { + const { gdprConsent } = bidderRequest; + const { url } = spec; + const bidRequests = []; + let referrer = ''; + if (bidderRequest && bidderRequest.refererInfo) { + referrer = bidderRequest.refererInfo.referer; + } + for (var i = 0; i < validBidRequests.length; i++) { + var index = Math.floor(Math.random() * validBidRequests[i].sizes.length); + var size = validBidRequests[i].sizes[index].join('x'); + bidRequests.push({ + method: 'POST', + url: url, + bidRequest: validBidRequests[i], + data: { + timezone: new Date().getTimezoneOffset() / 60, + location: referrer, + publisherId: validBidRequests[i].params.publisherId, + size: { + screenWidth: window.screen.width, + screenHeight: window.screen.height + }, + navigator: { + platform: window.navigator.platform, + language: window.navigator.language, + userAgent: window.navigator.userAgent + }, + creatives: [{ + size: size, + position: validBidRequests[i].adUnitCode + }], + blockedCreatives: [], + currentTimestamp: new Date().getTime(), + recentAds: [], + GDPR: gdprConsent + }, + options: { + withCredentials: false, + crossOrigin: true + }, + }); + } + return bidRequests; + }, + + interpretResponse: (serverResponse, request) => { + const responseBody = serverResponse ? serverResponse.body : {}; + + if (!responseBody.creatives || responseBody.creatives.length === 0) { + return []; + } + + const publisherURL = JSON.stringify(request.bidRequest.params.platformURL); + const oneTimeId = request.bidRequest.adUnitCode + Math.random().toFixed(16).replace('0.', '.'); + const bidderResponse = JSON.stringify({ responseText: JSON.stringify(responseBody) }); + const requestData = JSON.stringify(request.data); + + return [{ + requestId: request.bidRequest.bidId, + cpm: responseBody.creatives[0].costEUR, + ad: + `
+ + `, + width: request.bidRequest.sizes[0][0], + height: request.bidRequest.sizes[0][1], + creativeId: request.bidRequest.adUnitCode, + netRevenue: true, + currency: 'EUR', + ttl: 60 + }]; + } +}; + +registerBidder(spec); diff --git a/modules/adhashBidAdapter.md b/modules/adhashBidAdapter.md new file mode 100644 index 00000000000..c1093e0ccd7 --- /dev/null +++ b/modules/adhashBidAdapter.md @@ -0,0 +1,43 @@ +# Overview + +``` +Module Name: AdHash Bidder Adapter +Module Type: Bidder Adapter +Maintainer: damyan@adhash.org +``` + +# Description + +Here is what you need for Prebid integration with AdHash: +1. Register with AdHash. +2. Once registered and approved, you will receive a Publisher ID and Platform URL. +3. Use the Publisher ID and Platform URL as parameters in params. + +Please note that a number of AdHash functionalities are not supported in the Prebid.js integration: +* Cookie-less frequency and recency capping; +* Audience segments; +* Price floors and passback tags, as they are not needed in the Prebid.js setup; +* Reservation for direct deals only, as bids are evaluated based on their price. + +# Test Parameters +``` + var adUnits = [ + { + code: '/19968336/header-bid-tag-1', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'adhash', + params: { + publisherId: '0x1234567890123456789012345678901234567890', + platformURL: 'https://adhash.org/p/struma/' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index 5510db62950..4fae85e82ad 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -24,8 +24,7 @@ export const spec = { const gdprParams = (gdprConsent && gdprConsent.consentString) ? { xt: [gdprConsent.consentString] } : {}; const refererParams = (refererInfo && refererInfo.referer) ? { xf: [base64urlEncode(refererInfo.referer)] } : {}; - const id5Params = (getId5Id(validBidRequests)) ? { x5: [getId5Id(validBidRequests)] } : {}; - const commonParams = { ...gdprParams, ...refererParams, ...id5Params }; + const commonParams = { ...gdprParams, ...refererParams }; const slots = validBidRequests.map(bid => ({ slotname: bidToSlotName(bid), @@ -34,8 +33,13 @@ export const spec = { const payload = { slots: slots, - parameters: commonParams - } + parameters: commonParams, + user: { + ext: { + eids: getEids(validBidRequests), + } + } + }; const account = getAccount(validBidRequests); const uri = 'https://ads-' + account + '.adhese.com/json'; @@ -154,9 +158,9 @@ function getAccount(validBidRequests) { return validBidRequests[0].params.account; } -function getId5Id(validBidRequests) { - if (validBidRequests[0] && validBidRequests[0].userId && validBidRequests[0].userId.id5id && validBidRequests[0].userId.id5id.uid) { - return validBidRequests[0].userId.id5id.uid; +function getEids(validBidRequests) { + if (validBidRequests[0] && validBidRequests[0].userIdAsEids) { + return validBidRequests[0].userIdAsEids; } } diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 20ed65fe2e2..03fa5c2b2d9 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -52,7 +52,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { export const spec = { code: 'adkernel', - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads', 'bcm'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index d2a6f9a639e..6cf738a0086 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -21,7 +21,7 @@ export const spec = { buildRequests: function (validRequest, bidderRequest) { const payload = { imps: [], - fpd: config.getConfig('fpd') + fpd: config.getLegacyFpd(config.getConfig('ortb2')) }; let endpointUrl; if (bidderRequest) { @@ -42,7 +42,11 @@ export const spec = { } } validRequest.forEach((bid) => { - payload.imps.push(bid); + let imp = {}; + Object.keys(bid).forEach(key => { + (key === 'ortb2Imp') ? imp.fpd = config.getLegacyImpFpd(bid[key]) : imp[key] = bid[key]; + }); + payload.imps.push(imp); }); const payloadString = JSON.stringify(payload); return { diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 5da941c65ca..c6298cffde9 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -120,11 +120,11 @@ export const spec = { payload.referrer_detection = refererinfo; } - let fpdcfg = config.getConfig('fpd') + let fpdcfg = config.getLegacyFpd(config.getConfig('ortb2')); if (fpdcfg && fpdcfg.context) { let fdata = { - keywords: fpdcfg.context.keywords, - category: fpdcfg.context.data.category + keywords: fpdcfg.context.keywords || '', + category: utils.deepAccess(fpdcfg, 'context.data.category') || '' } payload.fpd = fdata; } diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 0964de397d1..669feeedb04 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -15,16 +15,10 @@ const HOST_GETTERS = { return 'ghb' + subdomainSuffixes[num++ % subdomainSuffixes.length] + '.adtelligent.com'; } }()), - appaloosa: function () { - return 'ghb.hb.appaloosa.media'; - }, - onefiftytwomedia: function () { - return 'ghb.ads.152media.com'; - }, - mediafuse: function () { - return 'ghb.hbmp.mediafuse.com'; - } - + navelix: () => 'ghb.hb.navelix.com', + appaloosa: () => 'ghb.hb.appaloosa.media', + onefiftytwomedia: () => 'ghb.ads.152media.com', + mediafuse: () => 'ghb.hbmp.mediafuse.com', } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -41,6 +35,7 @@ export const spec = { code: BIDDER_CODE, gvlid: 410, aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa', + { code: 'navelix', gvlid: 380 }, { code: 'mediafuse', skipPbsAliasing: true diff --git a/modules/adtrueBidAdapter.js b/modules/adtrueBidAdapter.js index fa83f9f29f7..b4dc7f7ea89 100644 --- a/modules/adtrueBidAdapter.js +++ b/modules/adtrueBidAdapter.js @@ -14,9 +14,8 @@ const UNDEFINED = undefined; const DEFAULT_WIDTH = 0; const DEFAULT_HEIGHT = 0; const NET_REVENUE = false; -const USER_SYNC_URL_IFRAME = 'https://hb.adtrue.com/prebid/usersync?t=iframe&p='; -const USER_SYNC_URL_IMAGE = 'https://hb.adtrue.com/prebid/usersync?t=img&p='; let publisherId = 0; +let zoneId = 0; let NATIVE_ASSET_ID_TO_KEY_MAP = {}; const DATA_TYPES = { 'NUMBER': 'number', @@ -25,6 +24,11 @@ const DATA_TYPES = { 'ARRAY': 'array', 'OBJECT': 'object' }; +const SYNC_TYPES = Object.freeze({ + 1: 'iframe', + 2: 'image' +}); + const VIDEO_CUSTOM_PARAMS = { 'mimes': DATA_TYPES.ARRAY, 'minduration': DATA_TYPES.NUMBER, @@ -440,6 +444,14 @@ export const spec = { utils.logWarn(LOG_WARN_PREFIX + 'Error: missing publisherId'); return false; } + if (!utils.isStr(bid.params.publisherId)) { + utils.logWarn(LOG_WARN_PREFIX + 'Error: publisherId is mandatory and cannot be numeric'); + return false; + } + if (!utils.isStr(bid.params.zoneId)) { + utils.logWarn(LOG_WARN_PREFIX + 'Error: zoneId is mandatory and cannot be numeric'); + return false; + } return true; } return false; @@ -478,6 +490,7 @@ export const spec = { return; } publisherId = conf.pubId.trim(); + zoneId = conf.zoneId.trim(); payload.site.publisher.id = conf.pubId.trim(); payload.ext.wrapper = {}; @@ -498,9 +511,7 @@ export const spec = { if (typeof config.getConfig('device') === 'object') { payload.device = Object.assign(payload.device, config.getConfig('device')); } - utils.deepSetValue(payload, 'source.tid', conf.transactionId); - // test bids if (window.location.href.indexOf('adtrueTest=true') !== -1) { payload.test = 1; @@ -509,7 +520,6 @@ export const spec = { if (validBidRequests[0].schain) { utils.deepSetValue(payload, 'source.ext.schain', validBidRequests[0].schain); } - // Attaching GDPR Consent Params if (bidderRequest && bidderRequest.gdprConsent) { utils.deepSetValue(payload, 'user.ext.consent', bidderRequest.gdprConsent.consentString); @@ -520,7 +530,6 @@ export const spec = { if (bidderRequest && bidderRequest.uspConsent) { utils.deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - // coppa compliance if (config.getConfig('coppa') === true) { utils.deepSetValue(payload, 'regs.coppa', 1); @@ -576,7 +585,6 @@ export const spec = { } }); } - newBid.meta = {}; if (bid.ext && bid.ext.dspid) { newBid.meta.networkId = bid.ext.dspid; @@ -588,14 +596,12 @@ export const spec = { newBid.meta.advertiserDomains = bid.adomain; newBid.meta.clickUrl = bid.adomain[0]; } - // adserverTargeting if (seatbidder.ext && seatbidder.ext.buyid) { newBid.adserverTargeting = { 'hb_buyid_adtrue': seatbidder.ext.buyid }; } - bidResponses.push(newBid); }); }); @@ -606,32 +612,28 @@ export const spec = { return bidResponses; }, getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { - let syncurl = '' + publisherId; - - if (gdprConsent) { - syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); - } - if (uspConsent) { - syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); - } - - // coppa compliance - if (config.getConfig('coppa') === true) { - syncurl += '&coppa=1'; - } - - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: USER_SYNC_URL_IFRAME + syncurl - }]; - } else { - return [{ - type: 'image', - url: USER_SYNC_URL_IMAGE + syncurl - }]; + if (!responses || responses.length === 0 || (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled)) { + return []; } + return responses.reduce((accum, rsp) => { + let cookieSyncs = utils.deepAccess(rsp, 'body.ext.cookie_sync'); + if (cookieSyncs) { + let cookieSyncObjects = cookieSyncs.map(cookieSync => { + return { + type: SYNC_TYPES[cookieSync.type], + url: cookieSync.url + + '&publisherId=' + publisherId + + '&zoneId=' + zoneId + + '&gdpr=' + (gdprConsent && gdprConsent.gdprApplies ? 1 : 0) + + '&gdpr_consent=' + encodeURIComponent((gdprConsent ? gdprConsent.consentString : '')) + + '&us_privacy=' + encodeURIComponent((uspConsent || '')) + + '&coppa=' + (config.getConfig('coppa') === true ? 1 : 0) + }; + }); + return accum.concat(cookieSyncObjects); + } + }, []); } }; + registerBidder(spec); diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 40d3cf84369..385ada65538 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -2,13 +2,40 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; const VERSION = '1.0'; const BIDDER_CODE = 'adyoulike'; const DEFAULT_DC = 'hb-api'; +const CURRENCY = 'USD'; + +const NATIVE_IMAGE = { + image: { + required: true + }, + title: { + required: true + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: false + }, + icon: { + required: false + }, + cta: { + required: false + } +}; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE, VIDEO], aliases: ['ayl'], // short code /** * Determines whether or not the given bid request is valid. @@ -18,10 +45,11 @@ export const spec = { */ isBidRequestValid: function (bid) { const sizes = getSize(getSizeArray(bid)); - if (!bid.params || !bid.params.placement || !sizes.width || !sizes.height) { - return false; - } - return true; + const sizeValid = sizes.width > 0 && sizes.height > 0; + + // allows no size fornative only + return (bid.params && bid.params.placement && + (sizeValid || (bid.mediaTypes && bid.mediaTypes.native))); }, /** * Make a server request from the list of BidRequests. @@ -32,15 +60,29 @@ export const spec = { buildRequests: function (bidRequests, bidderRequest) { const payload = { Version: VERSION, - Bids: bidRequests.reduce((accumulator, bid) => { - let sizesArray = getSizeArray(bid); + Bids: bidRequests.reduce((accumulator, bidReq) => { + let mediatype = getMediatype(bidReq); + let sizesArray = getSizeArray(bidReq); let size = getSize(sizesArray); - accumulator[bid.bidId] = {}; - accumulator[bid.bidId].PlacementID = bid.params.placement; - accumulator[bid.bidId].TransactionID = bid.transactionId; - accumulator[bid.bidId].Width = size.width; - accumulator[bid.bidId].Height = size.height; - accumulator[bid.bidId].AvailableSizes = sizesArray.join(','); + accumulator[bidReq.bidId] = {}; + accumulator[bidReq.bidId].PlacementID = bidReq.params.placement; + accumulator[bidReq.bidId].TransactionID = bidReq.transactionId; + accumulator[bidReq.bidId].Width = size.width; + accumulator[bidReq.bidId].Height = size.height; + accumulator[bidReq.bidId].AvailableSizes = sizesArray.join(','); + if (typeof bidReq.getFloor === 'function') { + accumulator[bidReq.bidId].Pricing = getFloor(bidReq, size, mediatype); + } + if (mediatype === NATIVE) { + let nativeReq = bidReq.mediaTypes.native; + if (nativeReq.type === 'image') { + nativeReq = Object.assign({}, NATIVE_IMAGE, nativeReq); + } + accumulator[bidReq.bidId].Native = nativeReq; + } + if (mediatype === VIDEO) { + accumulator[bidReq.bidId].Video = bidReq.mediaTypes.video; + } return accumulator; }, {}), PageRefreshed: getPageRefreshed() @@ -122,6 +164,31 @@ function getCanonicalUrl() { return ''; } +/* Get mediatype from bidRequest */ +function getMediatype(bidRequest) { + var type = BANNER; + + if (utils.deepAccess(bidRequest, 'mediaTypes.native')) { + type = NATIVE; + } else if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { + type = VIDEO; + } + + return type; +} +/* Get Floor price information */ +function getFloor(bidRequest, size, mediaType) { + const bidFloors = bidRequest.getFloor({ + currency: CURRENCY, + mediaType, + size: [ size.width, size.height ] + }); + + if (!isNaN(bidFloors.floor) && (bidFloors.currency === CURRENCY)) { + return bidFloors.floor; + } +} + /* Get information on page refresh */ function getPageRefreshed() { try { @@ -171,9 +238,9 @@ function createEndpointQS(bidderRequest) { } function getSizeArray(bid) { - let inputSize = bid.sizes; + let inputSize = bid.sizes || []; if (bid.mediaTypes && bid.mediaTypes.banner) { - inputSize = bid.mediaTypes.banner.sizes; + inputSize = bid.mediaTypes.banner.sizes || []; } return utils.parseSizesInput(inputSize); @@ -203,34 +270,196 @@ function getSize(sizesArray) { return parsed; } +function getInternalImgUrl(uid) { + if (!uid) return ''; + return 'https://blobs.omnitagjs.com/blobs/' + uid.substr(16, 2) + '/' + uid.substr(16) + '/' + uid; +} + +function getImageUrl(config, resource, width, height) { + let url = ''; + + switch (resource.Kind) { + case 'INTERNAL': + url = getInternalImgUrl(resource.Data.Internal.BlobReference.Uid); + + break; + + case 'EXTERNAL': + const dynPrefix = config.DynamicPrefix; + let extUrl = resource.Data.External.Url; + extUrl = extUrl.replace(/\[height\]/i, '' + height); + extUrl = extUrl.replace(/\[width\]/i, '' + width); + + if (extUrl.indexOf(dynPrefix) >= 0) { + const urlmatch = (/.*url=([^&]*)/gm).exec(extUrl); + url = urlmatch ? urlmatch[1] : ''; + if (!url) { + url = getInternalImgUrl((/.*key=([^&]*)/gm).exec(extUrl)[1]); + } + } else { + url = extUrl; + } + + break; + } + + return url; +} + +function getTrackers(eventsArray, jsTrackers) { + const result = []; + + if (!eventsArray) return result; + + eventsArray.map((item, index) => { + if ((jsTrackers && item.Kind === 'JAVASCRIPT_URL') || + (!jsTrackers && item.Kind === 'PIXEL_URL')) { + result.push(item.Url); + } + }); + return result; +} + +function getVideoAd(response) { + var adJson = {}; + if (typeof response.Ad === 'string') { + adJson = JSON.parse(response.Ad.match(/\/\*PREBID\*\/(.*)\/\*PREBID\*\//)[1]); + return utils.deepAccess(adJson, 'Content.MainVideo.Vast'); + } +} + +function getNativeAssets(response, nativeConfig) { + const native = {}; + + var adJson = {}; + var textsJson = {}; + if (typeof response.Ad === 'string') { + adJson = JSON.parse(response.Ad.match(/\/\*PREBID\*\/(.*)\/\*PREBID\*\//)[1]); + textsJson = adJson.Content.Preview.Text; + + var impressionUrl = adJson.TrackingPrefix + + '/pixel?event_kind=IMPRESSION&attempt=' + adJson.Attempt; + + if (adJson.Campaign) { + impressionUrl += '&campaign=' + adJson.Campaign; + } + + native.clickUrl = adJson.TrackingPrefix + '/ar?event_kind=CLICK&attempt=' + adJson.Attempt + + '&campaign=' + adJson.Campaign + '&url=' + encodeURIComponent(adJson.Content.Landing.Url); + + if (adJson.OnEvents) { + native.clickTrackers = getTrackers(adJson.OnEvents['CLICK']); + native.impressionTrackers = getTrackers(adJson.OnEvents['IMPRESSION']); + native.javascriptTrackers = getTrackers(adJson.OnEvents['IMPRESSION'], true); + } else { + native.impressionTrackers = []; + } + + native.impressionTrackers.push(impressionUrl); + } + + Object.keys(nativeConfig).map(function(key, index) { + if (typeof response.Native === 'object') { + native[key] = response.Native[key]; + } else { + switch (key) { + case 'title': + native[key] = textsJson.TITLE; + break; + case 'body': + native[key] = textsJson.DESCRIPTION; + break; + case 'cta': + native[key] = textsJson.CALLTOACTION; + break; + case 'sponsoredBy': + native[key] = adJson.Content.Preview.Sponsor.Name; + break; + case 'image': + // main image requested size + const imgSize = nativeConfig.image.sizes || []; + if (!imgSize.length) { + imgSize[0] = response.Width || 300; + imgSize[1] = response.Height || 250; + } + + native[key] = { + url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), + width: imgSize[0], + height: imgSize[1] + }; + break; + case 'icon': + if (adJson.HasSponsorImage) { + // icon requested size + const iconSize = nativeConfig.icon.sizes || []; + if (!iconSize.length) { + iconSize[0] = 50; + iconSize[1] = 50; + } + + native[key] = { + url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), + width: iconSize[0], + height: iconSize[1] + }; + } + break; + case 'privacyIcon': + native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); + break; + case 'privacyLink': + native[key] = adJson.Content.Preview.Credit.Url; + break; + } + } + }); + + return native; +} + /* Create bid from response */ function createBid(response, bidRequests) { - if (!response || !response.Ad) { + if (!response || (!response.Ad && !response.Native)) { return } + const request = bidRequests && bidRequests[response.BidID]; + // In case we don't retreive the size from the adserver, use the given one. - if (bidRequests && bidRequests[response.BidID]) { + if (request) { if (!response.Width || response.Width === '0') { - response.Width = bidRequests[response.BidID].Width; + response.Width = request.Width; } if (!response.Height || response.Height === '0') { - response.Height = bidRequests[response.BidID].Height; + response.Height = request.Height; } } - return { + const bid = { requestId: response.BidID, - width: response.Width, - height: response.Height, - ad: response.Ad, ttl: 3600, creativeId: response.CreativeID, cpm: response.Price, netRevenue: true, - currency: 'USD' + currency: CURRENCY }; + + if (request && request.Native) { + bid.native = getNativeAssets(response, request.Native); + bid.mediaType = 'native'; + } else if (request && request.Video) { + const vast64 = response.Vast || getVideoAd(response); + bid.vastXml = vast64 ? window.atob(vast64) : ''; + bid.mediaType = 'video'; + } else { + bid.width = response.Width; + bid.height = response.Height; + bid.ad = response.Ad; + } + + return bid; } registerBidder(spec); diff --git a/modules/adyoulikeBidAdapter.md b/modules/adyoulikeBidAdapter.md index 120bee3bcbb..edb47d25637 100644 --- a/modules/adyoulikeBidAdapter.md +++ b/modules/adyoulikeBidAdapter.md @@ -7,23 +7,57 @@ Maintainer: prebid@adyoulike.com # Description Module that connects to Adyoulike demand sources. -Banner formats are supported. +Banner, Native and Video ad formats are supported. # Test Parameters ``` - var adUnits = [ - { - code: 'test-div', - sizes: [[300, 250]], - bids: [ - { - bidder: "adyoulike", - params: { - placement: 194f787b85c829fb8822cdaf1ae64435, - DC: 'fra01', // Optional for set the data center name - } - } - ] - } - ]; + var adUnits = { + "code": "test-div", + "mediaTypes": { + "banner": { + "sizes": ["300x250"] + }, + "video": { + context: "instream", + playerSize: [[640, 480]] + }, + "native": { + "image": { + "required": true, + }, + "title": { + "required": true, + "len": 80 + }, + "cta": { + "required": false + }, + "sponsoredBy": { + "required": true + }, + "clickUrl": { + "required": true + }, + "privacyIcon": { + "required": false + }, + "privacyLink": { + "required": false + }, + "body": { + "required": true + }, + "icon": { + "required": true, + "sizes": [] + } + } + }, + bids: [{ + bidder: "adyoulike", + params: { + placement: "e622af275681965d3095808561a1e510" + } + }] + }; ``` diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index a1fa202c154..d48245e9604 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -1,6 +1,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { parseUrl, deepAccess, _each, formatQS, getUniqueIdentifierStr, triggerPixel } from '../src/utils.js'; +import { parseUrl, deepAccess, _each, formatQS, getUniqueIdentifierStr, triggerPixel, isFn, logError } from '../src/utils.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -8,8 +8,7 @@ const BIDDER_CODE = 'amx'; const storage = getStorageManager(737, BIDDER_CODE); const SIMPLE_TLD_TEST = /\.com?\.\w{2,4}$/; const DEFAULT_ENDPOINT = 'https://prebid.a-mo.net/a/c'; -const VERSION = 'pba1.2.1'; -const xmlDTDRxp = /^\s*<\?xml[^\?]+\?>/; +const VERSION = 'pba1.3.1'; const VAST_RXP = /^\s*<\??(?:vast|xml)/i; const TRACKING_ENDPOINT = 'https://1x1.a-mo.net/hbx/'; const AMUID_KEY = '__amuidpb'; @@ -45,11 +44,16 @@ function flatMap(input, mapFn) { .reduce((acc, item) => item != null && acc.concat(item), []) } -const generateDTD = (xmlDocument) => - ``; - const isVideoADM = (html) => html != null && VAST_RXP.test(html); -const getMediaType = (bid) => isVideoADM(bid.adm) ? VIDEO : BANNER; + +function getMediaType(bid) { + if (isVideoADM(bid.adm)) { + return VIDEO; + } + + return BANNER; +} + const nullOrType = (value, type) => value == null || (typeof value) === type // eslint-disable-line valid-typeof @@ -103,6 +107,32 @@ const trackEvent = (eventName, data) => eid: getUniqueIdentifierStr(), })}`); +const DEFAULT_MIN_FLOOR = 0; + +function ensureFloor(floorValue) { + return typeof floorValue === 'number' && isFinite(floorValue) && floorValue > 0.0 + ? floorValue : DEFAULT_MIN_FLOOR; +} + +function getFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.floor', DEFAULT_MIN_FLOOR); + } + + try { + const floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + bidRequest: bid + }); + return floor.floor; + } catch (e) { + logError('call to getFloor failed: ', e); + return DEFAULT_MIN_FLOOR; + } +} + function convertRequest(bid) { const size = largestSize(bid.sizes, bid.mediaTypes) || [0, 0]; const isVideoBid = bid.mediaType === VIDEO || VIDEO in bid.mediaTypes @@ -116,16 +146,21 @@ function convertRequest(bid) { bid.sizes, deepAccess(bid, `mediaTypes.${BANNER}.sizes`, []) || [], deepAccess(bid, `mediaTypes.${VIDEO}.sizes`, []) || [], - ] + ]; + + const videoData = deepAccess(bid, `mediaTypes.${VIDEO}`, {}) || {}; const params = { au, av, + vd: videoData, vr: isVideoBid, ms: multiSizes, aw: size[0], ah: size[1], tf: 0, + sc: bid.schain || {}, + f: ensureFloor(getFloor(bid)) }; if (typeof tid === 'string' && tid.length > 0) { @@ -143,52 +178,6 @@ function decorateADM(bid) { return bid.adm + impressions; } -function transformXmlSimple(bid) { - const pixels = [] - _each([bid.nurl].concat(bid.ext != null && bid.ext.himp != null ? bid.ext.himp : []), (pixel) => { - if (pixel != null) { - pixels.push(``) - } - }); - // find the current "Impression" here & slice ours in - const impressionIndex = bid.adm.indexOf(' url != null); - - _each(pixels, (pxl) => { - const imagePixel = doc.createElement('Impression'); - const cdata = doc.createCDATASection(pxl); - imagePixel.appendChild(cdata); - root.appendChild(imagePixel); - }); - - const dtdMatch = xmlDTDRxp.exec(bid.adm); - return (dtdMatch != null ? dtdMatch[0] : generateDTD(doc)) + getOuterHTML(doc.documentElement); -} - function resolveSize(bid, request, bidId) { if (bid.w != null && bid.w > 1 && bid.h != null && bid.h > 1) { return [bid.w, bid.h]; @@ -212,14 +201,16 @@ function values(source) { }); } +const isTrue = (boolValue) => + boolValue === true || boolValue === 1 || boolValue === 'true'; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid(bid) { return nullOrType(deepAccess(bid, 'params.endpoint', null), 'string') && - nullOrType(deepAccess(bid, 'params.tagId', null), 'string') && - nullOrType(deepAccess(bid, 'params.testMode', null), 'boolean'); + nullOrType(deepAccess(bid, 'params.tagId', null), 'string') }, buildRequests(bidRequests, bidderRequest) { @@ -239,8 +230,9 @@ export const spec = { brc: fbid.bidderRequestsCount || 0, bwc: fbid.bidderWinsCount || 0, trc: fbid.bidRequestsCount || 0, - tm: testMode, + tm: isTrue(testMode), V: '$prebid.version$', + vg: '$$PREBID_GLOBAL$$', i: (testMode && tagId != null) ? tagId : getID(loc), l: {}, f: 0.01, @@ -259,7 +251,8 @@ export const spec = { d: '', m: createBidMap(bidRequests), cpp: config.getConfig('coppa') ? 1 : 0, - fpd: config.getConfig('fpd'), + fpd2: config.getConfig('ortb2'), + tmax: config.getConfig('bidderTimeout'), eids: values(bidRequests.reduce((all, bid) => { // we only want unique ones in here if (bid == null || bid.userIdAsEids == null) { @@ -306,7 +299,6 @@ export const spec = { }, interpretResponse(serverResponse, request) { - // validate the body/response const response = serverResponse.body; if (response == null || typeof response === 'string') { return []; @@ -320,13 +312,14 @@ export const spec = { return flatMap(response.r[bidID], (siteBid) => siteBid.b.map((bid) => { const mediaType = getMediaType(bid); - // let ad = null; - let ad = mediaType === BANNER ? decorateADM(bid) : decorateVideoADM(bid); + const ad = mediaType === BANNER ? decorateADM(bid) : bid.adm; + if (ad == null) { return null; } const size = resolveSize(bid, request.data, bidID); + const defaultExpiration = mediaType === BANNER ? 240 : 300; return ({ requestId: bidID, @@ -341,7 +334,8 @@ export const spec = { advertiserDomains: bid.adomain, mediaType, }, - ttl: mediaType === VIDEO ? 90 : 70 + mediaType, + ttl: typeof bid.exp === 'number' ? bid.exp : defaultExpiration, }); })).filter((possibleBid) => possibleBid != null); }); diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 2582e4788c1..62ae3f54125 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -1,4 +1,5 @@ import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'apacdex'; const CONFIG = { @@ -49,11 +50,34 @@ export const spec = { }, buildRequests: function (validBidRequests, bidderRequest) { + let siteId; + let schain; + let eids; + let geo; + let test; + var bids = JSON.parse(JSON.stringify(validBidRequests)) bidderConfig = CONFIG[bids[0].bidder]; - const payload = {}; + + test = config.getConfig('debug'); bids.forEach(bidReq => { + siteId = siteId || bidReq.params.siteId; + + if (bidReq.schain) { + schain = schain || bidReq.schain + } + + if (bidReq.userIdAsEids) { + eids = eids || bidReq.userIdAsEids + } + + if (bidReq.params && bidReq.params.geo) { + if (validateGeoObject(bidReq.params.geo)) { + geo = bidReq.params.geo; + } + } + var targetKey = 0; if (bySlotTargetKey[bidReq.adUnitCode] != undefined) { targetKey = bySlotTargetKey[bidReq.adUnitCode]; @@ -73,36 +97,55 @@ export const spec = { bidReq.targetKey = targetKey; }); + const payload = {}; + payload.tmax = bidderRequest.timeout; + if (test) { + payload.test = 1; + } + payload.device = {}; payload.device.ua = navigator.userAgent; - payload.device.height = window.top.innerHeight; - payload.device.width = window.top.innerWidth; + payload.device.height = window.screen.width; + payload.device.width = window.screen.height; payload.device.dnt = _getDoNotTrack(); payload.device.language = navigator.language; + var pageUrl = _extractTopWindowUrlFromBidderRequest(bidderRequest); payload.site = {}; - payload.site.id = bids[0].params.siteId; - payload.site.page = _extractTopWindowUrlFromBidderRequest(bidderRequest); + payload.site.id = siteId; + payload.site.page = pageUrl payload.site.referrer = _extractTopWindowReferrerFromBidderRequest(bidderRequest); - payload.site.hostname = window.top.location.hostname; + payload.site.hostname = getDomain(pageUrl); // Apply GDPR parameters to request. - payload.gdpr = {}; if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdpr = {}; payload.gdpr.gdprApplies = !!bidderRequest.gdprConsent.gdprApplies; if (bidderRequest.gdprConsent.consentString) { payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; } } - // Apply schain. - if (bids[0].schain) { - payload.schain = bids[0].schain - } + // Apply us_privacy. if (bidderRequest && bidderRequest.uspConsent) { payload.us_privacy = bidderRequest.uspConsent; } + // Apply schain. + if (schain) { + payload.schain = schain + } + + // Apply eids. + if (eids) { + payload.eids = eids + } + + // Apply geo + if (geo) { + payload.geo = geo; + } + payload.bids = bids; return { @@ -115,12 +158,12 @@ export const spec = { }, interpretResponse: function (serverResponse, bidRequest) { const serverBody = serverResponse.body; - const serverBids = serverBody.bids; - // check overall response - if (!serverBody || typeof serverBody !== 'object') { + if (!serverBody || !utils.isPlainObject(serverBody)) { return []; } - if (!serverBids || typeof serverBids !== 'object') { + + const serverBids = serverBody.bids; + if (!serverBids || !utils.isArray(serverBids)) { return []; } @@ -192,15 +235,25 @@ function _getBiggestSize(sizes) { } function _getDoNotTrack() { - if (window.top.doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack) { - if (window.top.doNotTrack == '1' || navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') { + try { + if (window.top.doNotTrack && window.top.doNotTrack == '1') { return 1; - } else { - return 0; } - } else { - return 0; - } + } catch (e) { } + + try { + if (navigator.doNotTrack && (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1')) { + return 1; + } + } catch (e) { } + + try { + if (navigator.msDoNotTrack && navigator.msDoNotTrack == '1') { + return 1; + } + } catch (e) { } + + return 0 } /** @@ -210,8 +263,11 @@ function _getDoNotTrack() { * @returns {string} */ function _extractTopWindowUrlFromBidderRequest(bidderRequest) { - if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl')) { - return bidderRequest.refererInfo.canonicalUrl; + if (config.getConfig('pageUrl')) { + return config.getConfig('pageUrl'); + } + if (utils.deepAccess(bidderRequest, 'refererInfo.referer')) { + return bidderRequest.refererInfo.referer; } try { @@ -239,4 +295,45 @@ function _extractTopWindowReferrerFromBidderRequest(bidderRequest) { } } +/** + * Extracts the domain from given page url + * + * @param {string} url + * @returns {string} + */ +export function getDomain(pageUrl) { + if (config.getConfig('publisherDomain')) { + var publisherDomain = config.getConfig('publisherDomain'); + return publisherDomain.replace('http://', '').replace('https://', '').replace('www.', '').split(/[/?#:]/)[0]; + } + + if (!pageUrl) { + return pageUrl; + } + + return pageUrl.replace('http://', '').replace('https://', '').replace('www.', '').split(/[/?#:]/)[0]; +} + +/** + * Validate geo object + * + * @param {Object} geo + * @returns {boolean} + */ +export function validateGeoObject(geo) { + if (!utils.isPlainObject(geo)) { + return false; + } + if (!geo.lat) { + return false; + } + if (!geo.lon) { + return false; + } + if (!geo.accuracy) { + return false; + } + return true; +} + registerBidder(spec); diff --git a/modules/avocetBidAdapter.js b/modules/avocetBidAdapter.js index 7a9e5062c0f..1283bb865d4 100644 --- a/modules/avocetBidAdapter.js +++ b/modules/avocetBidAdapter.js @@ -53,7 +53,7 @@ export const spec = { const publisherDomain = config.getConfig('publisherDomain'); // First-party data from config - const fpd = config.getConfig('fpd'); + const fpd = config.getLegacyFpd(config.getConfig('ortb2')); // GDPR status and TCF consent string let tcfConsentString; diff --git a/modules/axonixBidAdapter.js b/modules/axonixBidAdapter.js new file mode 100644 index 00000000000..224486467f3 --- /dev/null +++ b/modules/axonixBidAdapter.js @@ -0,0 +1,186 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; + +const BIDDER_CODE = 'axonix'; +const BIDDER_VERSION = '1.0.1'; + +const CURRENCY = 'USD'; +const DEFAULT_REGION = 'us-east-1'; + +function getBidFloor(bidRequest) { + let floorInfo = {}; + + if (typeof bidRequest.getFloor === 'function') { + floorInfo = bidRequest.getFloor({ + currency: CURRENCY, + mediaType: '*', + size: '*' + }); + } + + return floorInfo.floor || 0; +} + +function getPageUrl(bidRequest, bidderRequest) { + let pageUrl = config.getConfig('pageUrl'); + + if (bidRequest.params.referrer) { + pageUrl = bidRequest.params.referrer; + } else if (!pageUrl) { + pageUrl = bidderRequest.refererInfo.referer; + } + + return bidRequest.params.secure ? pageUrl.replace(/^http:/i, 'https:') : pageUrl; +} + +function isMobile() { + return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); +} + +function isConnectedTV() { + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); +} + +function getURL(params, path) { + let { supplyId, region, endpoint } = params; + let url; + + if (endpoint) { + url = endpoint; + } else if (region) { + url = `https://openrtb-${region}.axonix.com/supply/${path}/${supplyId}`; + } else { + url = `https://openrtb-${DEFAULT_REGION}.axonix.com/supply/${path}/${supplyId}` + } + + return url; +} + +export const spec = { + code: BIDDER_CODE, + version: BIDDER_VERSION, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: function(bid) { + // video bid request validation + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + if (!bid.mediaTypes[VIDEO].hasOwnProperty('mimes') || + !utils.isArray(bid.mediaTypes[VIDEO].mimes) || + bid.mediaTypes[VIDEO].mimes.length === 0) { + utils.logError('mimes are mandatory for video bid request. Ad Unit: ', JSON.stringify(bid)); + + return false; + } + } + + return !!(bid.params && bid.params.supplyId); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + // device.connectiontype + let connection = window.navigator && (window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection) + let connectionType = 'unknown'; + let effectiveType = ''; + + if (connection) { + if (connection.type) { + connectionType = connection.type; + } + + if (connection.effectiveType) { + effectiveType = connection.effectiveType; + } + } + + const requests = validBidRequests.map(validBidRequest => { + // app/site + let app; + let site; + + if (typeof config.getConfig('app') === 'object') { + app = config.getConfig('app'); + } else { + site = { + page: getPageUrl(validBidRequest, bidderRequest) + } + } + + const data = { + app, + site, + validBidRequest, + connectionType, + effectiveType, + devicetype: isMobile() ? 1 : isConnectedTV() ? 3 : 2, + bidfloor: getBidFloor(validBidRequest), + dnt: (navigator.doNotTrack === 'yes' || navigator.doNotTrack === '1' || navigator.msDoNotTrack === '1') ? 1 : 0, + language: navigator.language, + prebidVersion: '$prebid.version$', + screenHeight: screen.height, + screenWidth: screen.width, + tmax: config.getConfig('bidderTimeout'), + ua: navigator.userAgent, + }; + + return { + method: 'POST', + url: getURL(validBidRequest.params, 'prebid'), + options: { + withCredentials: false, + contentType: 'application/json' + }, + data + }; + }); + + return requests; + }, + + interpretResponse: function(serverResponse) { + if (!utils.isArray(serverResponse)) { + return []; + } + + const responses = []; + + for (const response of serverResponse) { + if (response.requestId) { + responses.push(Object.assign(response, { + ttl: config.getConfig('_bidderTimeout') + })); + } + } + + return responses; + }, + + onTimeout: function(timeoutData) { + const params = utils.deepAccess(timeoutData, '0.params.0'); + + if (!utils.isEmpty(params)) { + ajax(getURL(params, 'prebid/timeout'), null, timeoutData[0], { + method: 'POST', + options: { + withCredentials: false, + contentType: 'application/json' + } + }); + } + }, + + onBidWon: function(bids) { + for (const bid of bids) { + const { nurl } = bid || {}; + + if (bid.nurl) { + utils.replaceAuctionPrice(nurl, bid.cpm) + utils.triggerPixel(nurl); + }; + } + } +} + +registerBidder(spec); diff --git a/modules/axonixBidAdapter.md b/modules/axonixBidAdapter.md new file mode 100644 index 00000000000..1ff59f17828 --- /dev/null +++ b/modules/axonixBidAdapter.md @@ -0,0 +1,140 @@ +# Overview + +``` +Module Name : Axonix Bidder Adapter +Module Type : Bidder Adapter +Maintainer : support-prebid@axonix.com +``` + +# Description + +Module that connects to Axonix's exchange for bids. + +# Parameters + +| Name | Scope | Description | Example | +| :------------ | :------- | :---------------------------------------------- | :------------------------------------- | +| `supplyId` | required | Supply UUID | `"2c426f78-bb18-4a16-abf4-62c6cd0ee8de"` | +| `region` | optional | Cloud region | `"us-east-1"` | +| `endpoint` | optional | Supply custom endpoint | `"https://open-rtb.axonix.com/custom"` | +| `instl` | optional | Set to 1 if using interstitial (default: 0) | `1` | + +# Test Parameters + +## Banner + +```javascript +var bannerAdUnit = { + code: 'test-banner', + mediaTypes: { + banner: { + sizes: [[120, 600], [300, 250], [320, 50], [468, 60], [728, 90]] + } + }, + bids: [{ + bidder: 'axonix', + params: { + supplyId: 'abc', + region: 'def', + endpoint: 'url' + } + }] +}; +``` + +## Video + +```javascript +var videoAdUnit = { + code: 'test-video', + mediaTypes: { + video: { + protocols: [1, 2, 3, 4, 5, 6, 7, 8] + } + }, + bids: [{ + bidder: 'axonix', + params: { + supplyId: 'abc', + region: 'def', + endpoint: 'url' + } + }] +}; +``` + +## Native + +```javascript +var nativeAdUnit = { + code: 'test-native', + mediaTypes: { + native: { + + } + }, + bids: [{ + bidder: 'axonix', + params: { + supplyId: 'abc', + region: 'def', + endpoint: 'url' + } + }] +}; +``` + +## Multiformat + +```javascript +var adUnits = [ +{ + code: 'test-banner', + mediaTypes: { + banner: { + sizes: [[120, 600], [300, 250], [320, 50], [468, 60], [728, 90]] + } + }, + bids: [{ + bidder: 'axonix', + params: { + supplyId: 'abc', + region: 'def', + endpoint: 'url' + } + }] +}, +{ + code: 'test-video', + mediaTypes: { + video: { + protocols: [1, 2, 3, 4, 5, 6, 7, 8] + } + }, + bids: [{ + bidder: 'axonix', + params: { + supplyId: 'abc', + region: 'def', + endpoint: 'url' + } + }] +}, +{ + code: 'test-native', + mediaTypes: { + native: { + + } + }, + bids: [{ + bidder: 'axonix', + params: { + supplyId: 'abc', + region: 'def', + endpoint: 'url' + } + }] +} +]; +``` diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js index 6db35f184ca..44f5cdf4384 100644 --- a/modules/bidglassBidAdapter.js +++ b/modules/bidglassBidAdapter.js @@ -46,7 +46,7 @@ export const spec = { return window === window.top ? window.location.href : window.parent === window.top ? document.referrer : null; }; let getOrigins = function() { - var ori = ['https://' + window.location.hostname]; + var ori = [window.location.protocol + '//' + window.location.hostname]; if (window.location.ancestorOrigins) { for (var i = 0; i < window.location.ancestorOrigins.length; i++) { @@ -56,7 +56,7 @@ export const spec = { // Derive the parent origin var parts = document.referrer.split('/'); - ori.push('https://' + parts[2]); + ori.push(parts[0] + '//' + parts[2]); if (window.parent !== window.top) { // Additional unknown origins exist @@ -67,15 +67,30 @@ export const spec = { return ori; }; + let bidglass = window['bidglass']; + utils._each(validBidRequests, function(bid) { bid.sizes = ((utils.isArray(bid.sizes) && utils.isArray(bid.sizes[0])) ? bid.sizes : [bid.sizes]); bid.sizes = bid.sizes.filter(size => utils.isArray(size)); - // Stuff to send: [bid id, sizes, adUnitId] + var adUnitId = utils.getBidIdParameter('adUnitId', bid.params); + var options = utils.deepClone(bid.params); + + delete options.adUnitId; + + // Merge externally set targeting params + if (typeof bidglass === 'object' && bidglass.getTargeting) { + let targeting = bidglass.getTargeting(adUnitId, options.targeting); + + if (targeting && Object.keys(targeting).length > 0) options.targeting = targeting; + } + + // Stuff to send: [bid id, sizes, adUnitId, options] imps.push({ bidId: bid.bidId, sizes: bid.sizes, - adUnitId: utils.getBidIdParameter('adUnitId', bid.params) + adUnitId: adUnitId, + options: options }); }); diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js index 80d2f6b5395..2af9a7afed2 100644 --- a/modules/bizzclickBidAdapter.js +++ b/modules/bizzclickBidAdapter.js @@ -81,10 +81,12 @@ export const spec = { let data = { id: bidRequest.bidId, test: config.getConfig('debug') ? 1 : 0, + at: 1, cur: ['USD'], device: { w: winTop.screen.width, h: winTop.screen.height, + dnt: utils.getDNT() ? 1 : 0, language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '', }, site: { @@ -94,9 +96,26 @@ export const spec = { source: { tid: bidRequest.transactionId }, + regs: { + coppa: config.getConfig('coppa') === true ? 1 : 0, + ext: {} + }, + user: { + ext: {} + }, tmax: bidRequest.timeout, imp: [impObject], }; + if (bidRequest) { + if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) { + utils.deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0); + utils.deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString); + } + + if (bidRequest.uspConsent !== undefined) { + utils.deepSetValue(data, 'regs.ext.us_privacy', bidRequest.uspConsent); + } + } bids.push(data) } return { diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index d1811a1b7d1..4fa2a56a004 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -72,7 +72,7 @@ export const spec = { // EIDS Support if (validBidRequests[0].userId) { - data.user.ext.eids = createEidsArray(validBidRequests[0].userId); + utils.deepSetValue(data, 'user.ext.eids', createEidsArray(validBidRequests[0].userId)); } validBidRequests.map(bid => { diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index e3a6b9eaa12..41cbb0670c8 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -28,7 +28,7 @@ export const spec = { gvlid: GVLID, supportedMediaTypes: [ BANNER, VIDEO, NATIVE ], - /** + /** f * @param {object} bid * @return {boolean} */ @@ -56,10 +56,11 @@ export const spec = { buildRequests: (bidRequests, bidderRequest) => { let url; let data; + let fpd = config.getLegacyFpd(config.getConfig('ortb2')) || {}; Object.assign(bidderRequest, { - publisherExt: config.getConfig('fpd.context'), - userExt: config.getConfig('fpd.user'), + publisherExt: fpd.context, + userExt: fpd.user, ceh: config.getConfig('criteo.ceh') }); @@ -280,8 +281,8 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (bidRequest.params.zoneId) { slot.zoneid = bidRequest.params.zoneId; } - if (bidRequest.fpd && bidRequest.fpd.context) { - slot.ext = bidRequest.fpd.context; + if (utils.deepAccess(bidRequest, 'ortb2Imp.ext')) { + slot.ext = bidRequest.ortb2Imp.ext; } if (bidRequest.params.ext) { slot.ext = Object.assign({}, slot.ext, bidRequest.params.ext); diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index f01c3c2ce9f..ec0a0a2f2e6 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -7,6 +7,7 @@ const BIDDER_CODE = 'districtmDMX'; const DMXURI = 'https://dmx.districtm.io/b/v1'; +const GVLID = 144; const VIDEO_MAPPING = { playback_method: { 'auto_play_sound_on': 1, @@ -19,6 +20,8 @@ const VIDEO_MAPPING = { }; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, + aliases: ['dmx'], supportedFormat: [BANNER, VIDEO], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid(bid) { @@ -41,7 +44,7 @@ export const spec = { nBid.height = nBid.h || height; nBid.ttl = 300; nBid.mediaType = bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner'; - if (nBid.mediaType) { + if (nBid.mediaType === 'video') { nBid.vastXml = cleanVast(nBid.adm, nBid.nurl); nBid.ttl = 3600; } diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js index e61b377eefa..bb838788f07 100644 --- a/modules/fabrickIdSystem.js +++ b/modules/fabrickIdSystem.js @@ -46,7 +46,7 @@ export const fabrickIdSubmodule = { if (window.fabrickMod1) { window.fabrickMod1(configParams, consentData, cacheIdObj); } - if (!configParams || typeof configParams.apiKey !== 'string') { + if (!configParams || !configParams.apiKey || typeof configParams.apiKey !== 'string') { utils.logError('fabrick submodule requires an apiKey.'); return; } @@ -55,28 +55,34 @@ export const fabrickIdSubmodule = { let keysArr = Object.keys(configParams); for (let i in keysArr) { let k = keysArr[i]; - if (k === 'url' || k === 'refererInfo') { + if (k === 'url' || k === 'refererInfo' || (k.length > 3 && k.substring(0, 3) === 'max')) { continue; } let v = configParams[k]; if (Array.isArray(v)) { for (let j in v) { - url += `${k}=${v[j]}&`; + if (typeof v[j] === 'string' || typeof v[j] === 'number') { + url += `${k}=${v[j]}&`; + } } - } else { + } else if (typeof v === 'string' || typeof v === 'number') { url += `${k}=${v}&`; } } // pull off the trailing & url = url.slice(0, -1) const referer = _getRefererInfo(configParams); - const urls = new Set(); - url = truncateAndAppend(urls, url, 'r', referer.referer); + const refs = new Map(); + _setReferrer(refs, referer.referer); if (referer.stack && referer.stack[0]) { - url = truncateAndAppend(urls, url, 'r', referer.stack[0]); + _setReferrer(refs, referer.stack[0]); } - url = truncateAndAppend(urls, url, 'r', referer.canonicalUrl); - url = truncateAndAppend(urls, url, 'r', window.location.href); + _setReferrer(refs, referer.canonicalUrl); + _setReferrer(refs, window.location.href); + + refs.forEach(v => { + url = appendUrl(url, 'r', v, configParams); + }); const resp = function (callback) { const callbacks = { @@ -130,18 +136,48 @@ function _getBaseUrl(configParams) { } } -function truncateAndAppend(urls, url, paramName, s) { - if (s && url.length < 2000) { - if (s.length > 200) { - s = s.substring(0, 200); +function _setReferrer(refs, s) { + if (s) { + // store the longest one for the same URI + const url = s.split('?')[0]; + // OR store the longest one for the same domain + // const url = s.split('?')[0].replace('http://','').replace('https://', '').split('/')[0]; + if (refs.has(url)) { + const prevRef = refs.get(url); + if (s.length > prevRef.length) { + refs.set(url, s); + } + } else { + refs.set(url, s); + } + } +} + +export function appendUrl(url, paramName, s, configParams) { + const maxUrlLen = (configParams && configParams.maxUrlLen) || 2000; + const maxRefLen = (configParams && configParams.maxRefLen) || 1000; + const maxSpaceAvailable = (configParams && configParams.maxSpaceAvailable) || 50; + // make sure we have enough space left to make it worthwhile + if (s && url.length < (maxUrlLen - maxSpaceAvailable)) { + let thisMaxRefLen = maxUrlLen - url.length; + if (thisMaxRefLen > maxRefLen) { + thisMaxRefLen = maxRefLen; } - // Don't send the same url in multiple params - if (!urls.has(s)) { - urls.add(s); - return `${url}&${paramName}=${s}` + + s = `&${paramName}=${encodeURIComponent(s)}`; + + if (s.length >= thisMaxRefLen) { + s = s.substring(0, thisMaxRefLen); + if (s.charAt(s.length - 1) === '%') { + s = s.substring(0, thisMaxRefLen - 1); + } else if (s.charAt(s.length - 2) === '%') { + s = s.substring(0, thisMaxRefLen - 2); + } } + return `${url}${s}` + } else { + return url; } - return url; } submodule('userId', fabrickIdSubmodule); diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index f8d2b70981c..de839219897 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -43,7 +43,7 @@ export const helper = { export const spec = { code: 'gamoshi', - aliases: ['gambid', 'cleanmedia', '9MediaOnline', 'MobfoxX'], + aliases: ['gambid', '9MediaOnline'], supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { diff --git a/modules/geoedgeRtdProvider.md b/modules/geoedgeRtdProvider.md index e4aa046a97d..5414606612c 100644 --- a/modules/geoedgeRtdProvider.md +++ b/modules/geoedgeRtdProvider.md @@ -4,7 +4,7 @@ Module Name: Geoedge Rtd provider Module Type: Rtd Provider Maintainer: guy.books@geoedge.com -The Geoedge Realtime module let pusblishers to block bad ads such as automatic redirects, malware, offensive creatives and landing pages. +The Geoedge Realtime module lets publishers block bad ads such as automatic redirects, malware, offensive creatives and landing pages. To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and cutomer key. ## Integration diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index d9dc8f7641a..5eac4069b21 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -29,7 +29,7 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { const bidRequests = []; - const url = bidderRequest.refererInfo.referer; + const urlInfo = getUrlInfo(bidderRequest.refererInfo); const cur = getCurrencyType(); const dnt = utils.getDNT() ? '1' : '0'; @@ -46,7 +46,8 @@ export const spec = { queryString = utils.tryAppendQueryString(queryString, 'bid', bid); queryString = utils.tryAppendQueryString(queryString, 'ver', ver); queryString = utils.tryAppendQueryString(queryString, 'sid', sid); - queryString = utils.tryAppendQueryString(queryString, 'url', url); + queryString = utils.tryAppendQueryString(queryString, 'url', urlInfo.url); + queryString = utils.tryAppendQueryString(queryString, 'ref', urlInfo.ref); queryString = utils.tryAppendQueryString(queryString, 'cur', cur); queryString = utils.tryAppendQueryString(queryString, 'dnt', dnt); @@ -131,4 +132,31 @@ function getCurrencyType() { return 'JPY'; } +function getUrlInfo(refererInfo) { + return { + url: getUrl(refererInfo), + ref: getReferrer(), + }; +} + +function getUrl(refererInfo) { + if (refererInfo && refererInfo.referer) { + return refererInfo.referer; + } + + try { + return window.top.location.href; + } catch (e) { + return window.location.href; + } +} + +function getReferrer() { + try { + return window.top.document.referrer; + } catch (e) { + return document.referrer; + } +} + registerBidder(spec); diff --git a/modules/gptPreAuction.js b/modules/gptPreAuction.js index 48b72671d6a..ee2b5406453 100644 --- a/modules/gptPreAuction.js +++ b/modules/gptPreAuction.js @@ -26,46 +26,48 @@ export const appendGptSlots = adUnits => { if (matchingAdUnitCode) { const adUnit = adUnitMap[matchingAdUnitCode]; - adUnit.fpd = adUnit.fpd || {}; - adUnit.fpd.context = adUnit.fpd.context || {}; + adUnit.ortb2Imp = adUnit.ortb2Imp || {}; + adUnit.ortb2Imp.ext = adUnit.ortb2Imp.ext || {}; + adUnit.ortb2Imp.ext.data = adUnit.ortb2Imp.ext.data || {}; - const context = adUnit.fpd.context; - context.adServer = context.adServer || {}; - context.adServer.name = 'gam'; - context.adServer.adSlot = slot.getAdUnitPath(); + const context = adUnit.ortb2Imp.ext.data; + context.adserver = context.adserver || {}; + context.adserver.name = 'gam'; + context.adserver.adslot = slot.getAdUnitPath(); } }); }; export const appendPbAdSlot = adUnit => { - adUnit.fpd = adUnit.fpd || {}; - adUnit.fpd.context = adUnit.fpd.context || {}; - const context = adUnit.fpd.context; + adUnit.ortb2Imp = adUnit.ortb2Imp || {}; + adUnit.ortb2Imp.ext = adUnit.ortb2Imp.ext || {}; + adUnit.ortb2Imp.ext.data = adUnit.ortb2Imp.ext.data || {}; + const context = adUnit.ortb2Imp.ext.data; const { customPbAdSlot } = _currentConfig; if (customPbAdSlot) { - context.pbAdSlot = customPbAdSlot(adUnit.code, utils.deepAccess(context, 'adServer.adSlot')); + context.pbadslot = customPbAdSlot(adUnit.code, utils.deepAccess(context, 'adserver.adslot')); return; } // use context.pbAdSlot if set - if (context.pbAdSlot) { + if (context.pbadslot) { return; } // use data attribute 'data-adslotid' if set try { const adUnitCodeDiv = document.getElementById(adUnit.code); if (adUnitCodeDiv.dataset.adslotid) { - context.pbAdSlot = adUnitCodeDiv.dataset.adslotid; + context.pbadslot = adUnitCodeDiv.dataset.adslotid; return; } } catch (e) {} // banner adUnit, use GPT adunit if defined - if (context.adServer) { - context.pbAdSlot = context.adServer.adSlot; + if (utils.deepAccess(context, 'adserver.adslot')) { + context.pbadslot = context.adserver.adslot; return; } - context.pbAdSlot = adUnit.code; + context.pbadslot = adUnit.code; }; export const makeBidRequestsHook = (fn, adUnits, ...args) => { diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 6e610b67e0e..964b34dcfa2 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -180,8 +180,8 @@ export const spec = { } const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + 'user': utils.deepAccess(config.getConfig('ortb2.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('ortb2.site'), 'keywords') || null }); if (configKeywords.length) { @@ -329,7 +329,6 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { if (bid) { const bidResponse = { requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, @@ -365,7 +364,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { } function createVideoRequest(bid, mediaType) { - const {playerSize, mimes, durationRangeSec} = mediaType; + const {playerSize, mimes, durationRangeSec, protocols} = mediaType; const size = (playerSize || bid.sizes || [])[0]; if (!size) return; @@ -380,6 +379,10 @@ function createVideoRequest(bid, mediaType) { result.maxduration = durationRangeSec[1]; } + if (protocols && protocols.length) { + result.protocols = protocols; + } + return result; } diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index ffd6c1b250c..af1e9f84f43 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -134,7 +134,6 @@ export const spec = { } const bidResponse = { requestId: bid.bidId, - bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 2b0f7e03d22..9a01cd21fa4 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -208,25 +208,24 @@ function _getVidParams (attributes) { * @param {Object} bid * @returns {Number} floor */ -function _getFloor (mediaTypes, bidfloor, bid) { +function _getFloor (mediaTypes, staticBidfloor, bid) { const curMediaType = Object.keys(mediaTypes)[0] || 'banner'; - let floor = bidfloor || 0; + const bidFloor = { floor: 0, currency: 'USD' }; if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({ - currency: 'USD', + const { currency, floor } = bid.getFloor({ mediaType: curMediaType, size: '*' }); + floor && (bidFloor.floor = floor); + currency && (bidFloor.currency = currency); - if (typeof floorInfo === 'object' && - floorInfo.currency === 'USD' && - !isNaN(parseFloat(floorInfo.floor))) { - floor = Math.max(floor, parseFloat(floorInfo.floor)); + if (staticBidfloor && floor && currency === 'USD') { + bidFloor.floor = Math.max(staticBidfloor, parseFloat(floor)); } } - return floor; + return bidFloor; } /** @@ -250,7 +249,7 @@ function buildRequests (validBidRequests, bidderRequest) { transactionId, userId = {} } = bidRequest; - const bidFloor = _getFloor(mediaTypes, params.bidfloor, bidRequest); + const { currency, floor } = _getFloor(mediaTypes, params.bidfloor, bidRequest); let sizes = [1, 1]; let data = {}; @@ -265,8 +264,9 @@ function buildRequests (validBidRequests, bidderRequest) { data.pv = pageViewId; } - if (bidFloor) { - data.fp = bidFloor; + if (floor) { + data.fp = floor; + data.fpc = currency; } if (params.iriscat && typeof params.iriscat === 'string') { diff --git a/modules/h12mediaBidAdapter.js b/modules/h12mediaBidAdapter.js index 0d2c22a3f68..7b736780226 100644 --- a/modules/h12mediaBidAdapter.js +++ b/modules/h12mediaBidAdapter.js @@ -1,6 +1,5 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'h12media'; const DEFAULT_URL = 'https://bidder.h12-media.com/prebid/'; const DEFAULT_CURRENCY = 'USD'; @@ -16,21 +15,31 @@ export const spec = { }, buildRequests: function(validBidRequests, bidderRequest) { - const requestUrl = validBidRequests[0].params.endpointdom || DEFAULT_URL; - const isiframe = !((window.self === window.top) || window.frameElement); + const isiframe = utils.inIframe(); const screenSize = getClientDimensions(); const docSize = getDocumentDimensions(); - const bidrequests = validBidRequests.map((bidRequest) => { + return validBidRequests.map((bidRequest) => { const bidderParams = bidRequest.params; - const adUnitElement = document.getElementById(bidRequest.adUnitCode); + const requestUrl = bidderParams.endpointdom || DEFAULT_URL; + let pubsubid = bidderParams.pubsubid || ''; + if (pubsubid && pubsubid.length > 32) { + utils.logError('Bidder param \'pubsubid\' should be not more than 32 chars.'); + pubsubid = ''; + } + const pubcontainerid = bidderParams.pubcontainerid; + const adUnitElement = document.getElementById(pubcontainerid || bidRequest.adUnitCode); const ishidden = !isVisible(adUnitElement); - const coords = { + const framePos = getFramePos(); + const coords = isiframe ? { + x: framePos[0], + y: framePos[1], + } : { x: adUnitElement && adUnitElement.getBoundingClientRect().x, y: adUnitElement && adUnitElement.getBoundingClientRect().y, }; - return { + const bidrequest = { bidId: bidRequest.bidId, transactionId: bidRequest.transactionId, adunitId: bidRequest.adUnitCode, @@ -40,33 +49,46 @@ export const spec = { adunitSize: bidRequest.mediaTypes.banner.sizes || [], coords, ishidden, + pubsubid, + pubcontainerid, }; - }); - return { - method: 'POST', - url: requestUrl, - options: {withCredentials: false}, - data: { - gdpr: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? Boolean(bidderRequest.gdprConsent.gdprApplies & 1) : false, - gdpr_cs: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? bidderRequest.gdprConsent.consentString : '', - topLevelUrl: window.top.location.href, - refererUrl: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer : '', - isiframe, - version: '$prebid.version$', - visitorInfo: { - localTime: getLocalDateFormatted(), - dayOfWeek: new Date().getDay(), - screenWidth: screenSize[0], - screenHeight: screenSize[1], - docWidth: docSize[0], - docHeight: docSize[1], - scrollbarx: window.scrollX, - scrollbary: window.scrollY, + let windowTop; + try { + windowTop = window.top; + } catch (e) { + utils.logMessage(e); + windowTop = window; + } + + return { + method: 'POST', + url: requestUrl, + options: {withCredentials: true}, + data: { + gdpr: !!utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies', false), + gdpr_cs: utils.deepAccess(bidderRequest, 'gdprConsent.consentString', ''), + usp: !!utils.deepAccess(bidderRequest, 'uspConsent', false), + usp_cs: utils.deepAccess(bidderRequest, 'uspConsent', ''), + topLevelUrl: utils.deepAccess(bidderRequest, 'refererInfo.referer', ''), + refererUrl: windowTop.document.referrer, + isiframe, + version: '$prebid.version$', + ExtUserIDs: bidRequest.userId, + visitorInfo: { + localTime: getLocalDateFormatted(), + dayOfWeek: new Date().getDay(), + screenWidth: screenSize[0], + screenHeight: screenSize[1], + docWidth: docSize[0], + docHeight: docSize[1], + scrollbarx: windowTop.scrollX, + scrollbary: windowTop.scrollY, + }, + bidrequest, }, - bidrequests, - }, - }; + }; + }); }, interpretResponse: function(serverResponse, bidRequests) { @@ -74,29 +96,28 @@ export const spec = { try { const serverBody = serverResponse.body; if (serverBody) { - if (serverBody.bids) { - serverBody.bids.forEach(bidBody => { - const bidRequest = find(bidRequests.data.bidrequests, bid => bid.bidId === bidBody.bidId); - const bidResponse = { - currency: serverBody.currency || DEFAULT_CURRENCY, - netRevenue: serverBody.netRevenue || DEFAULT_NET_REVENUE, - ttl: serverBody.ttl || DEFAULT_TTL, - requestId: bidBody.bidId, - cpm: bidBody.cpm, - width: bidBody.width, - height: bidBody.height, - creativeId: bidBody.creativeId, - ad: bidBody.ad, - meta: bidBody.meta, - mediaType: 'banner', - }; - if (bidRequest) { - bidResponse.pubid = bidRequest.pubid; - bidResponse.placementid = bidRequest.placementid; - bidResponse.size = bidRequest.size; - } - bidResponses.push(bidResponse); - }); + if (serverBody.bid) { + const bidBody = serverBody.bid; + const bidRequest = bidRequests.data.bidrequest; + const bidResponse = { + currency: serverBody.currency || DEFAULT_CURRENCY, + netRevenue: serverBody.netRevenue || DEFAULT_NET_REVENUE, + ttl: serverBody.ttl || DEFAULT_TTL, + requestId: bidBody.bidId, + cpm: bidBody.cpm, + width: bidBody.width, + height: bidBody.height, + creativeId: bidBody.creativeId, + ad: bidBody.ad, + meta: bidBody.meta, + mediaType: 'banner', + }; + if (bidRequest) { + bidResponse.pubid = bidRequest.pubid; + bidResponse.placementid = bidRequest.placementid; + bidResponse.size = bidRequest.size; + } + bidResponses.push(bidResponse); } } return bidResponses; @@ -105,47 +126,50 @@ export const spec = { } }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { - const serverBody = serverResponses[0].body; + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, usPrivacy) { const syncs = []; + const uspApplies = !!utils.deepAccess(usPrivacy, 'uspConsent', false); + const uspString = utils.deepAccess(usPrivacy, 'uspConsent', ''); gdprConsent = gdprConsent || { gdprApplies: false, consentString: '', }; - if (serverBody) { - if (serverBody.bids) { - serverBody.bids.forEach(bidBody => { - const userSyncUrls = bidBody.usersync || []; - const userSyncUrlProcess = url => { - return url - .replace('{gdpr}', gdprConsent.gdprApplies) - .replace('{gdpr_cs}', gdprConsent.consentString); - } + const userSyncUrlProcess = url => { + return url + .replace('{gdpr}', gdprConsent.gdprApplies) + .replace('{gdpr_cs}', gdprConsent.consentString) + .replace('{usp}', uspApplies) + .replace('{usp_cs}', uspString); + } - userSyncUrls.forEach(sync => { - if (syncOptions.iframeEnabled && sync.type === 'iframe' && sync.url) { - syncs.push({ - type: 'iframe', - url: userSyncUrlProcess(sync.url), - }); - } - if (syncOptions.pixelEnabled && sync.type === 'image' && sync.url) { - syncs.push({ - type: 'image', - url: userSyncUrlProcess(sync.url), - }); - } + serverResponses.forEach(serverResponse => { + const userSyncUrls = serverResponse.body.usersync || []; + userSyncUrls.forEach(sync => { + if (syncOptions.iframeEnabled && sync.type === 'iframe' && sync.url) { + syncs.push({ + type: 'iframe', + url: userSyncUrlProcess(sync.url), }); - }); - } - } + } + if (syncOptions.pixelEnabled && sync.type === 'image' && sync.url) { + syncs.push({ + type: 'image', + url: userSyncUrlProcess(sync.url), + }); + } + }) + }); return syncs; }, } function getContext(elem) { - return elem && window.document.body.contains(elem) ? window : (window.top.document.body.contains(elem) ? top : undefined); + try { + return elem && window.document.body.contains(elem) ? window : (window.top.document.body.contains(elem) ? top : undefined); + } catch (e) { + return undefined; + } } function isDefined(val) { @@ -206,4 +230,24 @@ function getLocalDateFormatted() { return `${d.getFullYear()}-${two(d.getMonth() + 1)}-${two(d.getDate())} ${two(d.getHours())}:${two(d.getMinutes())}:${two(d.getSeconds())}`; } +function getFramePos() { + let t = window; + let m = 0; + let frmLeft = 0; + let frmTop = 0; + do { + m = m + 1; + try { + if (m > 1) { + t = t.parent + } + frmLeft = frmLeft + t.frameElement.getBoundingClientRect().left; + frmTop = frmTop + t.frameElement.getBoundingClientRect().top; + } catch (o) { /* keep looping */ + } + } while ((m < 100) && (t.parent !== t.self)) + + return [frmLeft, frmTop]; +} + registerBidder(spec); diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index dd55483ef33..e7281086a92 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -164,7 +164,7 @@ function wrapAd(bid, bidData) { parentDocument.style.width = "100%"; } var _content = "${encodeURIComponent(JSON.stringify(bid.inImageContent))}"; - window._ao_ssp.registerInImage(JSON.parse(decodeURIComponent(_content))); + window._hyb_prebid_ssp.registerInImage(JSON.parse(decodeURIComponent(_content))); `; diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 0432adf2b2d..dc0911ff5da 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -11,7 +11,7 @@ const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js const VIDEO_TARGETING = ['skip', 'skipmin', 'skipafter']; export const spec = { - version: '7.2.0', + version: '7.3.0', code: BIDDER_CODE, gvlid: 253, aliases: ['id'], @@ -126,7 +126,6 @@ export const spec = { } // Common properties - bid.adId = bidObject.id; bid.cpm = parseFloat(bidObject.price); bid.creativeId = bidObject.crid; bid.currency = bidObject.currency ? bidObject.currency.toUpperCase() : 'USD'; diff --git a/modules/inmarBidAdapter.js b/modules/inmarBidAdapter.js index 7583985b23c..e1edd935587 100755 --- a/modules/inmarBidAdapter.js +++ b/modules/inmarBidAdapter.js @@ -40,7 +40,7 @@ export const spec = { uspConsent: bidderRequest.uspConsent, currencyCode: config.getConfig('currency.adServerCurrency'), coppa: config.getConfig('coppa'), - firstPartyData: config.getConfig('fpd'), + firstPartyData: config.getLegacyFpd(config.getConfig('ortb2')), prebidVersion: '$prebid.version$' }; diff --git a/modules/inskinBidAdapter.js b/modules/inskinBidAdapter.js index 3951e27c870..29040205818 100644 --- a/modules/inskinBidAdapter.js +++ b/modules/inskinBidAdapter.js @@ -4,9 +4,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'inskin'; const CONFIG = { - 'inskin': { - 'BASE_URI': 'https://mfad.inskinad.com/api/v2' - } + BASE_URI: 'https://mfad.inskinad.com/api/v2' }; export const spec = { @@ -97,8 +95,7 @@ export const spec = { } validBidRequests.map(bid => { - let config = CONFIG[bid.bidder]; - ENDPOINT_URL = config.BASE_URI; + ENDPOINT_URL = CONFIG.BASE_URI; const placement = Object.assign({ divName: bid.bidId, @@ -108,8 +105,12 @@ export const spec = { placement.adTypes.push(5, 9, 163, 2163, 3006); + placement.properties = placement.properties || {}; + + placement.properties.screenWidth = screen.width; + placement.properties.screenHeight = screen.height; + if (restrictions.length) { - placement.properties = placement.properties || {}; placement.properties.restrictions = restrictions; } diff --git a/modules/interactiveOffersBidAdapter.js b/modules/interactiveOffersBidAdapter.js new file mode 100644 index 00000000000..ea45c00903a --- /dev/null +++ b/modules/interactiveOffersBidAdapter.js @@ -0,0 +1,149 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'interactiveOffers'; +const ENDPOINT = 'https://rtb.ioadx.com/bidRequest/?partnerId=4a3bab187a74ac4862920cca864d6eff195ff5e4'; + +const DEFAULT = { + 'OpenRTBBidRequest': {}, + 'OpenRTBBidRequestSite': {}, + 'OpenRTBBidRequestSitePublisher': {}, + 'OpenRTBBidRequestSiteContent': { + language: navigator.language, + }, + 'OpenRTBBidRequestSource': {}, + 'OpenRTBBidRequestDevice': { + ua: navigator.userAgent, + language: navigator.language + }, + 'OpenRTBBidRequestUser': {}, + 'OpenRTBBidRequestImp': {}, + 'OpenRTBBidRequestImpBanner': {}, + 'PrebidBid': { + currency: 'USD', + ttl: 60, + netRevenue: false + } +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function(bid) { + let ret = true; + if (bid && bid.params) { + if (!utils.isNumber(bid.params.pubid)) { + utils.logWarn('pubid must be a valid numeric ID'); + ret = false; + } + if (bid.params.tmax && !utils.isNumber(bid.params.tmax)) { + utils.logWarn('tmax must be a valid numeric ID'); + ret = false; + } + } else { + utils.logWarn('invalid request'); + ret = false; + } + return ret; + }, + buildRequests: function(validBidRequests, bidderRequest) { + let payload = parseRequestPrebidjsToOpenRTB(bidderRequest); + return { + method: 'POST', + url: ENDPOINT, + data: JSON.stringify(payload), + bidderRequest: bidderRequest + }; + }, + + interpretResponse: function(response, request) { + let bidResponses = []; + if (response.body && response.body.length) { + bidResponses = parseResponseOpenRTBToPrebidjs(response.body); + } + return bidResponses; + } +}; + +function parseRequestPrebidjsToOpenRTB(prebidRequest) { + let pageURL = window.location.href; + let domain = window.location.hostname; + let secure = (window.location.protocol == 'https:' ? 1 : 0); + let openRTBRequest = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequest'])); + openRTBRequest.id = prebidRequest.auctionId; + + openRTBRequest.site = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestSite'])); + openRTBRequest.site.id = domain; + openRTBRequest.site.name = domain; + openRTBRequest.site.domain = domain; + openRTBRequest.site.page = pageURL; + openRTBRequest.site.ref = prebidRequest.refererInfo.referer; + + openRTBRequest.site.publisher = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestSitePublisher'])); + openRTBRequest.site.publisher.id = 0; + openRTBRequest.site.publisher.name = config.getConfig('publisherDomain'); + openRTBRequest.site.publisher.domain = domain; + openRTBRequest.site.publisher.domain = domain; + + openRTBRequest.site.content = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestSiteContent'])); + + openRTBRequest.source = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestSource'])); + openRTBRequest.source.fd = 0; + openRTBRequest.source.tid = prebidRequest.auctionId; + openRTBRequest.source.pchain = ''; + + openRTBRequest.device = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestDevice'])); + + openRTBRequest.user = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestUser'])); + + openRTBRequest.imp = []; + prebidRequest.bids.forEach(function(bid, impId) { + impId++; + let imp = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestImp'])); + imp.id = impId; + imp.secure = secure; + imp.tagid = bid.bidId; + + openRTBRequest.site.publisher.id = openRTBRequest.site.publisher.id || bid.params.pubid; + openRTBRequest.tmax = openRTBRequest.tmax || bid.params.tmax || 0; + + Object.keys(bid.mediaTypes).forEach(function(mediaType) { + if (mediaType == 'banner') { + imp.banner = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestImpBanner'])); + imp.banner.w = 0; + imp.banner.h = 0; + imp.banner.format = []; + bid.mediaTypes[mediaType].sizes.forEach(function(adSize) { + if (!imp.banner.w) { + imp.banner.w = adSize[0]; + imp.banner.h = adSize[1]; + } + imp.banner.format.push({w: adSize[0], h: adSize[1]}); + }); + } + }); + openRTBRequest.imp.push(imp); + }); + return openRTBRequest; +} +function parseResponseOpenRTBToPrebidjs(openRTBResponse) { + let prebidResponse = []; + openRTBResponse.forEach(function(response) { + response.seatbid.forEach(function(seatbid) { + seatbid.bid.forEach(function(bid) { + let prebid = JSON.parse(JSON.stringify(DEFAULT['PrebidBid'])); + prebid.requestId = bid.ext.tagid; + prebid.ad = bid.adm; + prebid.creativeId = bid.crid; + prebid.cpm = bid.price; + prebidResponse.push(prebid); + }); + }); + }); + return prebidResponse; +} + +registerBidder(spec); diff --git a/modules/interactiveOffersBidAdapter.md b/modules/interactiveOffersBidAdapter.md index 7eb440c8216..35be59862ef 100644 --- a/modules/interactiveOffersBidAdapter.md +++ b/modules/interactiveOffersBidAdapter.md @@ -1,14 +1,14 @@ # Overview - + ``` Module Name: interactiveOffers Bidder Adapter Module Type: Bidder Adapter -Maintainer: devteam@interactiveoffers.com +Maintainer: faria@interactiveoffers.com ``` # Description -Module that connects to interactiveOffers demand sources. Param pubId is required. +Module that connects to interactiveOffers demand sources. Param pubid is required. # Test Parameters ``` @@ -24,11 +24,11 @@ Module that connects to interactiveOffers demand sources. Param pubId is require { bidder: "interactiveOffers", params: { - pubId: '10', - tmax: 5000 + pubid: 10, + tmax: 250 } } ] } ]; -``` \ No newline at end of file +``` diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 50ed7d5f57c..d4d26b1e017 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {getStorageManager} from '../src/storageManager.js'; const CONSTANTS = { BIDDER_CODE: 'invibes', @@ -8,9 +8,10 @@ const CONSTANTS = { SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync', TIME_TO_LIVE: 300, DEFAULT_CURRENCY: 'EUR', - PREBID_VERSION: 4, + PREBID_VERSION: 5, METHOD: 'GET', - INVIBES_VENDOR_ID: 436 + INVIBES_VENDOR_ID: 436, + USERID_PROVIDERS: ['pubcid', 'pubProvidedId'] }; const storage = getStorageManager(CONSTANTS.INVIBES_VENDOR_ID); @@ -54,6 +55,7 @@ registerBidder(spec); const topWin = getTopMostWindow(); let invibes = topWin.invibes = topWin.invibes || {}; invibes.purposes = invibes.purposes || [false, false, false, false, false, false, false, false, false, false]; +invibes.legitimateInterests = invibes.legitimateInterests || [false, false, false, false, false, false, false, false, false, false]; let _customUserSync; function isBidRequestValid(bid) { @@ -77,7 +79,7 @@ function isBidRequestValid(bid) { function buildRequest(bidRequests, bidderRequest) { bidderRequest = bidderRequest || {}; const _placementIds = []; - let _loginId, _customEndpoint; + let _loginId, _customEndpoint, _userId; let _ivAuctionStart = bidderRequest.auctionStart || Date.now(); bidRequests.forEach(function (bidRequest) { @@ -86,6 +88,7 @@ function buildRequest(bidRequests, bidderRequest) { _loginId = _loginId || bidRequest.params.loginId; _customEndpoint = _customEndpoint || bidRequest.params.customEndpoint; _customUserSync = _customUserSync || bidRequest.params.customUserSync; + _userId = _userId || bidRequest.userId; }); invibes.optIn = invibes.optIn || readGdprConsent(bidderRequest.gdprConsent); @@ -95,19 +98,23 @@ function buildRequest(bidRequests, bidderRequest) { let lid = initDomainId(invibes.domainOptions); const currentQueryStringParams = parseQueryStringParams(); - + let userIdModel = getUserIds(_userId); + let bidParamsJson = { + placementIds: _placementIds, + loginId: _loginId, + auctionStartTime: _ivAuctionStart, + bidVersion: CONSTANTS.PREBID_VERSION + }; + if (userIdModel) { + bidParamsJson.userId = userIdModel; + } let data = { location: getDocumentLocation(topWin), videoAdHtmlId: generateRandomId(), showFallback: currentQueryStringParams['advs'] === '0', ivbsCampIdsLocal: invibes.getCookie('IvbsCampIdsLocal'), - bidParamsJson: JSON.stringify({ - placementIds: _placementIds, - loginId: _loginId, - auctionStartTime: _ivAuctionStart, - bidVersion: CONSTANTS.PREBID_VERSION - }), + bidParamsJson: JSON.stringify(bidParamsJson), capCounts: getCappedCampaignsAsString(), vId: invibes.visitId, @@ -118,6 +125,7 @@ function buildRequest(bidRequests, bidderRequest) { kw: keywords, purposes: invibes.purposes.toString(), + li: invibes.legitimateInterests.toString(), tc: invibes.gdpr_consent }; @@ -140,7 +148,7 @@ function buildRequest(bidRequests, bidderRequest) { method: CONSTANTS.METHOD, url: _customEndpoint || CONSTANTS.BID_ENDPOINT, data: data, - options: { withCredentials: true }, + options: {withCredentials: true}, // for POST: { contentType: 'application/json', withCredentials: true } bidRequests: bidRequests }; @@ -228,9 +236,26 @@ function getDocumentLocation(topWin) { return topWin.location.href.substring(0, 300).split(/[?#]/)[0]; } +function getUserIds(bidUserId) { + let userId; + if (bidUserId) { + CONSTANTS.USERID_PROVIDERS.forEach(provider => { + if (bidUserId[provider]) { + userId = userId || {}; + userId[provider] = bidUserId[provider]; + } + }); + } + + return userId; +} + function parseQueryStringParams() { let params = {}; - try { params = JSON.parse(localStorage.ivbs); } catch (e) { } + try { + params = JSON.parse(localStorage.ivbs); + } catch (e) { + } let re = /[\\?&]([^=]+)=([^\\?&#]+)/g; let m; while ((m = re.exec(window.location.href)) != null) { @@ -257,9 +282,12 @@ function getTopMostWindow() { try { while (top !== res) { - if (res.parent.location.href.length) { res = res.parent; } + if (res.parent.location.href.length) { + res = res.parent; + } } - } catch (e) { } + } catch (e) { + } return res; } @@ -277,7 +305,9 @@ function renderCreative(bidModel) { function getCappedCampaignsAsString() { const key = 'ivvcap'; - if (!invibes.optIn || !invibes.purposes[0]) { return ''; } + if (!invibes.optIn || !invibes.purposes[0]) { + return ''; + } let loadData = function () { try { @@ -311,24 +341,31 @@ function getCappedCampaignsAsString() { clearExpired(); let data = loadData(); return Object.keys(data) - .filter(function (k) { return data.hasOwnProperty(k); }) + .filter(function (k) { + return data.hasOwnProperty(k); + }) .sort() - .map(function (k) { return [k, data[k][0]]; }); + .map(function (k) { + return [k, data[k][0]]; + }); }; return getCappedCampaigns() - .map(function (record) { return record.join('='); }) + .map(function (record) { + return record.join('='); + }) .join(','); } -const noop = function () { }; +const noop = function () { +}; function initLogger() { if (storage.hasLocalStorage() && localStorage.InvibesDEBUG) { return window.console; } - return { info: noop, error: noop, log: noop, warn: noop, debug: noop }; + return {info: noop, error: noop, log: noop, warn: noop, debug: noop}; } function buildSyncUrl() { @@ -358,39 +395,37 @@ function readGdprConsent(gdprConsent) { for (index = 0; index < invibes.purposes; ++index) { invibes.purposes[index] = true; } + + for (index = 0; index < invibes.legitimateInterests.length; ++index) { + invibes.legitimateInterests[index] = true; + } return 2; } let purposeConsents = getPurposeConsents(gdprConsent.vendorData); - if (purposeConsents == null) { return 0; } + if (purposeConsents == null) { + return 0; + } let purposesLength = getPurposeConsentsCounter(gdprConsent.vendorData); - if (purposeConsents instanceof Array) { - for (let i = 0; i < purposesLength && i < purposeConsents.length; i++) { - invibes.purposes[i] = !((purposeConsents[i] === false || purposeConsents[i] === 'false' || purposeConsents[i] == null)); - } - } else if (typeof purposeConsents === 'object' && purposeConsents !== null) { - let i = 0; - for (let prop in purposeConsents) { - if (i === purposesLength) { - break; - } - - if (purposeConsents.hasOwnProperty(prop)) { - invibes.purposes[i] = !((purposeConsents[prop] === false || purposeConsents[prop] === 'false' || purposeConsents[prop] == null)); - i++; - } - } - } else { + if (!tryCopyValueToArray(purposeConsents, invibes.purposes, purposesLength)) { return 0; } + let legitimateInterests = getLegitimateInterests(gdprConsent.vendorData); + tryCopyValueToArray(legitimateInterests, invibes.legitimateInterests, 10); + let invibesVendorId = CONSTANTS.INVIBES_VENDOR_ID.toString(10); let vendorConsents = getVendorConsents(gdprConsent.vendorData); - if (vendorConsents == null || vendorConsents[invibesVendorId] == null) { return 4; } + let vendorHasLegitimateInterest = getVendorLegitimateInterest(gdprConsent.vendorData)[invibesVendorId] === true; + if (vendorConsents == null || vendorConsents[invibesVendorId] == null) { + return 4; + } - if (vendorConsents[invibesVendorId] === false) { return 0; } + if (vendorConsents[invibesVendorId] === false && vendorHasLegitimateInterest === false) { + return 0; + } return 2; } @@ -398,6 +433,31 @@ function readGdprConsent(gdprConsent) { return 0; } +function tryCopyValueToArray(value, target, length) { + if (value instanceof Array) { + for (let i = 0; i < length && i < value.length; i++) { + target[i] = !((value[i] === false || value[i] === 'false' || value[i] == null)); + } + return true; + } + if (typeof value === 'object' && value !== null) { + let i = 0; + for (let prop in value) { + if (i === length) { + break; + } + + if (value.hasOwnProperty(prop)) { + target[i] = !((value[prop] === false || value[prop] === 'false' || value[prop] == null)); + i++; + } + } + return true; + } + + return false; +} + function getPurposeConsentsCounter(vendorData) { if (vendorData.purpose && vendorData.purpose.consents) { return 10; @@ -418,9 +478,19 @@ function getPurposeConsents(vendorData) { return null; } +function getLegitimateInterests(vendorData) { + if (vendorData.purpose && vendorData.purpose.legitimateInterests) { + return vendorData.purpose.legitimateInterests; + } + + return null; +} + function getVendorConsentData(vendorData) { if (vendorData.purpose && vendorData.purpose.consents) { - if (vendorData.tcString != null) { return vendorData.tcString; } + if (vendorData.tcString != null) { + return vendorData.tcString; + } } return vendorData.consentData; }; @@ -437,13 +507,23 @@ function getVendorConsents(vendorData) { return null; } +function getVendorLegitimateInterest(vendorData) { + if (vendorData.vendor && vendorData.vendor.legitimateInterests) { + return vendorData.vendor.legitimateInterests; + } + + return {}; +} + const ivLogger = initLogger(); /// Local domain cookie management ===================== invibes.Uid = { generate: function () { let maxRand = parseInt('zzzzzz', 36) - let mkRand = function () { return Math.floor(Math.random() * maxRand).toString(36); }; + let mkRand = function () { + return Math.floor(Math.random() * maxRand).toString(36); + }; let rand1 = mkRand(); let rand2 = mkRand(); return rand1 + rand2; @@ -451,9 +531,13 @@ invibes.Uid = { }; invibes.getCookie = function (name) { - if (!storage.cookiesAreEnabled()) { return; } + if (!storage.cookiesAreEnabled()) { + return; + } - if (!invibes.optIn || !invibes.purposes[0]) { return; } + if (!invibes.optIn || !invibes.purposes[0]) { + return; + } return storage.getCookie(name); }; @@ -465,7 +549,8 @@ let initDomainId = function (options) { let str = invibes.getCookie(this.cname) || ''; try { return JSON.parse(str); - } catch (e) { } + } catch (e) { + } } }; @@ -546,6 +631,7 @@ let keywords = (function () { } return kw; }()); + // ===================== export function resetInvibes() { diff --git a/modules/ipromBidAdapter.js b/modules/ipromBidAdapter.js new file mode 100644 index 00000000000..e328cd1ec5d --- /dev/null +++ b/modules/ipromBidAdapter.js @@ -0,0 +1,72 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'iprom'; +const ENDPOINT_URL = 'https://core.iprom.net/programmatic'; +const VERSION = 'v1.0.0'; +const DEFAULT_CURRENCY = 'EUR'; +const DEFAULT_NETREVENUE = true; +const DEFAULT_TTL = 360; + +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function ({ bidder, params = {} } = {}) { + // id parameter checks + if (!params.id) { + utils.logError(`${bidder}: Parameter 'id' missing`); + return false; + } else if (typeof params.id !== 'string') { + utils.logError(`${bidder}: Parameter 'id' needs to be a string`); + return false; + } + // dimension parameter checks + if (!params.dimension) { + utils.logError(`${bidder}: Required parameter 'dimension' missing`); + return false; + } else if (typeof params.dimension !== 'string') { + utils.logError(`${bidder}: Parameter 'dimension' needs to be a string`); + return false; + } + + return true; + }, + + buildRequests: function (validBidRequests, bidderRequest) { + const payload = { + bids: validBidRequests, + referer: bidderRequest.refererInfo, + version: VERSION + }; + const payloadString = JSON.stringify(payload); + + return { + method: 'POST', + url: ENDPOINT_URL, + data: payloadString + }; + }, + + interpretResponse: function (serverResponse, request) { + let bids = serverResponse.body; + + const bidResponses = []; + + bids.forEach(bid => { + bidResponses.push({ + ad: bid.ad, + requestId: bid.requestId, + cpm: bid.cpm, + width: bid.width, + height: bid.height, + creativeId: bid.creativeId, + currency: bid.currency || DEFAULT_CURRENCY, + netRevenue: bid.netRevenue || DEFAULT_NETREVENUE, + ttl: bid.ttl || DEFAULT_TTL, + }); + }); + + return bidResponses; + }, +} + +registerBidder(spec); diff --git a/modules/ipromBidAdapter.md b/modules/ipromBidAdapter.md new file mode 100644 index 00000000000..f7124e7c89c --- /dev/null +++ b/modules/ipromBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: Iprom PreBid Adapter +Module Type: Bidder Adapter +Maintainer: support@iprom.si +``` + +# Description + +Module that connects to Iprom's demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, + bids: [ + { + bidder: "iprom", + params: { + id: '1234', + dimension: '300x250' + } + } + ] + } + ]; +``` diff --git a/modules/ironsourceBidAdapter.js b/modules/ironsourceBidAdapter.js index ba510e86e7f..5b8531d7a85 100644 --- a/modules/ironsourceBidAdapter.js +++ b/modules/ironsourceBidAdapter.js @@ -211,7 +211,8 @@ function generateParameters(bid, bidderRequest) { bid_id: utils.getBidIdParameter('bidId', bid), bidder_request_id: utils.getBidIdParameter('bidderRequestId', bid), transaction_id: utils.getBidIdParameter('transactionId', bid), - session_id: utils.getBidIdParameter('auctionId', bid), + session_id: params.sessionId || utils.getBidIdParameter('auctionId', bid), + is_wrapper: !!params.isWrapper, publisher_name: domain, site_domain: domain, bidder_version: BIDDER_VERSION diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 97341fbfd78..f3514f66b9b 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -14,11 +14,14 @@ const CENT_TO_DOLLAR_FACTOR = 100; const BANNER_TIME_TO_LIVE = 300; const VIDEO_TIME_TO_LIVE = 3600; // 1hr const NET_REVENUE = true; + const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 }; const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; +const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; + /** * Transform valid bid request config object to banner impression object that will be sent to ad server. * @@ -33,6 +36,8 @@ function bidToBannerImp(bid) { imp.banner.h = bid.params.size[1]; imp.banner.topframe = utils.inIframe() ? 0 : 1; + _applyFloor(bid, imp, BANNER); + return imp; } @@ -46,7 +51,7 @@ function bidToVideoImp(bid) { const imp = bidToImp(bid); const videoAdUnitRef = utils.deepAccess(bid, 'mediaTypes.video'); const context = utils.deepAccess(bid, 'mediaTypes.video.context'); - const videoAdUnitWhitelist = [ + const videoAdUnitAllowlist = [ 'mimes', 'minduration', 'maxduration', 'protocols', 'protocol', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', @@ -68,12 +73,14 @@ function bidToVideoImp(bid) { } } - for (let adUnitProperty in videoAdUnitRef) { - if (videoAdUnitWhitelist.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { + for (const adUnitProperty in videoAdUnitRef) { + if (videoAdUnitAllowlist.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { imp.video[adUnitProperty] = videoAdUnitRef[adUnitProperty]; } } + _applyFloor(bid, imp, VIDEO); + return imp; } @@ -92,12 +99,73 @@ function bidToImp(bid) { imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`; } - if (bid.params.hasOwnProperty('bidFloor') && bid.params.hasOwnProperty('bidFloorCur')) { - imp.bidfloor = bid.params.bidFloor; - imp.bidfloorcur = bid.params.bidFloorCur; + return imp; +} + +/** + * Gets priceFloors floors and IX adapter floors, + * Validates and sets the higher one on the impression + * @param {object} bid bid object + * @param {object} imp impression object + * @param {string} mediaType the impression ad type, one of the SUPPORTED_AD_TYPES + */ +function _applyFloor(bid, imp, mediaType) { + let adapterFloor = null; + let moduleFloor = null; + + if (bid.params.bidFloor && bid.params.bidFloorCur) { + adapterFloor = { floor: bid.params.bidFloor, currency: bid.params.bidFloorCur }; } - return imp; + if (utils.isFn(bid.getFloor)) { + let _mediaType = '*'; + let _size = '*'; + + if (mediaType && utils.contains(SUPPORTED_AD_TYPES, mediaType)) { + const { w: width, h: height } = imp[mediaType]; + _mediaType = mediaType; + _size = [width, height]; + } + try { + moduleFloor = bid.getFloor({ + mediaType: _mediaType, + size: _size + }); + } catch (err) { + // continue with no module floors + utils.logWarn('priceFloors module call getFloor failed, error : ', err); + } + } + + if (adapterFloor && moduleFloor) { + if (adapterFloor.currency !== moduleFloor.currency) { + utils.logWarn('The bid floor currency mismatch between IX params and priceFloors module config'); + return; + } + + if (adapterFloor.floor > moduleFloor.floor) { + imp.bidfloor = adapterFloor.floor; + imp.bidfloorcur = adapterFloor.currency; + imp.ext.fl = FLOOR_SOURCE.IX; + } else { + imp.bidfloor = moduleFloor.floor; + imp.bidfloorcur = moduleFloor.currency; + imp.ext.fl = FLOOR_SOURCE.PBJS; + } + return; + } + + if (moduleFloor) { + imp.bidfloor = moduleFloor.floor; + imp.bidfloorcur = moduleFloor.currency; + imp.ext.fl = FLOOR_SOURCE.PBJS; + } else if (adapterFloor) { + imp.bidfloor = adapterFloor.floor; + imp.bidfloorcur = adapterFloor.currency; + imp.ext.fl = FLOOR_SOURCE.IX; + } else { + utils.logInfo('IX Bid Adapter: No floors available, no floors applied'); + } } /** @@ -212,36 +280,35 @@ function getBidRequest(id, impressions) { } /** - * Adds a User ID module's response into user Eids array. - * - * @param {array} userEids An array of objects containing user ids, - * will be attached to bid request later. - * @param {object} seenIdPartners An object with Identity partners names already added, - * updated with new partner name. - * @param {*} id The id obtained from User ID module. - * @param {string} source The URL of the User ID module. - * @param {string} ixlPartnerName The name of the Identity Partner in IX Library. - * @param {string} rtiPartner The name of the User ID provider in Prebid. - * @return {boolean} True if successfully added the ID to the userEids, false otherwise. + * From the userIdAsEids array, filter for the ones our adserver can use, and modify them + * for our purposes, e.g. add rtiPartner + * @param {array} allEids userIdAsEids passed in by prebid + * @return {object} contains toSend (eids to send to the adserver) and seenSources (used to filter + * identity info from IX Library) */ -function addUserEids(userEids, seenIdPartners, id, source, ixlPartnerName, rtiPartner) { - if (id) { - // mark the partnername that IX RTI uses - seenIdPartners[ixlPartnerName] = 1; - userEids.push({ - source: source, - uids: [{ - id: id, - ext: { - rtiPartner: rtiPartner - } - }] - }); - return true; +function getEidInfo(allEids) { + // determines which eids we send and the rtiPartner field in ext + var sourceRTIMapping = { + 'liveramp.com': 'idl', + 'netid.de': 'NETID', + 'neustar.biz': 'fabrickId', + 'zeotap.com': 'zeotapIdPlus' + }; + var toSend = []; + var seenSources = {}; + if (utils.isArray(allEids)) { + for (var i = 0; i < allEids.length; i++) { + if (sourceRTIMapping[allEids[i].source] && utils.deepAccess(allEids[i], 'uids.0')) { + seenSources[allEids[i].source] = 1; + allEids[i].uids[0].ext = { + rtiPartner: sourceRTIMapping[allEids[i].source] + }; + delete allEids[i].uids[0].atype; + toSend.push(allEids[i]); + } + } } - - utils.logWarn('Tried to add a user ID from Prebid, the ID received was null'); - return false; + return { toSend: toSend, seenSources: seenSources }; } /** @@ -255,21 +322,12 @@ function addUserEids(userEids, seenIdPartners, id, source, ixlPartnerName, rtiPa * */ function buildRequest(validBidRequests, bidderRequest, impressions, version) { - const userEids = []; - // Always use secure HTTPS protocol. let baseUrl = SECURE_BID_URL; - // Dict for identity partners already populated from prebid - let seenIdPartners = {}; - // Get ids from Prebid User ID Modules - const userId = validBidRequests[0].userId; - if (userId && typeof userId === 'object') { - if (userId.idl_env) { - addUserEids(userEids, seenIdPartners, userId.idl_env, 'liveramp.com', 'LiveRampIp', 'idl'); - } - } + var eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids')); + var userEids = eidInfo.toSend; // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded // and if the data for the partner exist @@ -278,12 +336,10 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { if (identityInfo && typeof identityInfo === 'object') { for (const partnerName in identityInfo) { if (identityInfo.hasOwnProperty(partnerName)) { - // check if not already populated by prebid cache - if (!seenIdPartners.hasOwnProperty(partnerName)) { - let response = identityInfo[partnerName]; - if (!response.responsePending && response.data && typeof response.data === 'object' && Object.keys(response.data).length) { - userEids.push(response.data); - } + let response = identityInfo[partnerName]; + if (!response.responsePending && response.data && typeof response.data === 'object' && + Object.keys(response.data).length && !eidInfo.seenSources[response.data.source]) { + userEids.push(response.data); } } } @@ -341,6 +397,12 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r.user.ext = { consent: gdprConsent.consentString || '' }; + + if (gdprConsent.hasOwnProperty('addtlConsent') && gdprConsent.addtlConsent) { + r.user.ext.consented_providers_settings = { + consented_providers: gdprConsent.addtlConsent + } + } } } @@ -504,8 +566,9 @@ function buildIXDiag(validBidRequests) { iu: 0, nu: 0, ou: 0, - allU: 0, - ren: false + allu: 0, + ren: false, + version: '$prebid.version$' }; // create ad unit map and collect the required diag properties @@ -539,7 +602,7 @@ function buildIXDiag(validBidRequests) { ixdiag.iu++; } - ixdiag.allU++; + ixdiag.allu++; } } @@ -615,16 +678,19 @@ function updateMissingSizes(validBidRequest, missingBannerSizes, imp) { } /** - * + * @param {object} bid ValidBidRequest object, used to adjust floor * @param {object} imp Impression object to be modified * @param {array} newSize The new size to be applied * @return {object} newImp Updated impression object */ -function createMissingBannerImp(imp, newSize) { +function createMissingBannerImp(bid, imp, newSize) { const newImp = utils.deepClone(imp); newImp.ext.sid = `${newSize[0]}x${newSize[1]}`; newImp.banner.w = newSize[0]; newImp.banner.h = newSize[1]; + + _applyFloor(bid, newImp, BANNER); + return newImp; } @@ -663,7 +729,7 @@ export const spec = { } if (!includesSize(bid.sizes, paramsSize) && !((mediaTypeVideoPlayerSize && includesSize(mediaTypeVideoPlayerSize, paramsSize)) || - (mediaTypeBannerSizes && includesSize(mediaTypeBannerSizes, paramsSize)))) { + (mediaTypeBannerSizes && includesSize(mediaTypeBannerSizes, paramsSize)))) { utils.logError('ix bidder params: bid size is not included in ad unit sizes or player size.'); return false; } @@ -735,13 +801,12 @@ export const spec = { if (!videoImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { videoImps[validBidRequest.transactionId].ixImps = []; } - videoImps[validBidRequest.transactionId].ixImps.push(bidToVideoImp(validBidRequest)); } } if (validBidRequest.mediaType === BANNER || - (utils.deepAccess(validBidRequest, 'mediaTypes.banner') && includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), validBidRequest.params.size)) || - (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { + (utils.deepAccess(validBidRequest, 'mediaTypes.banner') && includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), validBidRequest.params.size)) || + (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { let imp = bidToBannerImp(validBidRequest); if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { @@ -772,7 +837,7 @@ export const spec = { let origImp = missingBannerSizes[transactionId].impression; for (let i = 0; i < missingSizes.length; i++) { - let newImp = createMissingBannerImp(origImp, missingSizes[i]); + let newImp = createMissingBannerImp(validBidRequest, origImp, missingSizes[i]); bannerImps[transactionId].missingImps.push(newImp); bannerImps[transactionId].missingCount++; } diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index a29c4ce5fa7..8332e720ae7 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -144,7 +144,7 @@ function enrichBidRequest(bidReqConfig, onDone) { * @param {function} onDone */ export function enrichAdUnits(adUnits) { - const fpdFallback = config.getConfig('fpd.context.data.jwTargeting'); + const fpdFallback = config.getConfig('ortb2.site.ext.data.jwTargeting'); adUnits.forEach(adUnit => { const jwTargeting = extractPublisherParams(adUnit, fpdFallback); if (!jwTargeting || !Object.keys(jwTargeting).length) { @@ -170,7 +170,7 @@ function supportsInstreamVideo(mediaTypes) { export function extractPublisherParams(adUnit, fallback) { let adUnitTargeting; try { - adUnitTargeting = adUnit.fpd.context.data.jwTargeting; + adUnitTargeting = adUnit.ortb2Imp.ext.data.jwTargeting; } catch (e) {} if (!adUnitTargeting && !supportsInstreamVideo(adUnit.mediaTypes)) { diff --git a/modules/jwplayerRtdProvider.md b/modules/jwplayerRtdProvider.md index ae09277979a..0723e8cbb6c 100644 --- a/modules/jwplayerRtdProvider.md +++ b/modules/jwplayerRtdProvider.md @@ -25,14 +25,14 @@ pbjs.setConfig({ } }); ``` -Lastly, include the content's media ID and/or the player's ID in the matching AdUnit's `fpd.context.data`: +Lastly, include the content's media ID and/or the player's ID in the matching AdUnit's `ortb2Imp.ext.data`: ```javascript const adUnit = { code: '/19968336/prebid_native_example_1', ... - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { // Note: the following Ids are placeholders and should be replaced with your Ids. @@ -52,7 +52,7 @@ pbjs.que.push(function() { }); ``` -**Note**: You may also include `jwTargeting` information in the prebid config's `fpd.context.data`. Information provided in the adUnit will always supersede, and information in the config will be used as a fallback. +**Note**: You may also include `jwTargeting` information in the prebid config's `ortb2.site.ext.data`. Information provided in the adUnit will always supersede, and information in the config will be used as a fallback. ##Prefetching In order to prefetch targeting information for certain media, include the media IDs in the `jwplayerDataProvider` var and set `waitForIt` to `true`: @@ -117,3 +117,7 @@ To view an example: `http://localhost:9999/integrationExamples/gpt/jwplayerRtdProvider_example.html` **Note:** the mediaIds in the example are placeholder values; replace them with your existing IDs. + +#Maintainer info + +Maintained by JW Player. For any questions, comments or feedback please contact Karim Mourra, karim@jwplayer.com diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 03767efc135..610f4558139 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -3,17 +3,19 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; -const storage = getStorageManager(); const BIDDER_CODE = 'kargo'; const HOST = 'https://krk.kargo.com'; -const SYNC = 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}'; +const SYNC = 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}&gdpr={GDPR}&gdpr_consent={GDPR_CONSENT}&us_privacy={US_PRIVACY}'; const SYNC_COUNT = 5; +const GVLID = 972; +const storage = getStorageManager(GVLID, BIDDER_CODE); let sessionId, lastPageUrl, requestCounter; export const spec = { + gvlid: GVLID, code: BIDDER_CODE, isBidRequestValid: function(bid) { if (!bid || !bid.params) { @@ -48,7 +50,7 @@ export const spec = { bidIDs: bidIds, bidSizes: bidSizes, prebidRawBidRequests: validBidRequests - }, spec._getAllMetadata(tdid, bidderRequest.uspConsent)); + }, spec._getAllMetadata(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent)); const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); return Object.assign({}, bidderRequest, { method: 'GET', @@ -85,15 +87,25 @@ export const spec = { } return bidResponses; }, - getUserSyncs: function(syncOptions) { + getUserSyncs: function(syncOptions, responses, gdprConsent, usPrivacy) { const syncs = []; const seed = spec._generateRandomUuid(); const clientId = spec._getClientId(); + var gdpr = (gdprConsent && gdprConsent.gdprApplies) ? 1 : 0; + var gdprConsentString = (gdprConsent && gdprConsent.consentString) ? gdprConsent.consentString : ''; + // don't sync if opted out via usPrivacy + if (typeof usPrivacy == 'string' && usPrivacy.length == 4 && usPrivacy[0] == 1 && usPrivacy[2] == 'Y') { + return syncs; + } if (syncOptions.iframeEnabled && seed && clientId) { for (let i = 0; i < SYNC_COUNT; i++) { syncs.push({ type: 'iframe', - url: SYNC.replace('{UUID}', clientId).replace('{SEED}', seed).replace('{INDEX}', i) + url: SYNC.replace('{UUID}', clientId).replace('{SEED}', seed) + .replace('{INDEX}', i) + .replace('{GDPR}', gdpr) + .replace('{GDPR_CONSENT}', gdprConsentString) + .replace('{US_PRIVACY}', usPrivacy || '') }); } } @@ -183,7 +195,7 @@ export const spec = { } }, - _getUserIds(tdid, usp) { + _getUserIds(tdid, usp, gdpr) { const crb = spec._getCrb(); const userIds = { kargoID: crb.userId, @@ -192,6 +204,16 @@ export const spec = { optOut: crb.optOut, usp: usp }; + + try { + if (gdpr) { + userIds['gdpr'] = { + consent: gdpr.consentString || '', + applies: !!gdpr.gdprApplies, + } + } + } catch (e) { + } if (tdid) { userIds.tdID = tdid; } @@ -203,9 +225,9 @@ export const spec = { return crb.clientId; }, - _getAllMetadata(tdid, usp) { + _getAllMetadata(tdid, usp, gdpr) { return { - userIDs: spec._getUserIds(tdid, usp), + userIDs: spec._getUserIds(tdid, usp, gdpr), krux: spec._getKrux(), pageURL: window.location.href, rawCRB: spec._readCookie('krg_crb'), diff --git a/modules/lemmaBidAdapter.js b/modules/lemmaBidAdapter.js index 5941802f97d..926761e5ab2 100644 --- a/modules/lemmaBidAdapter.js +++ b/modules/lemmaBidAdapter.js @@ -62,18 +62,6 @@ export var spec = { }, getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { let syncurl = USER_SYNC + 'pid=' + pubId; - - // Attaching GDPR Consent Params in UserSync url - if (gdprConsent) { - syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); - } - - // CCPA - if (uspConsent) { - syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); - } - if (syncOptions.iframeEnabled) { return [{ type: 'iframe', @@ -122,9 +110,9 @@ function parseRTBResponse(request, response) { newBid.dealId = bid.dealid; } if (req.imp && req.imp.length > 0) { - newBid.mediaType = req.mediaType; req.imp.forEach(robj => { if (bid.impid === robj.id) { + _checkMediaType(bid.adm, newBid); switch (newBid.mediaType) { case BANNER: break; @@ -277,10 +265,6 @@ function _getDeviceObject(request) { function setOtherParams(request, ortbRequest) { var params = request && request.params ? request.params : null; - if (request && request.gdprConsent) { - ortbRequest.regs = { ext: { gdpr: request.gdprConsent.gdprApplies ? 1 : 0 } }; - ortbRequest.user = { ext: { consent: request.gdprConsent.consentString } }; - } if (params) { ortbRequest.tmax = params.tmax; ortbRequest.bcat = params.bcat; @@ -424,4 +408,13 @@ function parse(rawResp) { return null; } +function _checkMediaType(adm, newBid) { + // Create a regex here to check the strings + var videoRegex = new RegExp(/VAST.*version/); + if (videoRegex.test(adm)) { + newBid.mediaType = VIDEO; + } else { + newBid.mediaType = BANNER; + } +} registerBidder(spec); diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index f87d67aae8e..5a955eefa92 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -166,15 +166,7 @@ export const liveIntentIdSubmodule = { const result = function(callback) { liveConnect.resolve( response => { - let responseObj = {}; - if (response) { - try { - responseObj = JSON.parse(response); - } catch (error) { - utils.logError(error); - } - } - callback(responseObj); + callback(response); }, error => { utils.logError(`${MODULE_NAME}: ID fetch encountered an error: `, error); diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index f3e9709cd11..2840da8dda6 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -259,34 +259,10 @@ function getAdblockerRecovered() { } catch (e) {} } -function AddExternalUserId(eids, value, source, atype, rtiPartner) { - if (utils.isStr(value)) { - var eid = { - source, - uids: [{ - id: value, - atype - }] - }; - - if (rtiPartner) { - eid.uids[0] = {ext: {rtiPartner}}; - } - - eids.push(eid); - } -} - function handleEids(bidRequests) { - let eids = []; const bidRequest = bidRequests[0]; - if (bidRequest && bidRequest.userId) { - AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcid.org', 1); // Also add this to eids - AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id.uid`), 'id5-sync.com', 1); - AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.criteoId`), 'criteo.com', 1); - } - if (eids.length > 0) { - return {user: {ext: {eids}}}; + if (bidRequest && bidRequest.userIdAsEids) { + return {user: {ext: {eids: bidRequest.userIdAsEids}}}; } return undefined; diff --git a/modules/loganBidAdapter.js b/modules/loganBidAdapter.js new file mode 100644 index 00000000000..e55876de675 --- /dev/null +++ b/modules/loganBidAdapter.js @@ -0,0 +1,118 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'logan'; +const AD_URL = 'https://USeast2.logan.ai/?c=o&m=multi'; +const SYNC_URL = 'https://ssp-cookie.logan.ai/html?src=pbjs' + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || + !bid.ttl || !bid.currency) { + return false; + } + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + const winTop = utils.getWindowTop(); + const location = winTop.location; + const placements = []; + const request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + 'host': location.host, + 'page': location.pathname, + 'placements': placements + }; + + if (bidderRequest) { + if (bidderRequest.uspConsent) { + request.ccpa = bidderRequest.uspConsent; + } + if (bidderRequest.gdprConsent) { + request.gdpr = bidderRequest.gdprConsent + } + } + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + const placement = { + placementId: bid.params.placementId, + bidId: bid.bidId, + schain: bid.schain || {}, + }; + const mediaType = bid.mediaTypes + + if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { + placement.sizes = mediaType[BANNER].sizes; + placement.traffic = BANNER; + } else if (mediaType && mediaType[VIDEO] && mediaType[VIDEO].playerSize) { + placement.wPlayer = mediaType[VIDEO].playerSize[0]; + placement.hPlayer = mediaType[VIDEO].playerSize[1]; + placement.traffic = VIDEO; + } else if (mediaType && mediaType[NATIVE]) { + placement.native = mediaType[NATIVE]; + placement.traffic = NATIVE; + } + placements.push(placement); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncUrl = SYNC_URL + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + return [{ + type: 'iframe', + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/loganBidAdapter.md b/modules/loganBidAdapter.md new file mode 100644 index 00000000000..3c628cdacc2 --- /dev/null +++ b/modules/loganBidAdapter.md @@ -0,0 +1,72 @@ +# Overview + +``` +Module Name: logan Bidder Adapter +Module Type: logan Bidder Adapter +Maintainer: support@logan.ai +``` + +# Description + +Module that connects to logan demand sources + +# Test Parameters +``` + var adUnits = [ + { + code:'1', + mediaTypes:{ + banner: { + sizes: [[300, 250]], + } + }, + bids:[ + { + bidder: 'logan', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids:[ + { + bidder: 'logan', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + native: { + title: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids:[ + { + bidder: 'logan', + params: { + placementId: 0 + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 4f7fd2ae1a0..29b54f77fbb 100644 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -279,8 +279,9 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } - const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); - const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); + const fpd = config.getLegacyFpd(config.getConfig('ortb2')) || {}; + const siteData = Object.assign({}, bidRequest.params.inventory, fpd.context); + const userData = Object.assign({}, bidRequest.params.visitor, fpd.user); if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) { const bidderData = { @@ -301,7 +302,7 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { utils.deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); } - const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); + const pbAdSlot = utils.deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); } diff --git a/modules/marsmediaBidAdapter.js b/modules/marsmediaBidAdapter.js index 1ce2558b8de..bb1763ebb2e 100644 --- a/modules/marsmediaBidAdapter.js +++ b/modules/marsmediaBidAdapter.js @@ -3,6 +3,7 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; function MarsmediaAdapter() { this.code = 'marsmedia'; @@ -16,7 +17,7 @@ function MarsmediaAdapter() { let SUPPORTED_VIDEO_API = [1, 2, 5]; let slotsToBids = {}; let that = this; - let version = '2.3'; + let version = '2.4'; this.isBidRequestValid = function (bid) { return !!(bid.params && bid.params.zoneId); @@ -53,6 +54,7 @@ function MarsmediaAdapter() { if (!(impObj.banner || impObj.video)) { continue; } + impObj.bidfloor = _getFloor(BRs[i]); impObj.ext = frameExt(BRs[i]); impList.push(impObj); } @@ -153,9 +155,31 @@ function MarsmediaAdapter() { } function frameExt(bid) { - return { - bidder: { - zoneId: bid.params['zoneId'] + if ((bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes)) { + let bidSizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes; + bidSizes = ((utils.isArray(bidSizes) && utils.isArray(bidSizes[0])) ? bidSizes : [bidSizes]); + bidSizes = bidSizes.filter(size => utils.isArray(size)); + const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})); + + const element = document.getElementById(bid.adUnitCode); + const minSize = _getMinSize(processedSizes); + const viewabilityAmount = _isViewabilityMeasurable(element) + ? _getViewability(element, utils.getWindowTop(), minSize) + : 'na'; + const viewabilityAmountRounded = isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount); + + return { + bidder: { + zoneId: bid.params['zoneId'] + }, + viewability: viewabilityAmountRounded + } + } else { + return { + bidder: { + zoneId: bid.params['zoneId'] + }, + viewability: 'na' } } } @@ -180,12 +204,15 @@ function MarsmediaAdapter() { } }; if (BRs[0].schain) { - bid.source = { - 'ext': { - 'schain': BRs[0].schain - } - } + utils.deepSetValue(bid, 'source.ext.schain', BRs[0].schain); + } + if (bidderRequest.uspConsent) { + utils.deepSetValue(bid, 'regs.ext.us_privacy', bidderRequest.uspConsent) } + if (config.getConfig('coppa') === true) { + utils.deepSetValue(bid, 'regs.coppa', config.getConfig('coppa') & 1) + } + return bid; } @@ -241,12 +268,6 @@ function MarsmediaAdapter() { sendbeacon(bid, 20) }; - function sendbeacon(bid, type) { - const bidString = JSON.stringify(bid); - const encodedBuf = window.btoa(bidString); - utils.triggerPixel('https://ping-hqx-1.go2speed.media/notification/rtb/beacon/?bt=' + type + '&bid=3mhdom&hb_j=' + encodedBuf, null); - } - this.interpretResponse = function (serverResponse) { let responses = serverResponse.body || []; let bids = []; @@ -295,6 +316,126 @@ function MarsmediaAdapter() { return bids; }; + + function sendbeacon(bid, type) { + const bidString = JSON.stringify(bid); + const encodedBuf = window.btoa(bidString); + utils.triggerPixel('https://ping-hqx-1.go2speed.media/notification/rtb/beacon/?bt=' + type + '&bid=3mhdom&hb_j=' + encodedBuf, null); + } + + /** + * Gets bidfloor + * @param {Object} bid + * @returns {Number} floor + */ + function _getFloor (bid) { + const curMediaType = bid.mediaTypes.video ? 'video' : 'banner'; + let floor = 0; + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: curMediaType, + size: '*' + }); + + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = floorInfo.floor; + } + } + + return floor; + } + + function _getMinSize(sizes) { + return sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min); + } + + function _isViewabilityMeasurable(element) { + return !_isIframe() && element !== null; + } + + function _isIframe() { + try { + return utils.getWindowSelf() !== utils.getWindowTop(); + } catch (e) { + return true; + } + } + + function _getViewability(element, topWin, { w, h } = {}) { + return topWin.document.visibilityState === 'visible' + ? _getPercentInView(element, topWin, { w, h }) + : 0; + } + + function _getPercentInView(element, topWin, { w, h } = {}) { + const elementBoundingBox = _getBoundingBox(element, { w, h }); + + const elementInViewBoundingBox = _getIntersectionOfRects([ { + left: 0, + top: 0, + right: topWin.innerWidth, + bottom: topWin.innerHeight + }, elementBoundingBox ]); + + let elementInViewArea, elementTotalArea; + + if (elementInViewBoundingBox !== null) { + // Some or all of the element is in view + elementInViewArea = elementInViewBoundingBox.width * elementInViewBoundingBox.height; + elementTotalArea = elementBoundingBox.width * elementBoundingBox.height; + + return ((elementInViewArea / elementTotalArea) * 100); + } + + return 0; + } + + function _getBoundingBox(element, { w, h } = {}) { + let { width, height, left, top, right, bottom } = element.getBoundingClientRect(); + + if ((width === 0 || height === 0) && w && h) { + width = w; + height = h; + right = left + w; + bottom = top + h; + } + + return { width, height, left, top, right, bottom }; + } + + function _getIntersectionOfRects(rects) { + const bbox = { + left: rects[0].left, + right: rects[0].right, + top: rects[0].top, + bottom: rects[0].bottom + }; + + for (let i = 1; i < rects.length; ++i) { + bbox.left = Math.max(bbox.left, rects[i].left); + bbox.right = Math.min(bbox.right, rects[i].right); + + if (bbox.left >= bbox.right) { + return null; + } + + bbox.top = Math.max(bbox.top, rects[i].top); + bbox.bottom = Math.min(bbox.bottom, rects[i].bottom); + + if (bbox.top >= bbox.bottom) { + return null; + } + } + + bbox.width = bbox.right - bbox.left; + bbox.height = bbox.bottom - bbox.top; + + return bbox; + } } export const spec = new MarsmediaAdapter(); diff --git a/modules/mass.js b/modules/mass.js new file mode 100644 index 00000000000..14fe556a466 --- /dev/null +++ b/modules/mass.js @@ -0,0 +1,129 @@ +/** + * This module adds MASS support to Prebid.js. + */ + +import { config } from '../src/config.js'; +import { getHook } from '../src/hook.js'; +import find from 'core-js-pure/features/array/find.js'; + +export let listenerAdded = false; +export let massEnabled = false; + +const defaultCfg = { + dealIdPattern: /^MASS/i +}; +let cfg; + +const massBids = {}; + +init(); +config.getConfig('mass', config => init(config.mass)); + +/** + * Module init. + */ +export function init(customCfg) { + cfg = Object.assign({}, defaultCfg, customCfg); + + if (cfg.enabled === false) { + if (massEnabled) { + massEnabled = false; + getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); + } + } else { + if (!massEnabled) { + getHook('addBidResponse').before(addBidResponseHook); + massEnabled = true; + } + } +} + +/** + * Before hook for 'addBidResponse'. + */ +export function addBidResponseHook(next, adUnitCode, bid) { + if (!isMassBid(bid) || !cfg.renderUrl) { + return next(adUnitCode, bid); + } + + const bidRequest = find(this.bidderRequest.bids, bidRequest => + bidRequest.bidId === bid.requestId + ); + + massBids[bid.requestId] = { + bidRequest, + bid, + adm: bid.ad + }; + + bid.ad = ' diff --git a/package.json b/package.json index d8b6af04379..aca216767cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.26.0-pre", + "version": "4.33.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { diff --git a/src/adapterManager.js b/src/adapterManager.js index cb84607e130..f7f5d821932 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -68,7 +68,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) } bid = Object.assign({}, bid, getDefinedParams(adUnit, [ - 'fpd', + 'ortb2Imp', 'mediaType', 'renderer', 'storedAuctionResponse' diff --git a/src/auction.js b/src/auction.js index 15c6b50ce71..7005d56827e 100644 --- a/src/auction.js +++ b/src/auction.js @@ -458,7 +458,7 @@ function tryAddVideoBid(auctionInstance, bidResponse, bidRequests, afterBidAdded const context = videoMediaType && deepAccess(videoMediaType, 'context'); if (config.getConfig('cache.url') && context !== OUTSTREAM) { - if (!bidResponse.videoCacheKey) { + if (!bidResponse.videoCacheKey || config.getConfig('cache.ignoreBidderCacheKey')) { addBid = false; callPrebidCache(auctionInstance, bidResponse, afterBidAdded, bidderRequest); } else if (!bidResponse.vastUrl) { @@ -519,7 +519,7 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) { events.emit(CONSTANTS.EVENTS.BID_ADJUSTMENT, bidObject); // a publisher-defined renderer can be used to render bids - const bidReq = bidderRequest.bids && find(bidderRequest.bids, bid => bid.adUnitCode == adUnitCode); + const bidReq = bidderRequest.bids && find(bidderRequest.bids, bid => bid.adUnitCode == adUnitCode && bid.bidId == bidObject.requestId); const adUnitRenderer = bidReq && bidReq.renderer; // a publisher can also define a renderer for a mediaType @@ -533,9 +533,9 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) { var renderer = null; // the renderer for the mediaType takes precendence - if (mediaTypeRenderer && mediaTypeRenderer.url && !(mediaTypeRenderer.backupOnly === true && mediaTypeRenderer.render)) { + if (mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render && !(mediaTypeRenderer.backupOnly === true && bid.renderer)) { renderer = mediaTypeRenderer; - } else if (adUnitRenderer && adUnitRenderer.url && !(adUnitRenderer.backupOnly === true && bid.renderer)) { + } else if (adUnitRenderer && adUnitRenderer.url && adUnitRenderer.render && !(adUnitRenderer.backupOnly === true && bid.renderer)) { renderer = adUnitRenderer; } diff --git a/src/config.js b/src/config.js index daaf739bbbd..51184a8014d 100644 --- a/src/config.js +++ b/src/config.js @@ -324,6 +324,119 @@ export function newConfig() { return bidderConfig; } + /** + * Returns backwards compatible FPD data for modules + */ + function getLegacyFpd(obj) { + if (typeof obj !== 'object') return; + + let duplicate = {}; + + Object.keys(obj).forEach((type) => { + let prop = (type === 'site') ? 'context' : type; + duplicate[prop] = (prop === 'context' || prop === 'user') ? Object.keys(obj[type]).filter(key => key !== 'data').reduce((result, key) => { + if (key === 'ext') { + utils.mergeDeep(result, obj[type][key]); + } else { + utils.mergeDeep(result, {[key]: obj[type][key]}); + } + + return result; + }, {}) : obj[type]; + }); + + return duplicate; + } + + /** + * Returns backwards compatible FPD data for modules + */ + function getLegacyImpFpd(obj) { + if (typeof obj !== 'object') return; + + let duplicate = {}; + + if (utils.deepAccess(obj, 'ext.data')) { + Object.keys(obj.ext.data).forEach((key) => { + if (key === 'pbadslot') { + utils.mergeDeep(duplicate, {context: {pbAdSlot: obj.ext.data[key]}}); + } else if (key === 'adserver') { + utils.mergeDeep(duplicate, {context: {adServer: obj.ext.data[key]}}); + } else { + utils.mergeDeep(duplicate, {context: {data: {[key]: obj.ext.data[key]}}}); + } + }); + } + + return duplicate; + } + + /** + * Copy FPD over to OpenRTB standard format in config + */ + function convertFpd(opt) { + let duplicate = {}; + + Object.keys(opt).forEach((type) => { + let prop = (type === 'context') ? 'site' : type; + duplicate[prop] = (prop === 'site' || prop === 'user') ? Object.keys(opt[type]).reduce((result, key) => { + if (key === 'data') { + utils.mergeDeep(result, {ext: {data: opt[type][key]}}); + } else { + utils.mergeDeep(result, {[key]: opt[type][key]}); + } + + return result; + }, {}) : opt[type]; + }); + + return duplicate; + } + + /** + * Copy Impression FPD over to OpenRTB standard format in config + * Only accepts bid level context.data values with pbAdSlot and adServer exceptions + */ + function convertImpFpd(opt) { + let duplicate = {}; + + Object.keys(opt).filter(prop => prop === 'context').forEach((type) => { + Object.keys(opt[type]).forEach((key) => { + if (key === 'data') { + utils.mergeDeep(duplicate, {ext: {data: opt[type][key]}}); + } else { + if (typeof opt[type][key] === 'object' && !Array.isArray(opt[type][key])) { + Object.keys(opt[type][key]).forEach(data => { + utils.mergeDeep(duplicate, {ext: {data: {[key.toLowerCase()]: {[data.toLowerCase()]: opt[type][key][data]}}}}); + }); + } else { + utils.mergeDeep(duplicate, {ext: {data: {[key.toLowerCase()]: opt[type][key]}}}); + } + } + }); + }); + + return duplicate; + } + + /** + * Copy FPD over to OpenRTB standard format in each adunit + */ + function convertAdUnitFpd(arr) { + let convert = []; + + arr.forEach((adunit) => { + if (adunit.fpd) { + (adunit['ortb2Imp']) ? utils.mergeDeep(adunit['ortb2Imp'], convertImpFpd(adunit.fpd)) : adunit['ortb2Imp'] = convertImpFpd(adunit.fpd); + convert.push((({ fpd, ...duplicate }) => duplicate)(adunit)); + } else { + convert.push(adunit); + } + }); + + return convert; + } + /* * Sets configuration given an object containing key-value pairs and calls * listeners that were added by the `subscribe` function @@ -338,13 +451,14 @@ export function newConfig() { let topicalConfig = {}; topics.forEach(topic => { - let option = options[topic]; + let prop = (topic === 'fpd') ? 'ortb2' : topic; + let option = (topic === 'fpd') ? convertFpd(options[topic]) : options[topic]; - if (utils.isPlainObject(defaults[topic]) && utils.isPlainObject(option)) { - option = Object.assign({}, defaults[topic], option); + if (utils.isPlainObject(defaults[prop]) && utils.isPlainObject(option)) { + option = Object.assign({}, defaults[prop], option); } - topicalConfig[topic] = config[topic] = option; + topicalConfig[prop] = config[prop] = option; }); callSubscribers(topicalConfig); @@ -437,11 +551,13 @@ export function newConfig() { bidderConfig[bidder] = {}; } Object.keys(config.config).forEach(topic => { - let option = config.config[topic]; + let prop = (topic === 'fpd') ? 'ortb2' : topic; + let option = (topic === 'fpd') ? convertFpd(config.config[topic]) : config.config[topic]; + if (utils.isPlainObject(option)) { - bidderConfig[bidder][topic] = Object.assign({}, bidderConfig[bidder][topic] || {}, option); + bidderConfig[bidder][prop] = Object.assign({}, bidderConfig[bidder][prop] || {}, option); } else { - bidderConfig[bidder][topic] = option; + bidderConfig[bidder][prop] = option; } }); }); @@ -499,7 +615,10 @@ export function newConfig() { runWithBidder, callbackWithBidder, setBidderConfig, - getBidderConfig + getBidderConfig, + convertAdUnitFpd, + getLegacyFpd, + getLegacyImpFpd }; } diff --git a/src/native.js b/src/native.js index 2d22e16c8a5..b43e582327b 100644 --- a/src/native.js +++ b/src/native.js @@ -165,30 +165,38 @@ export function getNativeTargeting(bid, bidReq) { `nativeParams.sendTargetingKeys` ) !== false; - Object.keys(bid['native']).forEach(asset => { - if (asset !== 'adTemplate') { - const key = CONSTANTS.NATIVE_KEYS[asset]; - let value = getAssetValue(bid['native'][asset]); - - const sendPlaceholder = deepAccess( - bidReq, - `mediaTypes.native.${asset}.sendId` - ); - - if (sendPlaceholder) { - const placeholder = `${key}:${bid.adId}`; - value = placeholder; - } - - const assetSendTargetingKeys = deepAccess( - bidReq, - `nativeParams.${asset}.sendTargetingKeys`); - - const sendTargeting = typeof assetSendTargetingKeys === 'boolean' ? assetSendTargetingKeys : globalSendTargetingKeys; - - if (key && value && sendTargeting) { - keyValues[key] = value; - } + const nativeKeys = getNativeKeys(bidReq); + + const flatBidNativeKeys = { ...bid.native, ...bid.native.ext }; + delete flatBidNativeKeys.ext; + + Object.keys(flatBidNativeKeys).forEach(asset => { + const key = nativeKeys[asset]; + let value = getAssetValue(bid.native[asset]) || getAssetValue(deepAccess(bid, `native.ext.${asset}`)); + + if (asset === 'adTemplate' || !key || !value) { + return; + } + + let sendPlaceholder = deepAccess(bidReq, `nativeParams.${asset}.sendId`); + if (typeof sendPlaceholder !== 'boolean') { + sendPlaceholder = deepAccess(bidReq, `nativeParams.ext.${asset}.sendId`); + } + + if (sendPlaceholder) { + const placeholder = `${key}:${bid.adId}`; + value = placeholder; + } + + let assetSendTargetingKeys = deepAccess(bidReq, `nativeParams.${asset}.sendTargetingKeys`) + if (typeof assetSendTargetingKeys !== 'boolean') { + assetSendTargetingKeys = deepAccess(bidReq, `nativeParams.ext.${asset}.sendTargetingKeys`); + } + + const sendTargeting = typeof assetSendTargetingKeys === 'boolean' ? assetSendTargetingKeys : globalSendTargetingKeys; + + if (sendTargeting) { + keyValues[key] = value; } }); @@ -234,6 +242,13 @@ export function getAllAssetsMessage(data, adObject) { message.adTemplate = getAssetValue(adObject.native[key]); } else if (key === 'rendererUrl' && adObject.native[key]) { message.rendererUrl = getAssetValue(adObject.native[key]); + } else if (key === 'ext') { + Object.keys(adObject.native[key]).forEach(extKey => { + if (adObject.native[key][extKey]) { + const value = getAssetValue(adObject.native[key][extKey]); + message.assets.push({ key: extKey, value }); + } + }) } else if (adObject.native[key] && CONSTANTS.NATIVE_KEYS.hasOwnProperty(key)) { const value = getAssetValue(adObject.native[key]); @@ -255,3 +270,18 @@ function getAssetValue(value) { return value; } + +function getNativeKeys(bidReq) { + const extraNativeKeys = {} + + if (deepAccess(bidReq, 'nativeParams.ext')) { + Object.keys(bidReq.nativeParams.ext).forEach(extKey => { + extraNativeKeys[extKey] = `hb_native_${extKey}`; + }) + } + + return { + ...CONSTANTS.NATIVE_KEYS, + ...extraNativeKeys + } +} diff --git a/src/prebid.js b/src/prebid.js index 3452107effd..6565c1610d8 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -595,11 +595,7 @@ $$PREBID_GLOBAL$$.requestBids.before(executeCallbacks, 49); */ $$PREBID_GLOBAL$$.addAdUnits = function (adUnitArr) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.addAdUnits', arguments); - if (utils.isArray(adUnitArr)) { - $$PREBID_GLOBAL$$.adUnits.push.apply($$PREBID_GLOBAL$$.adUnits, adUnitArr); - } else if (typeof adUnitArr === 'object') { - $$PREBID_GLOBAL$$.adUnits.push(adUnitArr); - } + $$PREBID_GLOBAL$$.adUnits.push.apply($$PREBID_GLOBAL$$.adUnits, config.convertAdUnitFpd(utils.isArray(adUnitArr) ? adUnitArr : [adUnitArr])); // emit event events.emit(ADD_AD_UNITS); }; diff --git a/src/storageManager.js b/src/storageManager.js index 60e5a7706d0..66a0cf68cbf 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -1,4 +1,4 @@ -import { hook } from './hook.js'; +import {hook} from './hook.js'; import * as utils from './utils.js'; import includes from 'core-js-pure/features/array/includes.js'; @@ -110,7 +110,12 @@ export function newStorageManager({gvlid, moduleName, moduleType} = {}) { try { localStorage.setItem('prebid.cookieTest', '1'); return localStorage.getItem('prebid.cookieTest') === '1'; - } catch (error) {} + } catch (error) { + } finally { + try { + localStorage.removeItem('prebid.cookieTest'); + } catch (error) {} + } } return false; } diff --git a/src/targeting.js b/src/targeting.js index aa80ce6fd5d..cb53ea0fed3 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -20,7 +20,7 @@ export const TARGETING_KEYS = Object.keys(CONSTANTS.TARGETING_KEYS).map( ); // return unexpired bids -const isBidNotExpired = (bid) => (bid.responseTimestamp + bid.ttl * 1000 + TTL_BUFFER) > timestamp(); +const isBidNotExpired = (bid) => (bid.responseTimestamp + bid.ttl * 1000 - TTL_BUFFER) > timestamp(); // return bids whose status is not set. Winning bids can only have a status of `rendered`. const isUnusedBid = (bid) => bid && ((bid.status && !includes([CONSTANTS.BID_STATUS.RENDERED], bid.status)) || !bid.status); diff --git a/src/utils.js b/src/utils.js index acdf0f101ad..bd3257ab7e7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -43,6 +43,14 @@ export const internal = { deepEqual }; +let prebidInternal = {} +/** + * Returns object that is used as internal prebid namespace + */ +export function getPrebidInternal() { + return prebidInternal; +} + var uniqueRef = {}; export let bind = function(a, b) { return b; }.bind(null, 1, uniqueRef)() === uniqueRef ? Function.prototype.bind diff --git a/test/spec/adUnits_spec.js b/test/spec/adUnits_spec.js index 7dd48a13208..baa5b4ac8c4 100644 --- a/test/spec/adUnits_spec.js +++ b/test/spec/adUnits_spec.js @@ -23,6 +23,16 @@ describe('Publisher API _ AdUnits', function () { } ] }, { + fpd: { + context: { + pbAdSlot: 'adSlotTest', + data: { + inventory: [4], + keywords: 'foo,bar', + visitor: [1, 2, 3], + } + } + }, code: '/1996833/slot-2', sizes: [[468, 60]], bids: [ @@ -85,6 +95,7 @@ describe('Publisher API _ AdUnits', function () { it('the second adUnits value should be same with the adUnits that is added by $$PREBID_GLOBAL$$.addAdUnits();', function () { assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); assert.deepEqual(adUnit2.sizes, [[468, 60]], 'adUnit2 sizes'); + assert.deepEqual(adUnit2['ortb2Imp'], {'ext': {'data': {'pbadslot': 'adSlotTest', 'inventory': [4], 'keywords': 'foo,bar', 'visitor': [1, 2, 3]}}}, 'adUnit2 ortb2Imp'); assert.strictEqual(bids2[0].bidder, 'rubicon', 'adUnit2 bids1 bidder'); assert.strictEqual(bids2[0].params.rp_account, '4934', 'adUnit2 bids1 params.rp_account'); assert.strictEqual(bids2[0].params.rp_zonesize, '23948-15', 'adUnit2 bids1 params.rp_zonesize'); diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 81ce966efb2..0b8dd6978cf 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -6,6 +6,8 @@ const utils = require('src/utils'); let getConfig; let setConfig; +let getBidderConfig; +let setBidderConfig; let setDefaults; describe('config API', function () { @@ -15,6 +17,8 @@ describe('config API', function () { const config = newConfig(); getConfig = config.getConfig; setConfig = config.setConfig; + getBidderConfig = config.getBidderConfig; + setBidderConfig = config.setBidderConfig; setDefaults = config.setDefaults; logErrorSpy = sinon.spy(utils, 'logError'); logWarnSpy = sinon.spy(utils, 'logWarn'); @@ -57,6 +61,17 @@ describe('config API', function () { expect(getConfig('foo')).to.eql({baz: 'qux'}); }); + it('moves fpd config into ortb2 properties', function () { + setConfig({fpd: {context: {keywords: 'foo,bar', data: {inventory: [1]}}}}); + expect(getConfig('ortb2')).to.eql({site: {keywords: 'foo,bar', ext: {data: {inventory: [1]}}}}); + expect(getConfig('fpd')).to.eql(undefined); + }); + + it('moves fpd bidderconfig into ortb2 properties', function () { + setBidderConfig({bidders: ['bidderA'], config: {fpd: {context: {keywords: 'foo,bar', data: {inventory: [1]}}}}}); + expect(getBidderConfig()).to.eql({'bidderA': {ortb2: {site: {keywords: 'foo,bar', ext: {data: {inventory: [1]}}}}}}); + }); + it('sets debugging', function () { setConfig({ debug: true }); expect(getConfig('debug')).to.be.true; diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js index 3dccbb28426..cb05fa62ab6 100644 --- a/test/spec/modules/a4gBidAdapter_spec.js +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/a4gBidAdapter.js'; +import * as utils from 'src/utils.js'; describe('a4gAdapterTests', function () { describe('bidRequestValidity', function () { @@ -139,15 +140,54 @@ describe('a4gAdapterTests', function () { const bidResponse = { body: [{ - 'id': 'div-gpt-ad-1460505748561-0', + 'id': '51ef8751f9aead', 'ad': 'test ad', 'width': 320, 'height': 250, - 'cpm': 5.2 + 'cpm': 5.2, + 'crid': '111' }], headers: {} }; + it('should get correct bid response for banner ad', function () { + const expectedParse = [ + { + requestId: '51ef8751f9aead', + cpm: 5.2, + creativeId: '111', + width: 320, + height: 250, + ad: 'test ad', + currency: 'USD', + ttl: 120, + netRevenue: true + } + ]; + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result[0]).to.deep.equal(expectedParse[0]); + }); + + it('should set creativeId to default value if not provided', function () { + const bidResponseWithoutCrid = utils.deepClone(bidResponse); + delete bidResponseWithoutCrid.body[0].crid; + const expectedParse = [ + { + requestId: '51ef8751f9aead', + cpm: 5.2, + creativeId: '51ef8751f9aead', + width: 320, + height: 250, + ad: 'test ad', + currency: 'USD', + ttl: 120, + netRevenue: true + } + ]; + const result = spec.interpretResponse(bidResponseWithoutCrid, bidRequest); + expect(result[0]).to.deep.equal(expectedParse[0]); + }) + it('required keys', function () { const result = spec.interpretResponse(bidResponse, bidRequest); @@ -169,5 +209,11 @@ describe('a4gAdapterTests', function () { expect(requiredKeys.indexOf(key) !== -1).to.equal(true); }); }) + + it('adId should not be equal to requestId', function () { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].requestId).to.not.equal(result[0].adId); + }) }); }); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 0a585caaa1a..999773f1a1f 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1,5 +1,5 @@ import find from 'core-js-pure/features/array/find.js'; -import { expect } from 'chai'; +import { expect, util } from 'chai'; import { _features, internal as adagio, @@ -13,7 +13,8 @@ import { } from '../../../modules/adagioBidAdapter.js'; import { loadExternalScript } from '../../../src/adloader.js'; import * as utils from '../../../src/utils.js'; -import { config } from 'src/config.js'; +import { config } from '../../../src/config.js'; +import { NATIVE } from '../../../src/mediaTypes.js'; const BidRequestBuilder = function BidRequestBuilder(options) { const defaults = { @@ -879,6 +880,174 @@ describe('Adagio bid adapter', () => { expect(bidResponse.vastUrl).to.match(/^data:text\/xml;/) }); }); + + describe('Response with native add', () => { + const serverResponseWithNative = utils.deepClone(serverResponse) + serverResponseWithNative.body.bids[0].mediaType = 'native'; + serverResponseWithNative.body.bids[0].admNative = { + ver: '1.2', + link: { + url: 'https://i.am.a.click.url', + clickTrackers: [ + 'https://i.am.a.clicktracker.url' + ] + }, + privacy: 'http://www.myprivacyurl.url', + ext: { + bvw: 'test' + }, + eventtrackers: [ + { + event: 1, + method: 1, + url: 'https://eventrack.local/impression' + }, + { + event: 1, + method: 2, + url: 'https://eventrack.local/impression' + }, + { + event: 2, + method: 1, + url: 'https://eventrack.local/viewable-mrc50' + } + ], + assets: [ + { + required: 1, + title: { + text: 'My title' + } + }, + { + img: { + url: 'https://images.local/image.jpg', + w: 100, + h: 250 + } + }, + { + img: { + type: 1, + url: 'https://images.local/icon.png', + w: 40, + h: 40 + } + }, + { + data: { + type: 1, // sponsored + value: 'Adagio' + } + }, + { + data: { + type: 2, // desc / body + value: 'The super ad text' + } + }, + { + data: { + type: 3, // rating + value: '10 from 10' + } + }, + { + data: { + type: 11, // displayUrl + value: 'https://i.am.a.display.url' + } + } + ] + }; + + const bidRequestNative = utils.deepClone(bidRequest) + bidRequestNative.mediaTypes = { + native: { + sendTargetingKeys: false, + + clickUrl: { + required: true, + }, + title: { + required: true, + }, + body: { + required: true, + }, + sponsoredBy: { + required: false + }, + image: { + required: true + }, + icon: { + required: true + }, + privacyLink: { + required: false + }, + ext: { + adagio_bvw: {} + } + } + }; + + it('Should ignore native parsing due to missing raw admNative property', () => { + const alternateServerResponse = utils.deepClone(serverResponseWithNative); + delete alternateServerResponse.body.bids[0].admNative + const r = spec.interpretResponse(alternateServerResponse, bidRequestNative); + expect(r[0].mediaType).to.equal(NATIVE); + expect(r[0].native).not.ok; + utilsMock.expects('logError').once(); + }); + + it('Should ignore native parsing due to invalid raw admNative.assets property', () => { + const alternateServerResponse = utils.deepClone(serverResponseWithNative); + alternateServerResponse.body.bids[0].admNative.assets = { title: { text: 'test' } }; + const r = spec.interpretResponse(alternateServerResponse, bidRequestNative); + expect(r[0].mediaType).to.equal(NATIVE); + expect(r[0].native).not.ok; + utilsMock.expects('logError').once(); + }); + + it('Should handle and return a formated Native ad', () => { + const r = spec.interpretResponse(serverResponseWithNative, bidRequestNative); + const expected = { + displayUrl: 'https://i.am.a.display.url', + sponsoredBy: 'Adagio', + body: 'The super ad text', + rating: '10 from 10', + clickUrl: 'https://i.am.a.click.url', + title: 'My title', + impressionTrackers: [ + 'https://eventrack.local/impression' + ], + javascriptTrackers: '', + clickTrackers: [ + 'https://i.am.a.clicktracker.url' + ], + image: { + url: 'https://images.local/image.jpg', + width: 100, + height: 250 + }, + icon: { + url: 'https://images.local/icon.png', + width: 40, + height: 40 + }, + ext: { + adagio_bvw: 'test' + }, + privacyLink: 'http://www.myprivacyurl.url' + } + expect(r[0].mediaType).to.equal(NATIVE); + expect(r[0].native).ok; + expect(r[0].native).to.deep.equal(expected); + }); + }); }); describe('getUserSyncs()', function() { diff --git a/test/spec/modules/adformBidAdapter_spec.js b/test/spec/modules/adformBidAdapter_spec.js index 23db7a8dc97..18b2565d4f8 100644 --- a/test/spec/modules/adformBidAdapter_spec.js +++ b/test/spec/modules/adformBidAdapter_spec.js @@ -157,6 +157,37 @@ describe('Adform adapter', function () { assert.equal(eids, 'some_id_value'); }); + it('should add parameter to global parameters if it exists in all bids', function () { + const _bids = []; + _bids.push(bids[0]); + _bids.push(bids[1]); + _bids.push(bids[2]); + _bids[0].params.mkv = 'key:value,key1:value1'; + _bids[1].params.mkv = 'key:value,key1:value1,keyR:removed'; + _bids[2].params.mkv = 'key:value,key1:value1,keyR:removed,key8:value1'; + _bids[0].params.mkw = 'targeting'; + _bids[1].params.mkw = 'targeting'; + _bids[2].params.mkw = 'targeting,targeting2'; + _bids[0].params.msw = 'search:word,search:word2'; + _bids[1].params.msw = 'search:word'; + _bids[2].params.msw = 'search:word,search:word5'; + let bidList = _bids; + let request = spec.buildRequests(bidList); + let parsedUrl = parseUrl(request.url); + assert.equal(parsedUrl.query.mkv, encodeURIComponent('key:value,key1:value1')); + assert.equal(parsedUrl.query.mkw, 'targeting'); + assert.equal(parsedUrl.query.msw, encodeURIComponent('search:word')); + assert.ok(!parsedUrl.items[0].mkv); + assert.ok(!parsedUrl.items[0].mkw); + assert.equal(parsedUrl.items[0].msw, 'search:word2'); + assert.equal(parsedUrl.items[1].mkv, 'keyR:removed'); + assert.ok(!parsedUrl.items[1].mkw); + assert.ok(!parsedUrl.items[1].msw); + assert.equal(parsedUrl.items[2].mkv, 'keyR:removed,key8:value1'); + assert.equal(parsedUrl.items[2].mkw, 'targeting2'); + assert.equal(parsedUrl.items[2].msw, 'search:word5'); + }); + describe('user privacy', function () { it('should send GDPR Consent data to adform if gdprApplies', function () { let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); diff --git a/test/spec/modules/adhashBidAdapter_spec.js b/test/spec/modules/adhashBidAdapter_spec.js new file mode 100644 index 00000000000..ab4df84c093 --- /dev/null +++ b/test/spec/modules/adhashBidAdapter_spec.js @@ -0,0 +1,155 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adhashBidAdapter.js'; + +describe('adhashBidAdapter', function () { + describe('isBidRequestValid', function () { + const validBid = { + bidder: 'adhash', + params: { + publisherId: '0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb', + platformURL: 'https://adhash.org/p/struma/' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250]], + bidId: '12345678901234', + bidderRequestId: '98765432109876', + auctionId: '01234567891234', + }; + + it('should return true when all mandatory parameters are there', function () { + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + + it('should return false when there are no params', function () { + const bid = { ...validBid }; + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when unsupported media type is requested', function () { + const bid = { ...validBid }; + bid.mediaTypes = { native: { sizes: [[300, 250]] } }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when publisherId is not a string', function () { + const bid = { ...validBid }; + bid.params.publisherId = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when publisherId is not valid', function () { + const bid = { ...validBid }; + bid.params.publisherId = 'short string'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when publisherId is not a string', function () { + const bid = { ...validBid }; + bid.params.platformURL = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when publisherId is not valid', function () { + const bid = { ...validBid }; + bid.params.platformURL = 'https://'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequest = { + params: { + publisherId: '0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb' + }, + sizes: [[300, 250]], + adUnitCode: 'adUnitCode' + }; + it('should build the request correctly', function () { + const result = spec.buildRequests( + [ bidRequest ], + { gdprConsent: true, refererInfo: { referer: 'http://example.com/' } } + ); + expect(result.length).to.equal(1); + expect(result[0].method).to.equal('POST'); + expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true'); + expect(result[0].bidRequest).to.equal(bidRequest); + expect(result[0].data).to.have.property('timezone'); + expect(result[0].data).to.have.property('location'); + expect(result[0].data).to.have.property('publisherId'); + expect(result[0].data).to.have.property('size'); + expect(result[0].data).to.have.property('navigator'); + expect(result[0].data).to.have.property('creatives'); + expect(result[0].data).to.have.property('blockedCreatives'); + expect(result[0].data).to.have.property('currentTimestamp'); + expect(result[0].data).to.have.property('recentAds'); + }); + it('should build the request correctly without referer', function () { + const result = spec.buildRequests([ bidRequest ], { gdprConsent: true }); + expect(result.length).to.equal(1); + expect(result[0].method).to.equal('POST'); + expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true'); + expect(result[0].bidRequest).to.equal(bidRequest); + expect(result[0].data).to.have.property('timezone'); + expect(result[0].data).to.have.property('location'); + expect(result[0].data).to.have.property('publisherId'); + expect(result[0].data).to.have.property('size'); + expect(result[0].data).to.have.property('navigator'); + expect(result[0].data).to.have.property('creatives'); + expect(result[0].data).to.have.property('blockedCreatives'); + expect(result[0].data).to.have.property('currentTimestamp'); + expect(result[0].data).to.have.property('recentAds'); + }); + }); + + describe('interpretResponse', function () { + const request = { + data: { some: 'data' }, + bidRequest: { + bidId: '12345678901234', + adUnitCode: 'adunit-code', + sizes: [[300, 250]], + params: { + platformURL: 'https://adhash.org/p/struma/' + } + } + }; + + it('should interpret the response correctly', function () { + const serverResponse = { + body: { + creatives: [{ + costEUR: 1.234 + }] + } + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result.length).to.equal(1); + expect(result[0].requestId).to.equal('12345678901234'); + expect(result[0].cpm).to.equal(1.234); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].creativeId).to.equal('adunit-code'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].ttl).to.equal(60); + }); + + it('should return empty array when there are no creatives returned', function () { + expect(spec.interpretResponse({body: {creatives: []}}, request).length).to.equal(0); + }); + + it('should return empty array when there is no creatives key in the response', function () { + expect(spec.interpretResponse({body: {}}, request).length).to.equal(0); + }); + + it('should return empty array when something is not right', function () { + expect(spec.interpretResponse(null, request).length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index 0089a403749..2ce40fd7ffb 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -17,10 +17,9 @@ let minimalBid = function() { } }; -let bidWithParams = function(data, userId) { +let bidWithParams = function(data) { let bid = minimalBid(); bid.params.data = data; - bid.userId = userId; return bid; }; @@ -117,10 +116,19 @@ describe('AdheseAdapter', function () { expect(JSON.parse(req.data).parameters).to.deep.include({ 'xf': [ 'aHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ' ] }); }); - it('should include id5 id as /x5 param', function () { - let req = spec.buildRequests([ bidWithParams({}, { 'id5id': { 'uid': 'ID5-1234567890' } }) ], bidderRequest); + it('should include eids', function () { + let bid = minimalBid(); + bid.userIdAsEids = [{ source: 'id5-sync.com', uids: [{ id: 'ID5@59sigaS-...' }] }]; + + let req = spec.buildRequests([ bid ], bidderRequest); + + expect(JSON.parse(req.data).user.ext.eids).to.deep.equal(bid.userIdAsEids); + }); + + it('should not include eids field when userid module disabled', function () { + let req = spec.buildRequests([ minimalBid() ], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({ 'x5': [ 'ID5-1234567890' ] }); + expect(JSON.parse(req.data)).to.not.have.key('eids'); }); it('should include bids', function () { diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 4454aa00a71..0d772423a22 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -556,7 +556,7 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(11); + expect(spec.aliases).to.have.lengthOf(12); }); }); diff --git a/test/spec/modules/adrelevantisBidAdapter_spec.js b/test/spec/modules/adrelevantisBidAdapter_spec.js index 11a6a14a353..596f3bade5d 100644 --- a/test/spec/modules/adrelevantisBidAdapter_spec.js +++ b/test/spec/modules/adrelevantisBidAdapter_spec.js @@ -224,12 +224,14 @@ describe('AdrelevantisAdapter', function () { let bidRequest = Object.assign({}, bidRequests[0]); sinon .stub(config, 'getConfig') - .withArgs('fpd') + .withArgs('ortb2') .returns({ - context: { + site: { keywords: 'US Open', - data: { - category: 'sports/tennis' + ext: { + data: { + category: 'sports/tennis' + } } } }); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 7a0d704d7c2..67372216e96 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -14,7 +14,8 @@ const aliasEP = { appaloosa: 'https://ghb.hb.appaloosa.media/v2/auction/', appaloosa_publisherSuffix: 'https://ghb.hb.appaloosa.media/v2/auction/', onefiftytwomedia: 'https://ghb.ads.152media.com/v2/auction/', - mediafuse: 'https://ghb.hbmp.mediafuse.com/v2/auction/' + mediafuse: 'https://ghb.hbmp.mediafuse.com/v2/auction/', + navelix: 'https://ghb.hb.navelix.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; diff --git a/test/spec/modules/adtrueBidAdapter_spec.js b/test/spec/modules/adtrueBidAdapter_spec.js index a7732d6aeb7..8e1c872d460 100644 --- a/test/spec/modules/adtrueBidAdapter_spec.js +++ b/test/spec/modules/adtrueBidAdapter_spec.js @@ -8,6 +8,7 @@ describe('AdTrueBidAdapter', function () { const adapter = newBidder(spec) let bidRequests; let bidResponses; + let bidResponses2; beforeEach(function () { bidRequests = [ { @@ -48,34 +49,85 @@ describe('AdTrueBidAdapter', function () { } ]; bidResponses = { - 'body': { - 'id': '1610681506302', - 'seatbid': [ + body: { + id: '1610681506302', + seatbid: [ { - 'bid': [ + bid: [ { - 'id': '1', - 'impid': '201fb513ca24e9', - 'price': 2.880000114440918, - 'burl': 'https://hb.adtrue.com/prebid/win-notify?impid=1610681506302&wp=${AUCTION_PRICE}', - 'adm': '', - 'adid': '1610681506302', - 'adomain': [ + id: '1', + impid: '201fb513ca24e9', + price: 2.880000114440918, + burl: 'https://hb.adtrue.com/prebid/win-notify?impid=1610681506302&wp=${AUCTION_PRICE}', + adm: '', + adid: '1610681506302', + adomain: [ 'adtrue.com' ], - 'cid': 'f6l0r6n', - 'crid': 'abc77au4', - 'attr': [], - 'w': 300, - 'h': 250 + cid: 'f6l0r6n', + crid: 'abc77au4', + attr: [], + w: 300, + h: 250 } ], - 'seat': 'adtrue', - 'group': 0 + seat: 'adtrue', + group: 0 } ], - 'bidid': '1610681506302', - 'cur': 'USD' + bidid: '1610681506302', + cur: 'USD', + ext: { + cookie_sync: [ + { + type: 1, + url: 'https://hb.adtrue.com/prebid/usersync?bidder=adtrue' + } + ] + } + } + }; + bidResponses2 = { + body: { + id: '1610681506302', + seatbid: [ + { + bid: [ + { + id: '1', + impid: '201fb513ca24e9', + price: 2.880000114440918, + burl: 'https://hb.adtrue.com/prebid/win-notify?impid=1610681506302&wp=${AUCTION_PRICE}', + adm: '', + adid: '1610681506302', + adomain: [ + 'adtrue.com' + ], + cid: 'f6l0r6n', + crid: 'abc77au4', + attr: [], + w: 300, + h: 250 + } + ], + seat: 'adtrue', + group: 0 + } + ], + bidid: '1610681506302', + cur: 'USD', + ext: { + cookie_sync: [ + { + type: 2, + url: 'https://hb.adtrue.com/prebid/usersync?bidder=adtrue&type=image' + }, + { + type: 1, + url: 'https://hb.adtrue.com/prebid/usersync?bidder=appnexus' + } + ] + } } }; }); @@ -349,8 +401,6 @@ describe('AdTrueBidAdapter', function () { }); }); describe('getUserSyncs', function () { - let USER_SYNC_URL_IFRAME = 'https://hb.adtrue.com/prebid/usersync?t=iframe&p=1212'; - let USER_SYNC_URL_IMAGE = 'https://hb.adtrue.com/prebid/usersync?t=img&p=1212'; let sandbox; beforeEach(function () { sandbox = sinon.sandbox.create(); @@ -359,12 +409,23 @@ describe('AdTrueBidAdapter', function () { sandbox.restore(); }); it('execute as per config', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, undefined, undefined)).to.deep.equal([{ - type: 'iframe', url: USER_SYNC_URL_IFRAME - }]); - expect(spec.getUserSyncs({iframeEnabled: false}, {}, undefined, undefined)).to.deep.equal([{ - type: 'image', url: USER_SYNC_URL_IMAGE + expect(spec.getUserSyncs({iframeEnabled: true}, [bidResponses], undefined, undefined)).to.deep.equal([{ + type: 'iframe', + url: 'https://hb.adtrue.com/prebid/usersync?bidder=adtrue&publisherId=1212&zoneId=21423&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' }]); }); + // Multiple user sync output + it('execute as per config', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, [bidResponses2], undefined, undefined)).to.deep.equal([ + { + type: 'image', + url: 'https://hb.adtrue.com/prebid/usersync?bidder=adtrue&type=image&publisherId=1212&zoneId=21423&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' + }, + { + type: 'iframe', + url: 'https://hb.adtrue.com/prebid/usersync?bidder=appnexus&publisherId=1212&zoneId=21423&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' + } + ]); + }); }); }); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index ec8f2f00923..8f60cbbbe3e 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -58,12 +58,137 @@ describe('Adyoulike Adapter', function () { 'mediaTypes': { 'banner': {'sizes': ['300x250'] + }, + 'native': + { 'image': { + 'required': true, + }, + 'title': { + 'required': true, + 'len': 80 + }, + 'cta': { + 'required': false + }, + 'sponsoredBy': { + 'required': true + }, + 'clickUrl': { + 'required': true + }, + 'privacyIcon': { + 'required': false + }, + 'privacyLink': { + 'required': false + }, + 'body': { + 'required': true + }, + 'icon': { + 'required': true, + 'sizes': [] } + }, }, 'transactionId': 'bid_id_0_transaction_id' } ]; + const bidRequestWithNativeImageType = [ + { + 'bidId': 'bid_id_0', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-0', + 'params': { + 'placement': 'placement_0' + }, + 'sizes': '300x250', + 'mediaTypes': + { + 'native': { + 'type': 'image', + 'additional': { + 'will': 'be', + 'sent': ['300x250'] + } + }, + }, + 'transactionId': 'bid_id_0_transaction_id' + } + ]; + + const sentBidNative = { + 'bid_id_0': { + 'PlacementID': 'e622af275681965d3095808561a1e510', + 'TransactionID': 'e8355240-d976-4cd5-a493-640656fe08e8', + 'AvailableSizes': '', + 'Native': { + 'image': { + 'required': true, + 'sizes': [] + }, + 'title': { + 'required': true, + 'len': 80 + }, + 'cta': { + 'required': false + }, + 'sponsoredBy': { + 'required': true + }, + 'clickUrl': { + 'required': true + }, + 'privacyIcon': { + 'required': false + }, + 'privacyLink': { + 'required': false + }, + 'body': { + 'required': true + }, + 'icon': { + 'required': true, + 'sizes': [] + } + } + } + }; + + const sentNativeImageType = { + 'additional': { + 'sent': [ + '300x250' + ], + 'will': 'be' + }, + 'body': { + 'required': false + }, + 'clickUrl': { + 'required': true + }, + 'cta': { + 'required': false + }, + 'icon': { + 'required': false + }, + 'image': { + 'required': true + }, + 'sponsoredBy': { + 'required': true + }, + 'title': { + 'required': true + }, + 'type': 'image' + }; + const bidRequestWithDCPlacement = [ { 'bidId': 'bid_id_0', @@ -165,11 +290,12 @@ describe('Adyoulike Adapter', function () { 'Placement': 'placement_0' } ]; + const admSample = "\u003cscript id=\"ayl-prebid-a11a121205932e75e622af275681965d\"\u003e\n(function(){\n\twindow.isPrebid = true\n\tvar prebidResults = /*PREBID*/{\"OnEvents\":{\"CLICK\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelCLICK.com/fake\"}],\"IMPRESSION\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelIMP.com/fake\"},{\"Kind\":\"JAVASCRIPT_URL\",\"Url\":\"https://testJsIMP.com/fake.js\"}]},\"Disabled\":false,\"Attempt\":\"a11a121205932e75e622af275681965d\",\"ApiPrefix\":\"https://fo-api.omnitagjs.com/fo-api\",\"TrackingPrefix\":\"https://tracking.omnitagjs.com/tracking\",\"DynamicPrefix\":\"https://tag-dyn.omnitagjs.com/fo-dyn\",\"StaticPrefix\":\"https://fo-static.omnitagjs.com/fo-static\",\"BlobPrefix\":\"https://fo-api.omnitagjs.com/fo-api/blobs\",\"SspPrefix\":\"https://fo-ssp.omnitagjs.com/fo-ssp\",\"VisitorPrefix\":\"https://visitor.omnitagjs.com/visitor\",\"Trusted\":true,\"Placement\":\"e622af275681965d3095808561a1e510\",\"PlacementAccess\":\"ALL\",\"Site\":\"6e2df7a92203c3c7a25561ed63f25a27\",\"Lang\":\"EN\",\"SiteLogo\":null,\"HasSponsorImage\":false,\"ResizeIframe\":true,\"IntegrationConfig\":{\"Kind\":\"WIDGET\",\"Widget\":{\"ExtraStyleSheet\":\"\",\"Placeholders\":{\"Body\":{\"Color\":{\"R\":77,\"G\":21,\"B\":82,\"A\":100},\"BackgroundColor\":{\"R\":255,\"G\":255,\"B\":255,\"A\":100},\"FontFamily\":\"Lato\",\"Width\":\"100%\",\"Align\":\"\",\"BoxShadow\":true},\"CallToAction\":{\"Color\":{\"R\":26,\"G\":157,\"B\":212,\"A\":100}},\"Description\":{\"Length\":130},\"Image\":{\"Width\":600,\"Height\":600,\"Lowres\":false,\"Raw\":false},\"Size\":{\"Height\":\"250px\",\"Width\":\"300px\"},\"Sponsor\":{\"Color\":{\"R\":35,\"G\":35,\"B\":35,\"A\":100},\"Label\":true,\"WithoutLogo\":false},\"Title\":{\"Color\":{\"R\":219,\"G\":181,\"B\":255,\"A\":100}}},\"Selector\":{\"Kind\":\"CSS\",\"Css\":\"#ayl-prebid-a11a121205932e75e622af275681965d\"},\"Insertion\":\"AFTER\",\"ClickFormat\":true,\"Creative20\":true,\"WidgetKind\":\"CREATIVE_TEMPLATE_4\"}},\"Legal\":\"Sponsored\",\"ForcedCampaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"ForcedTrack\":\"\",\"ForcedCreative\":\"\",\"ForcedSource\":\"\",\"DisplayMode\":\"DEFAULT\",\"Campaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"CampaignAccess\":\"ALL\",\"CampaignKind\":\"AD_TRAFFIC\",\"DataSource\":\"LOCAL\",\"DataSourceUrl\":\"\",\"DataSourceOnEventsIsolated\":false,\"DataSourceWithoutCookie\":false,\"Content\":{\"Preview\":{\"Thumbnail\":{\"Image\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://tag-dyn.omnitagjs.com/fo-dyn/native/preview/image?key=fd4362d35bb174d6f1c80d4bb5643c22\\u0026kind=INTERNAL\\u0026ztop=0.000000\\u0026zleft=0.000000\\u0026zwidth=0.333333\\u0026zheight=1.000000\\u0026width=[width]\\u0026height=[height]\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Text\":{\"CALLTOACTION\":\"Click here to learn more\",\"DESCRIPTION\":\"Considérant l'extrémité conjoncturelle, il serait bon d'anticiper toutes les voies de bon sens.\",\"SPONSOR\":\"Tested by\",\"TITLE\":\"Adserver Traffic Redirect Internal\"},\"Sponsor\":{\"Name\":\"QA Team\"},\"Credit\":{\"Logo\":{\"Resource\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Url\":\"https://blobs.omnitagjs.com/adchoice/\"}},\"Landing\":{\"Url\":\"https://www.w3.org/People/mimasa/test/xhtml/entities/entities-11.xhtml#lat1\",\"LegacyTracking\":false},\"ViewButtons\":{\"Close\":{\"Skip\":6000}},\"InternalContentFields\":{\"AnimatedImage\":false}},\"AdDomain\":\"adyoulike.com\",\"Opener\":\"REDIRECT\",\"PerformUITriggers\":[\"CLICK\"],\"RedirectionTarget\":\"TAB\"}/*PREBID*/;\n\tvar insertAds = function insertAds() {\insertAds();\n\t}\n})();\n\u003c/script\u003e"; const responseWithSinglePlacement = [ { 'BidID': 'bid_id_0', 'Placement': 'placement_0', - 'Ad': 'placement_0', + 'Ad': admSample, 'Price': 0.5, 'Height': 600, } @@ -214,15 +340,34 @@ describe('Adyoulike Adapter', function () { 'transactionId': 'bid_id_1_transaction_id' }; + let nativeBid = { + 'bidId': 'bid_id_1', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-1', + 'params': { + 'placement': 'placement_1' + }, + mediaTypes: { + native: { + + } + }, + 'transactionId': 'bid_id_1_transaction_id' + }; + it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(!!spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when required params found for native ad', function () { + expect(!!spec.isBidRequestValid(nativeBid)).to.equal(true); }); it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.size; - expect(spec.isBidRequestValid(bid)).to.equal(false); + expect(!!spec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when required params are not passed', function () { @@ -231,7 +376,7 @@ describe('Adyoulike Adapter', function () { bid.params = { 'placement': 0 }; - expect(spec.isBidRequestValid(bid)).to.equal(false); + expect(!!spec.isBidRequestValid(bid)).to.equal(false); }); }); @@ -250,6 +395,23 @@ describe('Adyoulike Adapter', function () { canonicalQuery.restore(); }); + it('Should expand short native image config type', function() { + const request = spec.buildRequests(bidRequestWithNativeImageType, bidderRequest); + const payload = JSON.parse(request.data); + + expect(request.url).to.contain(getEndpoint()); + expect(request.method).to.equal('POST'); + expect(request.url).to.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl)); + expect(request.url).to.contains('RefererUrl=' + encodeURIComponent(referrerUrl)); + expect(request.url).to.contains('PublisherDomain=http%3A%2F%2Flocalhost%3A9876'); + + expect(payload.Version).to.equal('1.0'); + expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0'); + expect(payload.PageRefreshed).to.equal(false); + expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); + expect(payload.Bids['bid_id_0'].Native).deep.equal(sentNativeImageType); + }); + it('should add gdpr/usp consent information to the request', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let uspConsentData = '1YCC'; @@ -384,7 +546,7 @@ describe('Adyoulike Adapter', function () { expect(result.length).to.equal(1); expect(result[0].cpm).to.equal(0.5); - expect(result[0].ad).to.equal('placement_0'); + expect(result[0].ad).to.equal(admSample); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(600); }); @@ -405,5 +567,47 @@ describe('Adyoulike Adapter', function () { expect(result[1].width).to.equal(400); expect(result[1].height).to.equal(250); }); + + it('receive reponse with Native ad', function () { + serverResponse.body = responseWithSinglePlacement; + let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); + + expect(result.length).to.equal(1); + + expect(result).to.deep.equal([{ + cpm: 0.5, + creativeId: undefined, + currency: 'USD', + netRevenue: true, + requestId: 'bid_id_0', + ttl: 3600, + mediaType: 'native', + native: { + body: 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', + clickTrackers: [ + 'https://testPixelCLICK.com/fake' + ], + clickUrl: 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', + cta: 'Click here to learn more', + image: { + height: 600, + url: 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', + width: 300, + }, + impressionTrackers: [ + 'https://testPixelIMP.com/fake', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' + ], + javascriptTrackers: [ + 'https://testJsIMP.com/fake.js' + ], + privacyIcon: 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', + privacyLink: 'https://blobs.omnitagjs.com/adchoice/', + sponsoredBy: 'QA Team', + title: 'Adserver Traffic Redirect Internal', + } + + }]); + }); }); }); diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 766045b0f3e..f502d631c17 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -17,10 +17,12 @@ const embeddedTrackingPixel = `https://1x1.a-mo.net/hbx/g_impression?A=sample&B= const sampleNurl = 'https://example.exchange/nurl'; const sampleFPD = { - context: { + site: { keywords: 'sample keywords', - data: { - pageType: 'article' + ext: { + data: { + pageType: 'article' + } } }, user: { @@ -31,7 +33,7 @@ const sampleFPD = { const stubConfig = (withStub) => { const stub = sinon.stub(config, 'getConfig').callsFake( - (arg) => arg === 'fpd' ? sampleFPD : null + (arg) => arg === 'ortb2' ? sampleFPD : null ) withStub(); @@ -58,6 +60,15 @@ const sampleBidRequestBase = { endpoint: 'https://httpbin.org/post', }, sizes: [[320, 50]], + getFloor(params) { + if (params.size == null || params.currency == null || params.mediaType == null) { + throw new Error(`getFloor called with incomplete params: ${JSON.stringify(params)}`) + } + return { + floor: 0.5, + currency: 'USD' + } + }, mediaTypes: { [BANNER]: { sizes: [[300, 250]] @@ -69,13 +80,28 @@ const sampleBidRequestBase = { auctionId: utils.getUniqueIdentifierStr(), }; +const schainConfig = { + ver: '1.0', + nodes: [{ + asi: 'greatnetwork.exchange', + sid: '000001', + hp: 1, + rid: 'bid_request_1', + domain: 'publisher.com' + }] +}; + const sampleBidRequestVideo = { ...sampleBidRequestBase, bidId: sampleRequestId + '_video', sizes: [[300, 150]], + schain: schainConfig, mediaTypes: { [VIDEO]: { - sizes: [[360, 250]] + sizes: [[360, 250]], + context: 'adpod', + adPodDurationSec: 90, + contentMode: 'live' } } }; @@ -104,6 +130,7 @@ const sampleServerResponse = { 'h': 600, 'id': '2014691335735134254', 'impid': '1', + 'exp': 90, 'price': 0.25, 'w': 300 }, @@ -123,6 +150,7 @@ const sampleServerResponse = { 'h': 1, 'id': '7735706981389902829', 'impid': '1', + 'exp': 90, 'price': 0.25, 'w': 1 }, @@ -144,8 +172,11 @@ describe('AmxBidAdapter', () => { expect(spec.isBidRequestValid({params: { tagId: 'test' }})).to.equal(true) }); - it('testMode is an optional boolean', () => { - expect(spec.isBidRequestValid({params: { testMode: 1 }})).to.equal(false) + it('testMode is an optional truthy value', () => { + expect(spec.isBidRequestValid({params: { testMode: 1 }})).to.equal(true) + expect(spec.isBidRequestValid({params: { testMode: 'true' }})).to.equal(true) + // ignore invalid values (falsy) + expect(spec.isBidRequestValid({params: { testMode: 'non-truthy-invalid-value' }})).to.equal(true) expect(spec.isBidRequestValid({params: { testMode: false }})).to.equal(true) }); @@ -179,6 +210,17 @@ describe('AmxBidAdapter', () => { expect(url).to.equal('https://prebid.a-mo.net/a/c') }); + it('will read the prebid version & global', () => { + const { data: { V: prebidVersion, vg: prebidGlobal } } = spec.buildRequests([{ + ...sampleBidRequestBase, + params: { + testMode: true + } + }], sampleBidderRequest); + expect(prebidVersion).to.equal('$prebid.version$') + expect(prebidGlobal).to.equal('$$PREBID_GLOBAL$$') + }); + it('reads test mode from the first bid request', () => { const { data } = spec.buildRequests([{ ...sampleBidRequestBase, @@ -253,7 +295,7 @@ describe('AmxBidAdapter', () => { it('will forward first-party data', () => { stubConfig(() => { const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); - expect(data.fpd).to.deep.equal(sampleFPD) + expect(data.fpd2).to.deep.equal(sampleFPD) }); }); @@ -299,20 +341,24 @@ describe('AmxBidAdapter', () => { expect(data.m[sampleRequestId]).to.deep.equal({ av: true, au: 'div-gpt-ad-example', + vd: {}, ms: [ [[320, 50]], [[300, 250]], [] ], aw: 300, + sc: {}, ah: 250, tf: 0, + f: 0.5, vr: false }); expect(data.m[sampleRequestId + '_2']).to.deep.equal({ av: true, aw: 300, au: 'div-gpt-ad-example', + sc: {}, ms: [ [[320, 50]], [[300, 250]], @@ -320,7 +366,9 @@ describe('AmxBidAdapter', () => { ], i: 'example', ah: 250, + vd: {}, tf: 0, + f: 0.5, vr: false, }); }); @@ -338,7 +386,15 @@ describe('AmxBidAdapter', () => { av: true, aw: 360, ah: 250, + sc: schainConfig, + vd: { + sizes: [[360, 250]], + context: 'adpod', + adPodDurationSec: 90, + contentMode: 'live' + }, tf: 0, + f: 0.5, vr: true }); }); @@ -383,9 +439,10 @@ describe('AmxBidAdapter', () => { ...baseBidResponse.meta, mediaType: BANNER, }, + mediaType: BANNER, width: 300, height: 600, // from the bid itself - ttl: 70, + ttl: 90, ad: sampleDisplayAd( `` + `` @@ -396,20 +453,14 @@ describe('AmxBidAdapter', () => { it('can parse a video ad', () => { const parsed = spec.interpretResponse({ body: sampleServerResponse }, baseRequest) expect(parsed.length).to.equal(2) - - // we should have display, video, display - const xml = parsed[1].vastXml - delete parsed[1].vastXml - - expect(xml).to.have.string(``) - expect(xml).to.have.string(``) - expect(parsed[1]).to.deep.equal({ ...baseBidResponse, meta: { ...baseBidResponse.meta, mediaType: VIDEO, }, + mediaType: VIDEO, + vastXml: sampleVideoAd(''), width: 300, height: 250, ttl: 90, diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index da9a050a8de..c3d0d025c0c 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -1,7 +1,8 @@ import { expect } from 'chai' -import { spec } from 'modules/apacdexBidAdapter.js' +import { spec, validateGeoObject, getDomain } from '../../../modules/apacdexBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' import { userSync } from '../../../src/userSync.js'; +import { config } from 'src/config.js'; describe('ApacdexBidAdapter', function () { const adapter = newBidder(spec) @@ -199,11 +200,34 @@ describe('ApacdexBidAdapter', function () { 'bidder': 'apacdex', 'params': { 'siteId': '1a2b3c4d5e6f1a2b3c4d', + 'geo': {'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60} }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], 'targetKey': 0, 'bidId': '30b31c1838de1f', + 'userIdAsEids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'p0cCLF9JazY1ZUFjazJRb3NKbEprVTcwZ0IwRUlGalBjOG9laUZNbFJ0ZGpOSnVFbE9VMjBNMzNBTzladGt4cUVGQzBybDY2Y1FqT1dkUkFsMmJIWDRHNjlvNXJjbiUyQlZDd1dOTmt6VlV2TDhRd0F0RTlBcmpyZU5WRHBPU25GQXpyMnlT', + 'atype': 1 + }] + }, { + 'source': 'pubcid.org', + 'uids': [{ + 'id': '2ae366c2-2576-45e5-bd21-72ed10598f17', + 'atype': 1 + }] + }, { + 'source': 'sharedid.org', + 'uids': [{ + 'id': '01EZXQDVAPER4KE1VBS29XKV4Z', + 'atype': 1, + 'ext': { + 'third': '01EZXQDVAPER4KE1VBS29XKV4Z' + } + }] + }], }, { 'bidder': 'apacdex', @@ -300,10 +324,23 @@ describe('ApacdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.schain).to.deep.equal(bidRequest[0].schain) }); + it('should return a properly formatted request with eids defined', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.eids).to.deep.equal(bidRequest[0].userIdAsEids) + }); + it('should return a properly formatted request with geo defined', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.geo).to.deep.equal(bidRequest[0].params.geo) + }); it('should return a properly formatted request with us_privacy included', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.us_privacy).to.equal('someCCPAString'); }); + it('should return a properly formatted request with pbjs_debug is true', function () { + config.setConfig({debug: true}); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.test).to.equal(1) + }); }); describe('.interpretResponse', function () { @@ -601,4 +638,66 @@ describe('ApacdexBidAdapter', function () { expect(spec.getUserSyncs({ pixelEnabled: true }, [])).to.have.length(0); }); }); + + describe('validateGeoObject', function () { + it('should return true if the geo object is valid', () => { + let geoObject = { + lat: 123.5624234, + lon: 23.6712341, + accuracy: 20 + }; + expect(validateGeoObject(geoObject)).to.equal(true); + }); + + it('should return false if the geo object is not plain object', () => { + let geoObject = [{ + lat: 123.5624234, + lon: 23.6712341, + accuracy: 20 + }]; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + + it('should return false if the geo object is missing lat attribute', () => { + let geoObject = { + lon: 23.6712341, + accuracy: 20 + }; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + + it('should return false if the geo object is missing lon attribute', () => { + let geoObject = { + lat: 123.5624234, + accuracy: 20 + }; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + + it('should return false if the geo object is missing accuracy attribute', () => { + let geoObject = { + lat: 123.5624234, + lon: 23.6712341 + }; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + }); + + describe('getDomain', function () { + it('should return valid domain from publisherDomain config', () => { + let pageUrl = 'https://www.example.com/page/prebid/exam.html'; + config.setConfig({publisherDomain: pageUrl}); + expect(getDomain(pageUrl)).to.equal('example.com'); + }); + it('should return valid domain from pageUrl argument', () => { + let pageUrl = 'https://www.example.com/page/prebid/exam.html'; + config.setConfig({publisherDomain: ''}); + expect(getDomain(pageUrl)).to.equal('example.com'); + }); + it('should return undefined if pageUrl and publisherDomain not config', () => { + let pageUrl; + config.setConfig({publisherDomain: ''}); + expect(getDomain(pageUrl)).to.equal(pageUrl); + }); + }); }); diff --git a/test/spec/modules/axonixBidAdapter_spec.js b/test/spec/modules/axonixBidAdapter_spec.js new file mode 100644 index 00000000000..aac1cbe08ff --- /dev/null +++ b/test/spec/modules/axonixBidAdapter_spec.js @@ -0,0 +1,374 @@ +import { expect } from 'chai'; +import { spec } from 'modules/axonixBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; + +describe('AxonixBidAdapter', function () { + const adapter = newBidder(spec); + + const SUPPLY_ID_1 = '91fd110a-5685-11eb-8db6-a7e0eeefbbc7'; + const SUPPLY_ID_2 = '22de2092-568b-11eb-bae3-cfa975dc72aa'; + const REGION_1 = 'us-east-1'; + const REGION_2 = 'eu-west-1'; + + const BANNER_REQUEST = { + adUnitCode: 'ad_code', + bidId: 'abcd1234', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 200] + ] + } + }, + bidder: 'axonix', + params: { + supplyId: SUPPLY_ID_1, + region: REGION_1 + }, + requestId: 'q4owht8ofqi3ulwsd', + transactionId: 'fvpq3oireansdwo' + }; + + const VIDEO_REQUEST = { + adUnitCode: 'ad_code', + bidId: 'abcd1234', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'], + playerSize: [400, 300], + renderer: { + url: 'https://url.com', + backupOnly: true, + render: () => true + }, + } + }, + bidder: 'axonix', + params: { + supplyId: SUPPLY_ID_1, + region: REGION_1 + }, + requestId: 'q4owht8ofqi3ulwsd', + transactionId: 'fvpq3oireansdwo' + }; + + const BIDDER_REQUEST = { + bidderCode: 'axonix', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + timeout: 3000, + gdprConsent: { + consentString: 'BOKAVy4OKAVy4ABAB8AAAAAZ+A==', + gdprApplies: true + }, + refererInfo: { + referer: 'https://www.prebid.org', + canonicalUrl: 'https://www.prebid.org/the/link/to/the/page' + } + }; + + const BANNER_RESPONSE = { + requestId: 'f08b3a8dcff747eabada295dcf94eee0', + cpm: 6, + currency: 'USD', + width: 300, + height: 250, + ad: '', + creativeId: 'abc', + netRevenue: false, + meta: { + networkId: 'nid', + advertiserDomains: [ + 'https://the.url' + ], + secondaryCatIds: [ + 'IAB1' + ], + mediaType: 'banner' + }, + nurl: 'https://win.url' + }; + + const VIDEO_RESPONSE = { + requestId: 'f08b3a8dcff747eabada295dcf94eee0', + cpm: 6, + currency: 'USD', + width: 300, + height: 250, + ad: '', + creativeId: 'abc', + netRevenue: false, + meta: { + networkId: 'nid', + advertiserDomains: [ + 'https://the.url' + ], + secondaryCatIds: [ + 'IAB1' + ], + mediaType: 'video' + }, + nurl: 'https://win.url' + }; + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let validBids = [ + { + bidder: 'axonix', + params: { + supplyId: SUPPLY_ID_1, + region: REGION_1 + }, + }, + { + bidder: 'axonix', + params: { + supplyId: SUPPLY_ID_2, + region: REGION_2 + }, + future_parameter: { + future: 'ididid' + } + }, + ]; + + let invalidBids = [ + { + bidder: 'axonix', + params: {}, + }, + { + bidder: 'axonix', + }, + ]; + + it('should accept valid bids', function () { + for (let bid of validBids) { + expect(spec.isBidRequestValid(bid)).to.equal(true); + } + }); + + it('should reject invalid bids', function () { + for (let bid of invalidBids) { + expect(spec.isBidRequestValid(bid)).to.equal(false); + } + }); + }); + + describe('buildRequests: can handle banner ad requests', function () { + it('creates ServerRequests with the correct data', function () { + const [request] = spec.buildRequests([BANNER_REQUEST], BIDDER_REQUEST); + + expect(request).to.have.property('url', `https://openrtb-${REGION_1}.axonix.com/supply/prebid/${SUPPLY_ID_1}`); + expect(request).to.have.property('method', 'POST'); + expect(request).to.have.property('data'); + + const { data } = request; + expect(data.app).to.be.undefined; + + expect(data).to.have.property('site'); + expect(data.site).to.have.property('page', 'https://www.prebid.org'); + + expect(data).to.have.property('validBidRequest', BANNER_REQUEST); + expect(data).to.have.property('connectionType').to.exist; + expect(data).to.have.property('effectiveType').to.exist; + expect(data).to.have.property('devicetype', 2); + expect(data).to.have.property('bidfloor', 0); + expect(data).to.have.property('dnt', 0); + expect(data).to.have.property('language').to.be.a('string'); + expect(data).to.have.property('prebidVersion').to.be.a('string'); + expect(data).to.have.property('screenHeight').to.be.a('number'); + expect(data).to.have.property('screenWidth').to.be.a('number'); + expect(data).to.have.property('tmax').to.be.a('number'); + expect(data).to.have.property('ua').to.be.a('string'); + }); + + it('creates ServerRequests pointing to the correct region and endpoint if it changes', function () { + const bannerRequests = [utils.deepClone(BANNER_REQUEST), utils.deepClone(BANNER_REQUEST)]; + bannerRequests[0].params.endpoint = 'https://the.url'; + bannerRequests[1].params.endpoint = 'https://the.other.url'; + + const requests = spec.buildRequests(bannerRequests, BIDDER_REQUEST); + + requests.forEach((request, index) => { + expect(request).to.have.property('url', bannerRequests[index].params.endpoint); + }); + }); + + it('creates ServerRequests pointing to default endpoint if missing', function () { + const bannerRequests = [utils.deepClone(BANNER_REQUEST), utils.deepClone(BANNER_REQUEST)]; + bannerRequests[1].params.supplyId = SUPPLY_ID_2; + bannerRequests[1].params.region = REGION_2; + + const requests = spec.buildRequests(bannerRequests, BIDDER_REQUEST); + expect(requests[0]).to.have.property('url', `https://openrtb-${REGION_1}.axonix.com/supply/prebid/${SUPPLY_ID_1}`); + expect(requests[1]).to.have.property('url', `https://openrtb-${REGION_2}.axonix.com/supply/prebid/${SUPPLY_ID_2}`); + }); + + it('creates ServerRequests pointing to default region if missing', function () { + const bannerRequest = utils.deepClone(BANNER_REQUEST); + delete bannerRequest.params.region; + + const requests = spec.buildRequests([bannerRequest], BIDDER_REQUEST); + expect(requests[0]).to.have.property('url', `https://openrtb-${REGION_1}.axonix.com/supply/prebid/${SUPPLY_ID_1}`); + }); + }); + + describe('buildRequests: can handle video ad requests', function () { + it('creates ServerRequests with the correct data', function () { + const [request] = spec.buildRequests([VIDEO_REQUEST], BIDDER_REQUEST); + + expect(request).to.have.property('url', `https://openrtb-${REGION_1}.axonix.com/supply/prebid/${SUPPLY_ID_1}`); + expect(request).to.have.property('method', 'POST'); + expect(request).to.have.property('data'); + + const { data } = request; + expect(data.app).to.be.undefined; + + expect(data).to.have.property('site'); + expect(data.site).to.have.property('page', 'https://www.prebid.org'); + + expect(data).to.have.property('validBidRequest', VIDEO_REQUEST); + expect(data).to.have.property('connectionType').to.exist; + expect(data).to.have.property('effectiveType').to.exist; + expect(data).to.have.property('devicetype', 2); + expect(data).to.have.property('bidfloor', 0); + expect(data).to.have.property('dnt', 0); + expect(data).to.have.property('language').to.be.a('string'); + expect(data).to.have.property('prebidVersion').to.be.a('string'); + expect(data).to.have.property('screenHeight').to.be.a('number'); + expect(data).to.have.property('screenWidth').to.be.a('number'); + expect(data).to.have.property('tmax').to.be.a('number'); + expect(data).to.have.property('ua').to.be.a('string'); + }); + + it('creates ServerRequests pointing to the correct region and endpoint if it changes', function () { + const videoRequests = [utils.deepClone(VIDEO_REQUEST), utils.deepClone(VIDEO_REQUEST)]; + videoRequests[0].params.endpoint = 'https://the.url'; + videoRequests[1].params.endpoint = 'https://the.other.url'; + + const requests = spec.buildRequests(videoRequests, BIDDER_REQUEST); + + requests.forEach((request, index) => { + expect(request).to.have.property('url', videoRequests[index].params.endpoint); + }); + }); + + it('creates ServerRequests pointing to default endpoint if missing', function () { + const videoRequests = [utils.deepClone(VIDEO_REQUEST), utils.deepClone(VIDEO_REQUEST)]; + videoRequests[1].params.supplyId = SUPPLY_ID_2; + videoRequests[1].params.region = REGION_2; + + const requests = spec.buildRequests(videoRequests, BIDDER_REQUEST); + expect(requests[0]).to.have.property('url', `https://openrtb-${REGION_1}.axonix.com/supply/prebid/${SUPPLY_ID_1}`); + expect(requests[1]).to.have.property('url', `https://openrtb-${REGION_2}.axonix.com/supply/prebid/${SUPPLY_ID_2}`); + }); + + it('creates ServerRequests pointing to default region if missing', function () { + const videoRequest = utils.deepClone(VIDEO_REQUEST); + delete videoRequest.params.region; + + const requests = spec.buildRequests([videoRequest], BIDDER_REQUEST); + expect(requests[0]).to.have.property('url', `https://openrtb-${REGION_1}.axonix.com/supply/prebid/${SUPPLY_ID_1}`); + }); + }); + + describe.skip('buildRequests: can handle native ad requests', function () { + it('creates ServerRequests pointing to the correct region and endpoint if it changes', function () { + // loop: + // set supply id + // set region/endpoint in ssp config + // call buildRequests, validate request (url, method, supply id) + expect.fail('Not implemented'); + }); + + it('creates ServerRequests pointing to default endpoint if missing', function () { + // no endpoint in config means default value openrtb.axonix.com + expect.fail('Not implemented'); + }); + + it('creates ServerRequests pointing to default region if missing', function () { + // no region in config means default value us-east-1 + expect.fail('Not implemented'); + }); + }); + + describe('interpretResponse', function () { + it('considers corner cases', function() { + expect(spec.interpretResponse(null)).to.be.an('array').that.is.empty; + expect(spec.interpretResponse()).to.be.an('array').that.is.empty; + }); + + it('ignores unparseable responses', function() { + expect(spec.interpretResponse('invalid')).to.be.an('array').that.is.empty; + expect(spec.interpretResponse(['invalid'])).to.be.an('array').that.is.empty; + expect(spec.interpretResponse([{ invalid: 'object' }])).to.be.an('array').that.is.empty; + }); + + it('parses banner responses', function () { + const response = spec.interpretResponse([BANNER_RESPONSE]); + + expect(response).to.be.an('array').that.is.not.empty; + expect(response[0]).to.equal(BANNER_RESPONSE); + }); + + it('parses 1 video responses', function () { + const response = spec.interpretResponse([VIDEO_RESPONSE]); + + expect(response).to.be.an('array').that.is.not.empty; + expect(response[0]).to.equal(VIDEO_RESPONSE); + }); + + it.skip('parses 1 native responses', function () { + // passing 1 valid native in a response generates an array with 1 correct prebid response + // examine mediaType:native, native element + // check nativeBidIsValid from {@link file://./../../../src/native.js} + expect.fail('Not implemented'); + }); + }); + + describe('onBidWon', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function () { + utils.triggerPixel.restore(); + }); + + it('called once', function () { + spec.onBidWon(spec.interpretResponse([BANNER_RESPONSE])); + expect(utils.triggerPixel.calledOnce).to.equal(true); + }); + + it('called false', function () { + spec.onBidWon([{ cpm: '2.21' }]); + expect(utils.triggerPixel.called).to.equal(false); + }); + + it('when there is no notification expected server side, none is called', function () { + var response = spec.onBidWon([]); + expect(utils.triggerPixel.called).to.equal(false); + expect(response).to.be.an('undefined') + }); + }); + + describe('onTimeout', function () { + it('banner response', () => { + spec.onTimeout(spec.interpretResponse([BANNER_RESPONSE])); + }); + + it('video response', () => { + spec.onTimeout(spec.interpretResponse([VIDEO_RESPONSE])); + }); + }); +}); diff --git a/test/spec/modules/bizzclickBidAdapter_spec.js b/test/spec/modules/bizzclickBidAdapter_spec.js index 39ad4ae39c9..e0698c9eda8 100644 --- a/test/spec/modules/bizzclickBidAdapter_spec.js +++ b/test/spec/modules/bizzclickBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/bizzclickBidAdapter.js'; +import {config} from 'src/config.js'; const NATIVE_BID_REQUEST = { code: 'native_example', @@ -53,7 +54,11 @@ const BANNER_BID_REQUEST = { accountId: 'accountId' }, timeout: 1000, - + gdprConsent: { + consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + gdprApplies: 1, + }, + uspConsent: 'uspConsent' } const bidRequest = { @@ -172,6 +177,22 @@ const NATIVE_BID_RESPONSE = { }; describe('BizzclickAdapter', function() { + describe('with COPPA', function() { + beforeEach(function() { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + }); + afterEach(function() { + config.getConfig.restore(); + }); + + it('should send the Coppa "required" flag set to "1" in the request', function () { + let serverRequest = spec.buildRequests([BANNER_BID_REQUEST]); + expect(serverRequest.data[0].regs.coppa).to.equal(1); + }); + }); + describe('isBidRequestValid', function() { it('should return true when required params found', function () { expect(spec.isBidRequestValid(NATIVE_BID_REQUEST)).to.equal(true); @@ -225,6 +246,12 @@ describe('BizzclickAdapter', function() { expect(request.method).to.equal('POST'); }); + it('check consent and ccpa string is set properly', function() { + expect(request.data[0].regs.ext.gdpr).to.equal(1); + expect(request.data[0].user.ext.consent).to.equal(BANNER_BID_REQUEST.gdprConsent.consentString); + expect(request.data[0].regs.ext.us_privacy).to.equal(BANNER_BID_REQUEST.uspConsent); + }) + it('Returns valid URL', function () { expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); }); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index e91d3b10abb..cad1e3f8114 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -820,14 +820,18 @@ describe('The Criteo bidding adapter', function () { it('should properly build a request with first party data', function () { const contextData = { keywords: ['power tools'], - data: { - pageType: 'article' + ext: { + data: { + pageType: 'article' + } } }; const userData = { gender: 'M', - data: { - registered: true + ext: { + data: { + registered: true + } } }; const bidRequests = [ @@ -842,8 +846,8 @@ describe('The Criteo bidding adapter', function () { bidfloor: 0.75 } }, - fpd: { - context: { + ortb2Imp: { + ext: { data: { someContextAttribute: 'abc' } @@ -854,8 +858,8 @@ describe('The Criteo bidding adapter', function () { sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context: contextData, + ortb2: { + site: contextData, user: userData } }; @@ -863,8 +867,8 @@ describe('The Criteo bidding adapter', function () { }); const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.publisher.ext).to.deep.equal(contextData); - expect(request.data.user.ext).to.deep.equal(userData); + expect(request.data.publisher.ext).to.deep.equal({keywords: ['power tools'], data: {pageType: 'article'}}); + expect(request.data.user.ext).to.deep.equal({gender: 'M', data: {registered: true}}); expect(request.data.slots[0].ext).to.deep.equal({ bidfloor: 0.75, data: { diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 659e5257e90..bd839124b6d 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -276,6 +276,20 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('uid2', function() { + const userId = { + uid2: {'id': 'Sample_AD_Token'} + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'uidapi.com', + uids: [{ + id: 'Sample_AD_Token', + atype: 3 + }] + }); + }); it('pubProvidedId', function() { const userId = { pubProvidedId: [{ diff --git a/test/spec/modules/fabrickIdSystem_spec.js b/test/spec/modules/fabrickIdSystem_spec.js index cbd538816ab..c250c8e5e8b 100644 --- a/test/spec/modules/fabrickIdSystem_spec.js +++ b/test/spec/modules/fabrickIdSystem_spec.js @@ -1,7 +1,7 @@ import * as utils from '../../../src/utils.js'; import {server} from '../../mocks/xhr.js'; -import * as fabrickIdSystem from 'modules/fabrickIdSystem.js'; +import {fabrickIdSubmodule, appendUrl} from 'modules/fabrickIdSystem.js'; const defaultConfigParams = { apiKey: '123', @@ -10,26 +10,25 @@ const defaultConfigParams = { url: 'http://localhost:9999/test/mocks/fabrickId.json?' }; const responseHeader = {'Content-Type': 'application/json'} -const fabrickIdSubmodule = fabrickIdSystem.fabrickIdSubmodule; describe('Fabrick ID System', function() { let logErrorStub; beforeEach(function () { - logErrorStub = sinon.stub(utils, 'logError'); }); afterEach(function () { - logErrorStub.restore(); - fabrickIdSubmodule.getRefererInfoOverride = null; }); it('should log an error if no configParams were passed into getId', function () { + logErrorStub = sinon.stub(utils, 'logError'); fabrickIdSubmodule.getId(); expect(logErrorStub.calledOnce).to.be.true; + logErrorStub.restore(); }); it('should error on json parsing', function() { + logErrorStub = sinon.stub(utils, 'logError'); let submoduleCallback = fabrickIdSubmodule.getId({ name: 'fabrickId', params: defaultConfigParams @@ -44,11 +43,12 @@ describe('Fabrick ID System', function() { ); expect(callBackSpy.calledOnce).to.be.true; expect(logErrorStub.calledOnce).to.be.true; + logErrorStub.restore(); }); it('should truncate the params', function() { let r = ''; - for (let i = 0; i < 300; i++) { + for (let i = 0; i < 1500; i++) { r += 'r'; } let configParams = Object.assign({}, defaultConfigParams, { @@ -66,7 +66,7 @@ describe('Fabrick ID System', function() { submoduleCallback(callBackSpy); let request = server.requests[0]; r = ''; - for (let i = 0; i < 200; i++) { + for (let i = 0; i < 1000 - 3; i++) { r += 'r'; } expect(request.url).to.match(new RegExp(`r=${r}&r=`)); @@ -76,7 +76,6 @@ describe('Fabrick ID System', function() { JSON.stringify({}) ); expect(callBackSpy.calledOnce).to.be.true; - expect(logErrorStub.calledOnce).to.be.false; }); it('should complete successfully', function() { @@ -101,6 +100,35 @@ describe('Fabrick ID System', function() { JSON.stringify({}) ); expect(callBackSpy.calledOnce).to.be.true; - expect(logErrorStub.calledOnce).to.be.false; + }); + + it('should truncate 2', function() { + let configParams = { + maxUrlLen: 10, + maxRefLen: 5, + maxSpaceAvailable: 2 + }; + + let url = appendUrl('', 'r', '123', configParams); + expect(url).to.equal('&r=12'); + + url = appendUrl('12345', 'r', '678', configParams); + expect(url).to.equal('12345&r=67'); + + url = appendUrl('12345678', 'r', '9', configParams); + expect(url).to.equal('12345678'); + + configParams.maxRefLen = 8; + url = appendUrl('', 'r', '1234&', configParams); + expect(url).to.equal('&r=1234'); + + url = appendUrl('', 'r', '123&', configParams); + expect(url).to.equal('&r=123'); + + url = appendUrl('', 'r', '12&', configParams); + expect(url).to.equal('&r=12%26'); + + url = appendUrl('', 'r', '1&&', configParams); + expect(url).to.equal('&r=1%26'); }); }); diff --git a/test/spec/modules/gmosspBidAdapter_spec.js b/test/spec/modules/gmosspBidAdapter_spec.js index 5de3db623c5..52bb8460caa 100644 --- a/test/spec/modules/gmosspBidAdapter_spec.js +++ b/test/spec/modules/gmosspBidAdapter_spec.js @@ -54,18 +54,30 @@ describe('GmosspAdapter', function () { } ]; - const bidderRequest = { - refererInfo: { - referer: 'https://hoge.com' - } - }; - it('sends bid request to ENDPOINT via GET', function () { + const bidderRequest = { + refererInfo: { + referer: 'https://hoge.com' + } + }; + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests[0].url).to.equal(ENDPOINT); expect(requests[0].method).to.equal('GET'); expect(requests[0].data).to.equal('tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&url=https%3A%2F%2Fhoge.com&cur=JPY&dnt=0&'); }); + + it('should use fallback if refererInfo.referer in bid request is empty', function () { + const bidderRequest = { + refererInfo: { + referer: '' + } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + const result = 'tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&url=' + encodeURIComponent(window.top.location.href) + '&cur=JPY&dnt=0&'; + expect(requests[0].data).to.equal(result); + }); }); describe('interpretResponse', function () { diff --git a/test/spec/modules/gptPreAuction_spec.js b/test/spec/modules/gptPreAuction_spec.js index 16b84467af2..c4a81c21d5c 100644 --- a/test/spec/modules/gptPreAuction_spec.js +++ b/test/spec/modules/gptPreAuction_spec.js @@ -30,34 +30,34 @@ describe('GPT pre-auction module', () => { '
test2
'; it('should be unchanged if already defined on adUnit', () => { - const adUnit = { fpd: { context: { pbAdSlot: '12345' } } }; + const adUnit = { ortb2Imp: { ext: { data: { pbadslot: '12345' } } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('12345'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('12345'); }); it('should use adUnit.code if matching id exists', () => { - const adUnit = { code: 'foo1', fpd: { context: {} } }; + const adUnit = { code: 'foo1', ortb2Imp: { ext: { data: {} } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('bar1'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('bar1'); }); it('should use the gptSlot.adUnitPath if the adUnit.code matches a div id but does not have a data-adslotid', () => { - const adUnit = { code: 'foo3', mediaTypes: { banner: { sizes: [[250, 250]] } }, fpd: { context: { adServer: { name: 'gam', adSlot: '/baz' } } } }; + const adUnit = { code: 'foo3', mediaTypes: { banner: { sizes: [[250, 250]] } }, ortb2Imp: { ext: { data: { adserver: { name: 'gam', adslot: '/baz' } } } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('/baz'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('/baz'); }); it('should use the video adUnit.code (which *should* match the configured "adSlotName", but is not being tested) if there is no matching div with "data-adslotid" defined', () => { - const adUnit = { code: 'foo4', mediaTypes: { video: { sizes: [[250, 250]] } }, fpd: { context: {} } }; + const adUnit = { code: 'foo4', mediaTypes: { video: { sizes: [[250, 250]] } }, ortb2Imp: { ext: { data: {} } } }; adUnit.code = 'foo5'; appendPbAdSlot(adUnit, undefined); - expect(adUnit.fpd.context.pbAdSlot).to.equal('foo5'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('foo5'); }); it('should use the adUnit.code if all other sources failed', () => { - const adUnit = { code: 'foo4', fpd: { context: {} } }; + const adUnit = { code: 'foo4', ortb2Imp: { ext: { data: {} } } }; appendPbAdSlot(adUnit, undefined); - expect(adUnit.fpd.context.pbAdSlot).to.equal('foo4'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('foo4'); }); it('should use the customPbAdSlot function if one is given', () => { @@ -67,32 +67,32 @@ describe('GPT pre-auction module', () => { } }); - const adUnit = { code: 'foo1', fpd: { context: {} } }; + const adUnit = { code: 'foo1', ortb2Imp: { ext: { data: {} } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('customPbAdSlotName'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('customPbAdSlotName'); }); }); describe('appendGptSlots', () => { it('should not add adServer object to context if no slots defined', () => { - const adUnit = { code: 'adUnitCode', fpd: { context: {} } }; + const adUnit = { code: 'adUnitCode', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.undefined; + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.undefined; }); it('should not add adServer object to context if no slot matches', () => { window.googletag.pubads().setSlots(testSlots); - const adUnit = { code: 'adUnitCode', fpd: { context: {} } }; + const adUnit = { code: 'adUnitCode', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.undefined; + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.undefined; }); it('should add adServer object to context if matching slot is found', () => { window.googletag.pubads().setSlots(testSlots); - const adUnit = { code: 'slotCode2', fpd: { context: {} } }; + const adUnit = { code: 'slotCode2', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.an('object'); - expect(adUnit.fpd.context.adServer).to.deep.equal({ name: 'gam', adSlot: 'slotCode2' }); + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.an('object'); + expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: 'slotCode2' }); }); it('should use the customGptSlotMatching function if one is given', () => { @@ -104,10 +104,10 @@ describe('GPT pre-auction module', () => { }); window.googletag.pubads().setSlots(testSlots); - const adUnit = { code: 'SlOtCoDe1', fpd: { context: {} } }; + const adUnit = { code: 'SlOtCoDe1', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.an('object'); - expect(adUnit.fpd.context.adServer).to.deep.equal({ name: 'gam', adSlot: 'slotCode1' }); + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.an('object'); + expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: 'slotCode1' }); }); }); @@ -164,23 +164,23 @@ describe('GPT pre-auction module', () => { it('should append PB Ad Slot and GPT Slot info to first-party data in each ad unit', () => { const testAdUnits = [{ code: 'adUnit1', - fpd: { context: { pbAdSlot: '12345' } } + ortb2Imp: { ext: { data: { pbadslot: '12345' } } } }, { code: 'slotCode1', - fpd: { context: { pbAdSlot: '67890' } } + ortb2Imp: { ext: { data: { pbadslot: '67890' } } } }, { code: 'slotCode3', }]; const expectedAdUnits = [{ code: 'adUnit1', - fpd: { context: { pbAdSlot: '12345' } } + ortb2Imp: { ext: { data: { pbadslot: '12345' } } } }, { code: 'slotCode1', - fpd: { context: { pbAdSlot: '67890', adServer: { name: 'gam', adSlot: 'slotCode1' } } } + ortb2Imp: { ext: { data: { pbadslot: '67890', adserver: { name: 'gam', adslot: 'slotCode1' } } } } }, { code: 'slotCode3', - fpd: { context: { pbAdSlot: 'slotCode3', adServer: { name: 'gam', adSlot: 'slotCode3' } } } + ortb2Imp: { ext: { data: { pbadslot: 'slotCode3', adserver: { name: 'gam', adslot: 'slotCode3' } } } } }]; window.googletag.pubads().setSlots(testSlots); diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 084c67562e6..2e8601bddf6 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -105,7 +105,8 @@ describe('TheMediaGrid Adapter', function () { 'sizes': [[728, 90]], 'mediaTypes': { 'video': { - 'playerSize': [[400, 600]] + 'playerSize': [[400, 600]], + 'protocols': [1, 2, 3] }, 'banner': { 'sizes': [[728, 90]] @@ -281,7 +282,8 @@ describe('TheMediaGrid Adapter', function () { }, 'video': { 'w': 400, - 'h': 600 + 'h': 600, + 'protocols': [1, 2, 3] } }] }); @@ -398,6 +400,16 @@ describe('TheMediaGrid Adapter', function () { expect(payload.site.content).to.deep.equal(jsContent); }); + it('should contain the keyword values if it present in ortb2.(site/user)', function () { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.user' ? {'keywords': 'foo'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.ext.keywords).to.deep.equal([{'key': 'user', 'value': ['foo']}, {'key': 'context', 'value': ['bar']}]); + getConfigStub.restore(); + }); + it('shold be right tmax when timeout in config is less then timeout in bidderRequest', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 2000 : null); @@ -489,7 +501,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -547,7 +558,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -561,7 +571,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 2
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -575,7 +584,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 728, 'height': 90, 'ad': '
test content 3
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -635,7 +643,6 @@ describe('TheMediaGrid Adapter', function () { 'dealId': undefined, 'width': 300, 'height': 600, - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, @@ -652,7 +659,6 @@ describe('TheMediaGrid Adapter', function () { 'dealId': undefined, 'width': undefined, 'height': undefined, - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, @@ -784,7 +790,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -798,7 +803,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 2
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -812,7 +816,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 728, 'height': 90, 'ad': '
test content 3
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -826,7 +829,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 4
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 0dbaac0c526..2aec9713000 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -300,7 +300,6 @@ describe('TheMediaGridNM Adapter', function () { 'dealId': 11, 'width': 300, 'height': 250, - 'bidderCode': 'gridNM', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, @@ -317,7 +316,6 @@ describe('TheMediaGridNM Adapter', function () { 'dealId': undefined, 'width': 300, 'height': 600, - 'bidderCode': 'gridNM', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 0d37c8b1d25..3f5d32bcef7 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -258,6 +258,10 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data.fp).to.equal(bidfloor); }); + it('should return a floor currency', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data.fpc).to.equal(floorTestData.currency); + }) }); it('sends bid request to ENDPOINT via GET', function () { diff --git a/test/spec/modules/h12mediaBidAdapter_spec.js b/test/spec/modules/h12mediaBidAdapter_spec.js index 08a83ce981f..9861069f260 100644 --- a/test/spec/modules/h12mediaBidAdapter_spec.js +++ b/test/spec/modules/h12mediaBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec} from 'modules/h12mediaBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; describe('H12 Media Adapter', function () { const DEFAULT_CURRENCY = 'USD'; @@ -21,6 +22,7 @@ describe('H12 Media Adapter', function () { auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f313', params: { pubid: 123321, + pubsubid: 'pubsubtestid', }, }; @@ -72,34 +74,34 @@ describe('H12 Media Adapter', function () { currency: 'EUR', netRevenue: true, ttl: 500, - bids: [{ + bid: { bidId: validBid.bidId, cpm: 0.33, width: 300, height: 600, creativeId: '335566', ad: '
my ad
', - usersync: [ - {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image'}, - {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe'} - ], meta: { advertiserId: '54321', advertiserName: 'My advertiser', advertiserDomains: ['test.com'] } - }] + }, + usersync: [ + {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image'}, + {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe'} + ], }; const serverResponse2 = { - bids: [{ + bid: { bidId: validBid2.bidId, cpm: 0.33, width: 300, height: 600, creativeId: '335566', ad: '
my ad 2
', - }] + } }; function removeElement(id) { @@ -152,6 +154,10 @@ describe('H12 Media Adapter', function () { beforeEach(function () { sandbox = sinon.sandbox.create(); + sandbox.stub(frameElement, 'getBoundingClientRect').returns({ + left: 10, + top: 10, + }); }); afterEach(function () { @@ -186,36 +192,62 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({adunitSize: validBid.mediaTypes.banner.sizes}); + expect(requestsData).to.include({adunitSize: validBid.mediaTypes.banner.sizes}); }); it('should return empty bid size', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData2 = requests[1].data.bidrequest; + + expect(requestsData2).to.deep.include({adunitSize: []}); + }); + + it('should return pubsubid from params', function () { + createElementVisible(validBid.adUnitCode); + createElementVisible(validBid2.adUnitCode); + const requests = spec.buildRequests([validBid, validBid2], bidderRequest); + const requestsData = requests[0].data.bidrequest; + const requestsData2 = requests[1].data.bidrequest; + + expect(requestsData).to.include({pubsubid: 'pubsubtestid'}); + expect(requestsData2).to.include({pubsubid: ''}); + }); - expect(requestsData.bidrequests[1]).to.deep.include({adunitSize: []}); + it('should return empty for incorrect pubsubid from params', function () { + createElementVisible(validBid.adUnitCode); + createElementVisible(validBid2.adUnitCode); + const bidWithPub = {...validBid}; + bidWithPub.params.pubsubid = 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'; // More than 32 chars + const requests = spec.buildRequests([bidWithPub], bidderRequest); + const requestsData = requests[0].data.bidrequest; + + expect(requestsData).to.include({pubsubid: ''}); }); it('should return bid size from params', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; + const requestsData2 = requests[1].data.bidrequest; - expect(requestsData.bidrequests[1]).to.include({size: validBid2.params.size}); + expect(requestsData).to.include({size: ''}); + expect(requestsData2).to.include({size: validBid2.params.size}); }); it('should return GDPR info', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; expect(requestsData).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString}); + expect(requestsData2).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString}); }); it('should not have error on empty GDPR', function () { @@ -223,9 +255,23 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid2.adUnitCode); const bidderRequestWithoutGDRP = {...bidderRequest, gdprConsent: null}; const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutGDRP); - const requestsData = requests.data; + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; expect(requestsData).to.include({gdpr: false}); + expect(requestsData2).to.include({gdpr: false}); + }); + + it('should not have error on empty USP', function () { + createElementVisible(validBid.adUnitCode); + createElementVisible(validBid2.adUnitCode); + const bidderRequestWithoutUSP = {...bidderRequest, uspConsent: null}; + const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutUSP); + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; + + expect(requestsData).to.include({usp: false}); + expect(requestsData2).to.include({usp: false}); }); it('should create single POST', function () { @@ -233,7 +279,8 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - expect(requests.method).to.equal('POST'); + expect(requests[0].method).to.equal('POST'); + expect(requests[1].method).to.equal('POST'); }); }); @@ -241,42 +288,44 @@ describe('H12 Media Adapter', function () { it('should return coords', function () { createElementVisible(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.deep.include({coords: {x: 10, y: 10}}); + expect(requestsData).to.deep.include({coords: {x: 10, y: 10}}); }); - it('should define not iframe', function () { + it('should define iframe', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; - expect(requestsData).to.include({isiframe: false}); + expect(requestsData).to.include({isiframe: true}); + expect(requestsData2).to.include({isiframe: true}); }); it('should define visible element', function () { createElementVisible(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({ishidden: false}); + expect(requestsData).to.include({ishidden: false}); }); it('should define invisible element', function () { createElementInvisible(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({ishidden: true}); + expect(requestsData).to.include({ishidden: true}); }); it('should define hidden element', function () { createElementHidden(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({ishidden: true}); + expect(requestsData).to.include({ishidden: true}); }); }); @@ -290,27 +339,27 @@ describe('H12 Media Adapter', function () { it('should return no bids if the response is empty', function () { const bidResponse = spec.interpretResponse({ body: [] }, { validBid }); - expect(bidResponse.length).to.equal(0); + expect(bidResponse).to.be.empty; }); it('should return valid bid responses', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const request = spec.buildRequests([validBid, validBid2], bidderRequest); - const bidResponse = spec.interpretResponse({body: serverResponse}, request); + const bidResponse = spec.interpretResponse({body: serverResponse}, request[0]); expect(bidResponse[0]).to.deep.include({ requestId: validBid.bidId, - ad: serverResponse.bids[0].ad, + ad: serverResponse.bid.ad, mediaType: 'banner', - creativeId: serverResponse.bids[0].creativeId, - cpm: serverResponse.bids[0].cpm, - width: serverResponse.bids[0].width, - height: serverResponse.bids[0].height, + creativeId: serverResponse.bid.creativeId, + cpm: serverResponse.bid.cpm, + width: serverResponse.bid.width, + height: serverResponse.bid.height, currency: 'EUR', netRevenue: true, ttl: 500, - meta: serverResponse.bids[0].meta, + meta: serverResponse.bid.meta, pubid: validBid.params.pubid }); }); @@ -319,17 +368,17 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const request = spec.buildRequests([validBid, validBid2], bidderRequest); - const bidResponse = spec.interpretResponse({body: serverResponse2}, request); + const bidResponse = spec.interpretResponse({body: serverResponse2}, request[0]); expect(bidResponse[0]).to.deep.include({ requestId: validBid2.bidId, - ad: serverResponse2.bids[0].ad, + ad: serverResponse2.bid.ad, mediaType: 'banner', - creativeId: serverResponse2.bids[0].creativeId, - cpm: serverResponse2.bids[0].cpm, - width: serverResponse2.bids[0].width, - height: serverResponse2.bids[0].height, - meta: serverResponse2.bids[0].meta, + creativeId: serverResponse2.bid.creativeId, + cpm: serverResponse2.bid.cpm, + width: serverResponse2.bid.width, + height: serverResponse2.bid.height, + meta: serverResponse2.bid.meta, pubid: validBid2.params.pubid, currency: DEFAULT_CURRENCY, netRevenue: DEFAULT_NET_REVENUE, diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index 4d0da695d95..f34a75ef8f3 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -750,7 +750,6 @@ describe('Improve Digital Adapter Tests', function () { const expectedBid = [ { 'ad': '', - 'adId': '33e9500b21129f', 'creativeId': '422031', 'cpm': 1.45888594164456, 'currency': 'USD', @@ -767,7 +766,6 @@ describe('Improve Digital Adapter Tests', function () { expectedBid[0], { 'ad': '', - 'adId': '1234', 'creativeId': '422033', 'cpm': 1.23, 'currency': 'USD', @@ -783,7 +781,6 @@ describe('Improve Digital Adapter Tests', function () { const expectedBidNative = [ { mediaType: 'native', - adId: '33e9500b21129f', creativeId: '422031', cpm: 1.45888594164456, currency: 'USD', @@ -832,7 +829,6 @@ describe('Improve Digital Adapter Tests', function () { const expectedBidInstreamVideo = [ { 'vastXml': '', - 'adId': '33e9500b21129f', 'creativeId': '422031', 'cpm': 1.45888594164456, 'currency': 'USD', diff --git a/test/spec/modules/inskinBidAdapter_spec.js b/test/spec/modules/inskinBidAdapter_spec.js index e817b3e3b81..cedaff2a0cf 100644 --- a/test/spec/modules/inskinBidAdapter_spec.js +++ b/test/spec/modules/inskinBidAdapter_spec.js @@ -250,7 +250,7 @@ describe('InSkin BidAdapter', function () { const payload = JSON.parse(request.data); expect(payload.keywords).to.be.an('array').that.is.empty; - expect(payload.placements[0].properties).to.be.undefined; + expect(payload.placements[0].properties.restrictions).to.be.undefined; }); it('should add keywords if TCF v2 purposes are not granted', function () { diff --git a/test/spec/modules/interactiveOffersBidAdapter_spec.js b/test/spec/modules/interactiveOffersBidAdapter_spec.js new file mode 100644 index 00000000000..8826977f34a --- /dev/null +++ b/test/spec/modules/interactiveOffersBidAdapter_spec.js @@ -0,0 +1,42 @@ +import { expect } from 'chai'; +import {spec} from 'modules/interactiveOffersBidAdapter.js'; + +describe('Interactive Offers Prebbid.js Adapter', function() { + describe('isBidRequestValid function', function() { + let bid = {bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}; + + it('returns true if all the required params are present and properly formatted', function() { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('returns false if any if the required params is missing', function() { + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('returns false if any if the required params is not properly formatted', function() { + bid.params = {pubid: 'abcd123', tmax: 250}; + expect(spec.isBidRequestValid(bid)).to.be.false; + bid.params = {pubid: 100, tmax: '+250'}; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + describe('buildRequests function', function() { + let validBidRequests = [{bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}]; + let bidderRequest = {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://www.google.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://www.google.com'], canonicalUrl: null}}; + + it('returns a Prebid.js request object with a valid json string at the "data" property', function() { + let request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).length !== 0; + }); + }); + describe('interpretResponse function', function() { + let openRTBResponse = {body: [{cur: 'USD', id: '2052afa35febb79baa9893cc3ae8b83b89740df65fe98b1bd358dbae6e912801', seatbid: [{seat: 1493, bid: [{ext: {tagid: '227faa83f86546'}, crid: '24477', adm: '', nurl: '', adid: '1138', adomain: ['url.com'], price: '1.53', w: 300, h: 250, iurl: 'http://url.com', cat: ['IAB13-11'], id: '5507ced7a39c06942d3cb260197112ba712e4180', attr: [], impid: 1, cid: '13280'}]}], 'bidid': '0959b9d58ba71b3db3fa29dce3b117c01fc85de0'}], 'headers': {}}; + let prebidRequest = {method: 'POST', url: 'https://url.com', data: '{"id": "1aad860c-e04b-482b-acac-0da55ed491c8", "site": {"id": "url.com", "name": "url.com", "domain": "url.com", "page": "http://url.com", "ref": "http://url.com", "publisher": {"id": 100, "name": "http://url.com", "domain": "url.com"}, "content": {"language": "pt-PT"}}, "source": {"fd": 0, "tid": "1aad860c-e04b-482b-acac-0da55ed491c8", "pchain": ""}, "device": {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36", "language": "pt-PT"}, "user": {}, "imp": [{"id":1, "secure": 0, "tagid": "227faa83f86546", "banner": {"pos": 0, "w": 300, "h": 250, "format": [{"w": 300, "h": 250}]}}], "tmax": 300}', bidderRequest: {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://url.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://url.com'], canonicalUrl: null}}}; + + it('returns an array of Prebid.js response objects', function() { + let prebidResponses = spec.interpretResponse(openRTBResponse, prebidRequest); + expect(prebidResponses).to.not.be.empty; + }); + }); +}); diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index 442039504f8..ee3624b5b16 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -22,7 +22,11 @@ describe('invibesBidAdapter:', function () { [400, 300], [125, 125] ], - transactionId: 't1' + transactionId: 't1', + userId: { + pubcid: 'pub-cid-code', + pubProvidedId: 'pub-provided-id' + } }, { bidId: 'b2', bidder: BIDDER_CODE, @@ -284,6 +288,45 @@ describe('invibesBidAdapter:', function () { expect(request.data.purposes.split(',')[9]).to.equal('true'); }); + it('should send legitimateInterests 2 & 7', function () { + let bidderRequest = { + gdprConsent: { + vendorData: { + gdprApplies: true, + hasGlobalConsent: false, + vendor: {consents: {436: true}}, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true + }, + legitimateInterests: { + 1: true, + 2: true, + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true + } + } + } + } + }; + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.li.split(',')[1] && request.data.li.split(',')[6]).to.equal('true'); + }); it('should send oi = 0 when vendorData is null', function () { let bidderRequest = { gdprConsent: { @@ -321,7 +364,6 @@ describe('invibesBidAdapter:', function () { let request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.oi).to.equal(2); }); - it('should send oi = 0 when vendor consents for invibes are false on tcf v2', function () { let bidderRequest = { gdprConsent: { @@ -349,6 +391,74 @@ describe('invibesBidAdapter:', function () { let request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.oi).to.equal(0); }); + it('should send oi = 2 when vendor consent for invibes are false and vendor legitimate interest for invibes are true on tcf v2', function () { + let bidderRequest = { + gdprConsent: { + vendorData: { + gdprApplies: true, + hasGlobalConsent: false, + vendor: {consents: {436: false}, legitimateInterests: {436: true}}, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true + } + } + } + } + }; + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.oi).to.equal(2); + }); + it('should send oi = 0 when vendor consents and legitimate interests for invibes are false on tcf v2', function () { + let bidderRequest = { + gdprConsent: { + vendorData: { + gdprApplies: true, + hasGlobalConsent: false, + vendor: {consents: {436: false}, legitimateInterests: {436: false}}, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true + } + } + } + } + }; + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.oi).to.equal(0); + }); + it('should send oi = 0 when purpose consents is null', function () { + let bidderRequest = { + gdprConsent: { + vendorData: { + gdprApplies: true, + hasGlobalConsent: false, + vendor: {consents: {436: false}}, + purpose: {} + } + } + }; + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.oi).to.equal(0); + }); it('should send oi = 2 when purpose consents weren\'t approved on tcf v2', function () { let bidderRequest = { diff --git a/test/spec/modules/ipromBidAdapter_spec.js b/test/spec/modules/ipromBidAdapter_spec.js new file mode 100644 index 00000000000..535cc332b21 --- /dev/null +++ b/test/spec/modules/ipromBidAdapter_spec.js @@ -0,0 +1,193 @@ +import {expect} from 'chai'; +import {spec} from 'modules/ipromBidAdapter.js'; + +describe('iPROM Adapter', function () { + let bidRequests; + let bidderRequest; + + beforeEach(function () { + bidRequests = [ + { + bidder: 'iprom', + params: { + id: '1234', + dimension: '300x250', + }, + adUnitCode: '/19966331/header-bid-tag-1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bidId: '29a72b151f7bd3', + auctionId: 'e36abb27-g3b1-1ad6-8a4c-701c8919d3hh', + bidderRequestId: '2z76da40m1b3cb8', + transactionId: 'j51lhf58-1ad6-g3b1-3j6s-912c9493g0gu' + } + ]; + + bidderRequest = { + timeout: 3000, + refererInfo: { + referer: 'https://adserver.si/index.html', + reachedTop: true, + numIframes: 1, + stack: [ + 'https://adserver.si/index.html', + 'https://adserver.si/iframe1.html', + ] + } + } + }); + + describe('validating bids', function () { + it('should accept valid bid', function () { + let validBid = { + bidder: 'iprom', + params: { + id: '1234', + dimension: '300x250', + }, + }; + + const isValid = spec.isBidRequestValid(validBid); + + expect(isValid).to.equal(true); + }); + + it('should reject bid if missing dimension and id', function () { + let invalidBid = { + bidder: 'iprom', + params: {} + }; + + const isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + + it('should reject bid if missing dimension', function () { + let invalidBid = { + bidder: 'iprom', + params: { + id: '1234', + } + }; + + const isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + + it('should reject bid if dimension is not a string', function () { + let invalidBid = { + bidder: 'iprom', + params: { + id: '1234', + dimension: 404, + } + }; + + const isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + + it('should reject bid if missing id', function () { + let invalidBid = { + bidder: 'iprom', + params: { + dimension: '300x250', + } + }; + + const isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + + it('should reject bid if id is not a string', function () { + let invalidBid = { + bidder: 'iprom', + params: { + id: 1234, + dimension: '300x250', + } + }; + + const isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + }); + + describe('building requests', function () { + it('should go to correct endpoint', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.method).to.exist; + expect(request.method).to.equal('POST'); + expect(request.url).to.exist; + expect(request.url).to.equal('https://core.iprom.net/programmatic'); + }); + + it('should add referer info', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const requestparse = JSON.parse(request.data); + + expect(requestparse.referer).to.exist; + expect(requestparse.referer.referer).to.equal('https://adserver.si/index.html'); + }); + + it('should add adapter version', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const requestparse = JSON.parse(request.data); + + expect(requestparse.version).to.exist; + }); + + it('should contain id and dimension', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const requestparse = JSON.parse(request.data); + + expect(requestparse.bids[0].params.id).to.equal('1234'); + expect(requestparse.bids[0].params.dimension).to.equal('300x250'); + }); + }); + + describe('handling responses', function () { + it('should return complete bid response', function () { + const serverResponse = { + body: [{ + requestId: '29a72b151f7bd3', + cpm: 0.5, + width: '300', + height: '250', + creativeId: 1234, + ad: 'Iprom Header bidding example' + } + ]}; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].requestId).to.equal('29a72b151f7bd3'); + expect(bids[0].cpm).to.equal(0.5); + expect(bids[0].width).to.equal('300'); + expect(bids[0].height).to.equal('250'); + expect(bids[0].ad).to.have.length.above(1); + }); + + it('should return empty bid response', function () { + const emptyServerResponse = { + body: [] + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const bids = spec.interpretResponse(emptyServerResponse, request); + + expect(bids).to.be.lengthOf(0); + }); + }); +}); diff --git a/test/spec/modules/ironsourceBidAdapter_spec.js b/test/spec/modules/ironsourceBidAdapter_spec.js index 93c3a6fb7b9..cca928ff28b 100644 --- a/test/spec/modules/ironsourceBidAdapter_spec.js +++ b/test/spec/modules/ironsourceBidAdapter_spec.js @@ -75,6 +75,8 @@ describe('ironsourceAdapter', function () { bidderCode: 'ironsource', } + const customSessionId = '12345678'; + it('sends bid request to ENDPOINT via GET', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { @@ -98,6 +100,22 @@ describe('ironsourceAdapter', function () { } }); + it('sends the is_wrapper query param', function () { + bidRequests[0].params.isWrapper = true; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.is_wrapper).to.equal(true); + } + }); + + it('sends the custom session id as a query param', function () { + bidRequests[0].params.sessionId = customSessionId; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.session_id).to.equal(customSessionId); + } + }); + it('should send the correct width and height', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index edbcc5725ac..6b7f35e7fd4 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -3,6 +3,7 @@ import { config } from 'src/config.js'; import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec } from 'modules/ixBidAdapter.js'; +import { createEidsArray } from 'modules/userId/eids.js'; describe('IndexexchangeAdapter', function () { const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/cygnus'; @@ -25,7 +26,7 @@ describe('IndexexchangeAdapter', function () { } ] }; - var div_many_sizes = [ + const div_many_sizes = [ [300, 250], [600, 410], [336, 280], @@ -93,6 +94,62 @@ describe('IndexexchangeAdapter', function () { [600, 40], [600, 30] ]; + + const ONE_VIDEO = [ + { + bidder: 'ix', + params: { + siteId: '456', + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [2] + }, + size: [400, 100] + }, + sizes: [[400, 100]], + mediaTypes: { + video: { + context: 'instream', + playerSize: [[400, 100]] + } + }, + adUnitCode: 'div-gpt-ad-1460505748562-0', + transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', + bidId: '1a2b3c4e', + bidderRequestId: '11a22b33c44e', + auctionId: '1aa2bb3cc4de', + schain: SAMPLE_SCHAIN + } + ]; + + const ONE_BANNER = [ + { + bidder: 'ix', + params: { + siteId: '123', + size: [300, 250] + }, + sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229', + bidId: '1a2b3c4d', + bidderRequestId: '11a22b33c44d', + auctionId: '1aa2bb3cc4dd', + schain: SAMPLE_SCHAIN + } + ]; + const DEFAULT_BANNER_VALID_BID = [ { bidder: 'ix', @@ -351,8 +408,13 @@ describe('IndexexchangeAdapter', function () { const DEFAULT_USERID_DATA = { idl_env: '1234-5678-9012-3456', // Liveramp + netId: 'testnetid123', // NetId + IDP: 'userIDP000', // IDP + fabrickId: 'fabrickId9000', // FabrickId }; + const DEFAULT_USERIDASEIDS_DATA = createEidsArray(DEFAULT_USERID_DATA); + const DEFAULT_USERID_PAYLOAD = [ { source: 'liveramp.com', @@ -362,6 +424,30 @@ describe('IndexexchangeAdapter', function () { rtiPartner: 'idl' } }] + }, { + source: 'netid.de', + uids: [{ + id: DEFAULT_USERID_DATA.netId, + ext: { + rtiPartner: 'NETID' + } + }] + }, { + source: 'neustar.biz', + uids: [{ + id: DEFAULT_USERID_DATA.fabrickId, + ext: { + rtiPartner: 'fabrickId' + } + }] + }, { + source: 'zeotap.com', + uids: [{ + id: DEFAULT_USERID_DATA.IDP, + ext: { + rtiPartner: 'zeotapIdPlus' + } + }] } ]; @@ -761,14 +847,18 @@ describe('IndexexchangeAdapter', function () { delete window.headertag; }); - it('IX adapter reads LiveRamp IDL envelope from Prebid and adds it to Video', function () { + it('IX adapter reads supported user modules from Prebid and adds it to Video', function () { const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); - cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA); + // cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = JSON.parse(request.data.r); - expect(payload.user.eids).to.have.lengthOf(1); + expect(payload.user.eids).to.have.lengthOf(4); expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); }); it('We continue to send in IXL identity info and Prebid takes precedence over IXL', function () { @@ -822,11 +912,45 @@ describe('IndexexchangeAdapter', function () { } } ] + }, + NetIdIp: { + source: 'netid.de', + uids: [ + { + id: 'testnetid', + ext: { + rtiPartner: 'NETID' + } + } + ] + }, + NeustarIp: { + source: 'neustar.biz', + uids: [ + { + id: 'testfabrick', + ext: { + rtiPartner: 'fabrickId' + } + } + ] + }, + ZeotapIp: { + source: 'zeotap.com', + uids: [ + { + id: 'testzeotap', + ext: { + rtiPartner: 'zeotapIdPlus' + } + } + ] } }; const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); - cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA) + // cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = JSON.parse(request.data.r); @@ -867,10 +991,14 @@ describe('IndexexchangeAdapter', function () { }) expect(payload.user).to.exist; - expect(payload.user.eids).to.have.lengthOf(3); + expect(payload.user.eids).to.have.lengthOf(6); + expect(payload.user.eids).to.deep.include(validUserIdPayload[0]); expect(payload.user.eids).to.deep.include(validUserIdPayload[1]); expect(payload.user.eids).to.deep.include(validUserIdPayload[2]); + expect(payload.user.eids).to.deep.include(validUserIdPayload[3]); + expect(payload.user.eids).to.deep.include(validUserIdPayload[4]); + expect(payload.user.eids).to.deep.include(validUserIdPayload[5]); }); it('IXL and Prebid are mutually exclusive', function () { @@ -892,7 +1020,8 @@ describe('IndexexchangeAdapter', function () { }; const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); - cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA); + // cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; @@ -910,9 +1039,12 @@ describe('IndexexchangeAdapter', function () { }); const payload = JSON.parse(request.data.r); - expect(payload.user.eids).to.have.lengthOf(2); + expect(payload.user.eids).to.have.lengthOf(5); expect(payload.user.eids).to.deep.include(validUserIdPayload[0]); expect(payload.user.eids).to.deep.include(validUserIdPayload[1]); + expect(payload.user.eids).to.deep.include(validUserIdPayload[2]); + expect(payload.user.eids).to.deep.include(validUserIdPayload[3]); + expect(payload.user.eids).to.deep.include(validUserIdPayload[4]); }); }); @@ -981,6 +1113,84 @@ describe('IndexexchangeAdapter', function () { expect(impression.ext.sid).to.equal(sidValue); }); + it('video impression has #priceFloors floors', function () { + const bid = utils.deepClone(ONE_VIDEO[0]); + const flr = 5.5 + const floorInfo = {floor: flr, currency: 'USD'}; + bid.getFloor = function () { + return floorInfo; + } + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(flr); + expect(imp1.bidfloorcur).to.equal('USD'); + expect(imp1.ext.fl).to.equal('p'); + }); + + it('banner imp has floors from #priceFloors module', function () { + const floor300x250 = 3.25 + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]) + + const floorInfo = { floor: floor300x250, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + + expect(imp1.bidfloorcur).to.equal('USD'); + expect(imp1.bidfloor).to.equal(floor300x250); + expect(imp1.ext.fl).to.equal('p'); + }); + + it('ix adapter floors chosen over #priceFloors ', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + + const floorhi = 4.5 + const floorlow = 3.5 + + bid.params.bidFloor = floorhi + bid.params.bidFloorCur = 'USD' + + const floorInfo = { floor: floorlow, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(floorhi); + expect(imp1.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(imp1.ext.fl).to.equal('x'); + }); + + it(' #priceFloors floors chosen over ix adapter floors', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + + const floorhi = 4.5 + const floorlow = 3.5 + + bid.params.bidFloor = floorlow + bid.params.bidFloorCur = 'USD' + + const floorInfo = { floor: floorhi, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(floorhi); + expect(imp1.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(imp1.ext.fl).to.equal('p'); + }); + it('impression should have bidFloor and bidFloorCur if configured', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; @@ -990,6 +1200,73 @@ describe('IndexexchangeAdapter', function () { expect(impression.bidfloor).to.equal(bid.params.bidFloor); expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(impression.ext.fl).to.equal('x'); + }); + + it('missing sizes #priceFloors ', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + bid.mediaTypes.banner.sizes.push([500, 400]) + + const floorInfo = { floor: 3.25, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + sinon.spy(bid, 'getFloor'); + + const requestBidFloor = spec.buildRequests([bid])[0]; + // called getFloor with 300 x 250 + expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); + expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); + + // called getFloor with 500 x 400 + expect(bid.getFloor.getCall(1).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(1).args[0].size[0]).to.equal(500); + expect(bid.getFloor.getCall(1).args[0].size[1]).to.equal(400); + + // both will have same floors due to mock getFloor + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(3.25); + expect(imp1.bidfloorcur).to.equal('USD'); + + const imp2 = JSON.parse(requestBidFloor.data.r).imp[1]; + expect(imp2.bidfloor).to.equal(3.25); + expect(imp2.bidfloorcur).to.equal('USD'); + }); + + it('#pricefloors inAdUnit, banner impressions should have floors', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + + const flr = 4.3 + bid.floors = { + currency: 'USD', + schema: { + delimiter: '|', + fields: ['mediaType', 'size'] + }, + values: { + 'banner|300x250': flr, + 'banner|600x500': 6.5, + 'banner|*': 7.5 + } + }; + const floorInfo = { floor: flr, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + sinon.spy(bid, 'getFloor'); + + const requestBidFloor = spec.buildRequests([bid])[0]; + // called getFloor with 300 x 250 + expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); + expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); + + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(flr); + expect(imp1.bidfloorcur).to.equal('USD'); }); it('payload without mediaType should have correct format and value', function () { @@ -1069,7 +1346,6 @@ describe('IndexexchangeAdapter', function () { const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithFirstPartyData.data.r).site.page; const expectedPageUrl = DEFAULT_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; - expect(pageUrl).to.equal(expectedPageUrl); }); @@ -1416,7 +1692,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.mimes[0]).to.not.equal('video/override'); }); - it('should not add video adunit level properties in imp object if they are not whitelisted', function () { + it('should not add video adunit level properties in imp object if they are not allowlisted', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes.video.context = 'outstream'; bid.mediaTypes.video.random = true; @@ -1426,7 +1702,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.random).to.not.exist; }); - it('should add whitelisted adunit level video properties in imp object if they are not configured at params level', function () { + it('should add allowlisted adunit level video properties in imp object if they are not configured at params level', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes.video.context = 'outstream'; delete bid.params.video.protocols; @@ -1495,7 +1771,8 @@ describe('IndexexchangeAdapter', function () { expect(diagObj.ou).to.equal(1); expect(diagObj.ren).to.equal(false); expect(diagObj.mfu).to.equal(1); - expect(diagObj.allU).to.equal(1); + expect(diagObj.allu).to.equal(1); + expect(diagObj.version).to.equal('$prebid.version$'); }); }); }); @@ -1772,5 +2049,32 @@ describe('IndexexchangeAdapter', function () { expect(requestWithConsent.regs.ext.gdpr).to.equal(1); expect(requestWithConsent.regs.ext.us_privacy).to.equal('1YYN'); }); + + it('should contain `consented_providers_settings.consented_providers` & consent on user.ext when both are provided', function () { + const options = { + gdprConsent: { + consentString: '3huaa11=qu3198ae', + addtlConsent: '1~1.35.41.101', + } + }; + + const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); + expect(requestWithConsent.user.ext.consented_providers_settings.consented_providers).to.equal('1~1.35.41.101'); + expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); + }); + + it('should not contain `consented_providers_settings.consented_providers` on user.ext when consent is not provided', function () { + const options = { + gdprConsent: { + addtlConsent: '1~1.35.41.101', + } + }; + + const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); + expect(utils.deepAccess(requestWithConsent, 'user.ext.consented_providers_settings')).to.not.exist; + expect(utils.deepAccess(requestWithConsent, 'user.ext.consent')).to.not.exist; + }); }); }); diff --git a/test/spec/modules/jwplayerRtdProvider_spec.js b/test/spec/modules/jwplayerRtdProvider_spec.js index f7beb6ba486..458e45e8ae7 100644 --- a/test/spec/modules/jwplayerRtdProvider_spec.js +++ b/test/spec/modules/jwplayerRtdProvider_spec.js @@ -229,8 +229,8 @@ describe('jwplayerRtdProvider', function() { const bid = {}; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: mediaIdWithSegment, @@ -298,8 +298,8 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForSuccess @@ -345,8 +345,8 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForSuccess @@ -392,8 +392,8 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForFailure @@ -435,19 +435,19 @@ describe('jwplayerRtdProvider', function() { }); it('should include banner ad units that specify jwTargeting', function() { - const adUnit = { mediaTypes: { banner: {} }, fpd: { context: { data: { jwTargeting: {} } } } }; + const adUnit = { mediaTypes: { banner: {} }, ortb2Imp: { ext: { data: { jwTargeting: {} } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.deep.equal(config); }); it('should include outstream ad units that specify jwTargeting', function() { - const adUnit = { mediaTypes: { video: { context: 'outstream' } }, fpd: { context: { data: { jwTargeting: {} } } } }; + const adUnit = { mediaTypes: { video: { context: 'outstream' } }, ortb2Imp: { ext: { data: { jwTargeting: {} } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.deep.equal(config); }); it('should fallback to config when empty jwTargeting is defined in ad unit', function () { - const adUnit = { fpd: { context: { data: { jwTargeting: {} } } } }; + const adUnit = { ortb2Imp: { ext: { data: { jwTargeting: {} } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.deep.equal(config); }); @@ -457,7 +457,7 @@ describe('jwplayerRtdProvider', function() { const expectedPlayerID = 'test_player_id'; const config = { playerID: 'bad_id', mediaID: 'bad_id' }; - const adUnit = { fpd: { context: { data: { jwTargeting: { mediaID: expectedMediaID, playerID: expectedPlayerID } } } } }; + const adUnit = { ortb2Imp: { ext: { data: { jwTargeting: { mediaID: expectedMediaID, playerID: expectedPlayerID } } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.have.property('mediaID', expectedMediaID); expect(targeting).to.have.property('playerID', expectedPlayerID); @@ -468,7 +468,7 @@ describe('jwplayerRtdProvider', function() { const expectedPlayerID = 'test_player_id'; const config = { playerID: expectedPlayerID, mediaID: 'bad_id' }; - const adUnit = { fpd: { context: { data: { jwTargeting: { mediaID: expectedMediaID } } } } }; + const adUnit = { ortb2Imp: { ext: { data: { jwTargeting: { mediaID: expectedMediaID } } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.have.property('mediaID', expectedMediaID); expect(targeting).to.have.property('playerID', expectedPlayerID); @@ -577,8 +577,8 @@ describe('jwplayerRtdProvider', function() { bidReqConfig = { adUnits: [ { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: validMediaIDs[0] @@ -591,8 +591,8 @@ describe('jwplayerRtdProvider', function() { ] }, { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: validMediaIDs[1] @@ -658,8 +658,8 @@ describe('jwplayerRtdProvider', function() { it('sets targeting data in proper structure', function () { const bid = {}; const adUnitWithMediaId = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForSuccess @@ -690,8 +690,8 @@ describe('jwplayerRtdProvider', function() { const adUnitCode = 'test_ad_unit'; const bid = {}; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForFailure @@ -701,7 +701,7 @@ describe('jwplayerRtdProvider', function() { }, bids: [ bid ] }; - const expectedContentId = 'jw_' + adUnit.fpd.context.data.jwTargeting.mediaID; + const expectedContentId = 'jw_' + adUnit.ortb2Imp.ext.data.jwTargeting.mediaID; const expectedTargeting = { content: { id: expectedContentId @@ -732,8 +732,8 @@ describe('jwplayerRtdProvider', function() { const adUnitEmptyfpd = { code: 'test_ad_unit_empty_fpd', - fpd: { - context: { + ortb2Imp: { + ext: { id: 'sthg' } }, diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 9dbcca8e331..43968bbef5a 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -134,6 +134,21 @@ describe('kargo adapter tests', function () { noAdServerCurrency = true; } + function generateGDPR(applies, haveConsent) { + var data = { + consentString: 'gdprconsentstring', + gdprApplies: applies, + }; + return data; + } + + function generateGDPRExpect(applies, haveConsent) { + return { + consent: 'gdprconsentstring', + applies: applies, + }; + } + function initializeKruxUser() { setLocalStorageItem('kxkar_user', 'rsgr9pnij'); } @@ -221,7 +236,7 @@ describe('kargo adapter tests', function () { return spec._getSessionId(); } - function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB, expectedRawCRBCookie) { + function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB, expectedRawCRBCookie, expectedGDPR) { var base = { timeout: 200, requestCount: requestCount++, @@ -299,6 +314,10 @@ describe('kargo adapter tests', function () { rawCRBLocalStorage: expectedRawCRB }; + if (expectedGDPR) { + base.userIDs['gdpr'] = expectedGDPR; + } + if (excludeUserIds === true) { base.userIDs = { crbIDs: {}, @@ -317,12 +336,16 @@ describe('kargo adapter tests', function () { return base; } - function testBuildRequests(excludeTdid, expected) { + function testBuildRequests(excludeTdid, expected, gdpr) { var clonedBids = JSON.parse(JSON.stringify(bids)); if (excludeTdid) { delete clonedBids[0].userId.tdid; } - var request = spec.buildRequests(clonedBids, {timeout: 200, uspConsent: '1---', foo: 'bar'}); + var payload = { timeout: 200, uspConsent: '1---', foo: 'bar' }; + if (gdpr) { + payload['gdprConsent'] = gdpr + } + var request = spec.buildRequests(clonedBids, payload); expected.sessionId = getSessionId(); sessionIds.push(expected.sessionId); var krakenParams = JSON.parse(decodeURIComponent(request.data.slice(5))); @@ -431,6 +454,15 @@ describe('kargo adapter tests', function () { initializeKrgCrb(); testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle())); }); + + it('sends gdpr consent', function () { + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgCrb(); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle(), generateGDPRExpect(true, true)), generateGDPR(true, true)); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle(), generateGDPRExpect(false, true)), generateGDPR(false, true)); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle(), generateGDPRExpect(false, false)), generateGDPR(false, false)); + }); }); describe('response handler', function() { @@ -558,8 +590,8 @@ describe('kargo adapter tests', function () { }); }); - function getUserSyncsWhenAllowed() { - return spec.getUserSyncs({iframeEnabled: true}); + function getUserSyncsWhenAllowed(gdprConsent, usPrivacy) { + return spec.getUserSyncs({iframeEnabled: true}, null, gdprConsent, usPrivacy); } function getUserSyncsWhenForbidden() { @@ -574,17 +606,17 @@ describe('kargo adapter tests', function () { shouldSimulateOutdatedBrowser = true; } - function getSyncUrl(index) { + function getSyncUrl(index, gdprApplies, gdprConsentString, usPrivacy) { return { type: 'iframe', - url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}` + url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}&gdpr=${gdprApplies}&gdpr_consent=${gdprConsentString}&us_privacy=${usPrivacy}` }; } - function getSyncUrls() { + function getSyncUrls(gdprApplies, gdprConsentString, usPrivacy) { var syncs = []; for (var i = 0; i < 5; i++) { - syncs[i] = getSyncUrl(i); + syncs[i] = getSyncUrl(i, gdprApplies || 0, gdprConsentString || '', usPrivacy || ''); } return syncs; } @@ -606,6 +638,21 @@ describe('kargo adapter tests', function () { safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); }); + it('no user syncs when there is no us privacy consent', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed(null, '1YYY')).to.be.an('array').that.is.empty); + }); + + it('pass through us privacy consent', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed(null, '1YNY')).to.deep.equal(getSyncUrls(0, '', '1YNY'))); + }); + + it('pass through gdpr consent', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed({ gdprApplies: true, consentString: 'consentstring' })).to.deep.equal(getSyncUrls(1, 'consentstring', ''))); + }); + it('no user syncs when there is outdated browser', function() { turnOnClientId(); simulateOutdatedBrowser(); diff --git a/test/spec/modules/lemmaBidAdapter_spec.js b/test/spec/modules/lemmaBidAdapter_spec.js index a00c25d126c..a22f5650e50 100644 --- a/test/spec/modules/lemmaBidAdapter_spec.js +++ b/test/spec/modules/lemmaBidAdapter_spec.js @@ -346,24 +346,6 @@ describe('lemmaBidAdapter', function() { type: 'iframe', url: syncurl_iframe }]); }); - - it('CCPA/USP', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&us_privacy=1NYN` - }]); - }); - - it('GDPR', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=foo` - }]); - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' }, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=0&gdpr_consent=foo` - }]); - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: undefined }, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=` - }]); - }); }); }); }); diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 3b4e4b9d9a7..f1de2f3bf93 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -36,7 +36,7 @@ describe('LiveIntentId', function() { resetLiveIntentIdSubmodule(); }); - it('should initialize LiveConnect with a privacy string when getId, and include it in the resolution request', function() { + it('should initialize LiveConnect with a privacy string when getId, and include it in the resolution request', function () { uspConsentDataStub.returns('1YNY'); gdprConsentDataStub.returns({ gdprApplies: true, @@ -47,12 +47,16 @@ describe('LiveIntentId', function() { submoduleCallback(callBackSpy); let request = server.requests[1]; expect(request.url).to.match(/.*us_privacy=1YNY.*&gdpr=1&gdpr_consent=consentDataString.*/); + const response = { + unifiedId: 'a_unified_id', + segments: [123, 234] + } request.respond( 200, responseHeader, - JSON.stringify({}) + JSON.stringify(response) ); - expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.calledOnceWith(response)).to.be.true; }); it('should fire an event when getId', function() { @@ -131,11 +135,10 @@ describe('LiveIntentId', function() { let request = server.requests[1]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899'); request.respond( - 200, - responseHeader, - JSON.stringify({}) + 204, + responseHeader ); - expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.calledOnceWith({})).to.be.true; }); it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() { diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index 7983e8fbb0b..fa00a359191 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -781,61 +781,45 @@ describe('Livewrapped adapter tests', function () { }); }); - it('should make use of Id5-Id if available', function() { + it('should make use of user ids if available', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; - testbidRequest.bids[0].userId = {}; - testbidRequest.bids[0].userId.id5id = { uid: 'id5-user-id' }; - let result = spec.buildRequests(testbidRequest.bids, testbidRequest); - let data = JSON.parse(result.data); - - expect(data.rtbData.user.ext.eids).to.deep.equal([{ - 'source': 'id5-sync.com', - 'uids': [{ - 'id': 'id5-user-id', - 'atype': 1 - }] - }]); - }); - - it('should make use of publisher common Id if available', function() { - sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); - let testbidRequest = clone(bidderRequest); - delete testbidRequest.bids[0].params.userId; - testbidRequest.bids[0].userId = {}; - testbidRequest.bids[0].userId.pubcid = 'publisher-common-id'; - let result = spec.buildRequests(testbidRequest.bids, testbidRequest); - let data = JSON.parse(result.data); - - expect(data.rtbData.user.ext.eids).to.deep.equal([{ - 'source': 'pubcid.org', - 'uids': [{ - 'id': 'publisher-common-id', - 'atype': 1 - }] - }]); - }); + testbidRequest.bids[0].userIdAsEids = [ + { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-id', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + }] + }, + { + 'source': 'pubcid.org', + 'uids': [{ + 'id': 'publisher-common-id', + 'atype': 1 + }] + }, + { + 'source': 'sharedid.org', + 'uids': [{ + 'id': 'sharedid', + 'atype': 1, + 'ext': { + 'third': 'sharedid' + } + }] + } + ]; - it('should make use of criteoId if available', function() { - sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); - let testbidRequest = clone(bidderRequest); - delete testbidRequest.bids[0].params.userId; - testbidRequest.bids[0].userId = {}; - testbidRequest.bids[0].userId.criteoId = 'criteo-id'; let result = spec.buildRequests(testbidRequest.bids, testbidRequest); let data = JSON.parse(result.data); - expect(data.rtbData.user.ext.eids).to.deep.equal([{ - 'source': 'criteo.com', - 'uids': [{ - 'id': 'criteo-id', - 'atype': 1 - }] - }]); + expect(data.rtbData.user.ext.eids).to.deep.equal(testbidRequest.bids[0].userIdAsEids); }); it('should send schain object if available', function() { diff --git a/test/spec/modules/loganBidAdapter_spec.js b/test/spec/modules/loganBidAdapter_spec.js new file mode 100644 index 00000000000..96029294df0 --- /dev/null +++ b/test/spec/modules/loganBidAdapter_spec.js @@ -0,0 +1,329 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/loganBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; + +describe('LoganBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'logan', + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 783, + traffic: BANNER + } + }; + + const bidderRequest = { + refererInfo: { + referer: 'test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + delete bid.params.placementId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid], bidderRequest); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://USeast2.logan.ai/?c=o&m=multi'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'schain'); + expect(placement.placementId).to.equal(783); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + expect(placement.sizes).to.be.an('array'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + it('Returns valid data for mediatype native', function () { + const native = { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + }; + + bid.mediaTypes = {}; + bid.params.traffic = NATIVE; + bid.mediaTypes[NATIVE] = native; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'native', 'schain'); + expect(placement.traffic).to.equal(NATIVE); + expect(placement.native).to.equal(native); + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + bidderRequest.gdprConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.width).to.equal(300); + expect(dataItem.height).to.equal(250); + expect(dataItem.ad).to.equal('Test'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('iframe') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://ssp-cookie.logan.ai/html?src=pbjs&gdpr=1&gdpr_consent=ALL') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1NNN' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('iframe') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://ssp-cookie.logan.ai/html?src=pbjs&ccpa_consent=1NNN') + }); + }); +}); diff --git a/test/spec/modules/marsmediaBidAdapter_spec.js b/test/spec/modules/marsmediaBidAdapter_spec.js index b4c2fe68f34..cf074b0f3d6 100644 --- a/test/spec/modules/marsmediaBidAdapter_spec.js +++ b/test/spec/modules/marsmediaBidAdapter_spec.js @@ -1,11 +1,41 @@ -import {spec} from '../../../modules/marsmediaBidAdapter.js'; -import * as utils from '../../../src/utils.js'; -import * as sinon from 'sinon'; +import { spec } from 'modules/marsmediaBidAdapter.js'; +import * as utils from 'src/utils.js'; +import { config } from 'src/config.js'; -var r1adapter = spec; +var marsAdapter = spec; describe('marsmedia adapter tests', function () { + let element, win; + let sandbox; + beforeEach(function() { + element = { + x: 0, + y: 0, + + width: 0, + height: 0, + + getBoundingClientRect: () => { + return { + width: element.width, + height: element.height, + + left: element.x, + top: element.y, + right: element.x + element.width, + bottom: element.y + element.height + }; + } + }; + win = { + document: { + visibilityState: 'visible' + }, + + innerWidth: 800, + innerHeight: 600 + }; this.defaultBidderRequest = { 'refererInfo': { 'referer': 'Reference Page', @@ -15,28 +45,40 @@ describe('marsmedia adapter tests', function () { ] } }; + + this.defaultBidRequestList = [ + { + 'bidder': 'marsmedia', + 'params': { + 'zoneId': 9999 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'Unit-Code', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + sandbox = sinon.sandbox.create(); + sandbox.stub(document, 'getElementById').withArgs('Unit-Code').returns(element); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns(win); + }); + + afterEach(function() { + sandbox.restore(); }); describe('Verify 1.0 POST Banner Bid Request', function () { it('buildRequests works', function () { - var bidRequestList = [ - { - 'bidder': 'marsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); expect(bidRequest.url).to.have.string('https://hb.go2speed.media/bidder/?bid=3mhdom&zoneId=9999&hbv='); expect(bidRequest.method).to.equal('POST'); @@ -52,11 +94,11 @@ describe('marsmedia adapter tests', function () { expect(openrtbRequest.imp[0].ext.bidder.zoneId).to.equal(9999); }); - it('interpretResponse works', function() { + /* it('interpretResponse works', function() { var bidList = { 'body': [ { - 'impid': 'div-gpt-ad-1438287399331-0', + 'impid': 'Unit-Code', 'w': 300, 'h': 250, 'adm': '
My Compelling Ad
', @@ -67,7 +109,7 @@ describe('marsmedia adapter tests', function () { ] }; - var bannerBids = r1adapter.interpretResponse(bidList); + var bannerBids = marsAdapter.interpretResponse(bidList); expect(bannerBids.length).to.equal(1); const bid = bannerBids[0]; @@ -78,7 +120,7 @@ describe('marsmedia adapter tests', function () { expect(bid.netRevenue).to.equal(true); expect(bid.cpm).to.equal(1.0); expect(bid.ttl).to.equal(350); - }); + }); */ }); describe('Verify POST Video Bid Request', function() { @@ -95,7 +137,7 @@ describe('marsmedia adapter tests', function () { 'context': 'instream' } }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'adUnitCode': 'Unit-Code', 'sizes': [ [300, 250] ], @@ -107,7 +149,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); expect(bidRequest.url).to.have.string('https://hb.go2speed.media/bidder/?bid=3mhdom&zoneId=9999&hbv='); expect(bidRequest.method).to.equal('POST'); @@ -132,7 +174,7 @@ describe('marsmedia adapter tests', function () { var bidList = { 'body': [ { - 'impid': 'div-gpt-ad-1438287399331-1', + 'impid': 'Unit-Code', 'price': 1, 'adm': 'https://example.com/', 'adomain': [ @@ -147,7 +189,7 @@ describe('marsmedia adapter tests', function () { ] }; - var videoBids = r1adapter.interpretResponse(bidList); + var videoBids = marsAdapter.interpretResponse(bidList); expect(videoBids.length).to.equal(1); const bid = videoBids[0]; @@ -166,7 +208,7 @@ describe('marsmedia adapter tests', function () { var bidList = { 'body': [ { - 'impid': 'div-gpt-ad-1438287399331-1', + 'impid': 'Unit-Code', 'price': 1, 'adm': '', 'adomain': [ @@ -181,7 +223,7 @@ describe('marsmedia adapter tests', function () { ] }; - var videoBids = r1adapter.interpretResponse(bidList); + var videoBids = marsAdapter.interpretResponse(bidList); expect(videoBids.length).to.equal(1); const bid = videoBids[0]; @@ -199,26 +241,6 @@ describe('marsmedia adapter tests', function () { describe('misc buildRequests', function() { it('should send GDPR Consent data to Marsmedia tag', function () { - var bidRequestList = [ - { - 'bidder': 'marsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250]] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-3', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - var consentString = 'testConsentString'; var gdprBidderRequest = this.defaultBidderRequest; gdprBidderRequest.gdprConsent = { @@ -226,13 +248,50 @@ describe('marsmedia adapter tests', function () { 'consentString': consentString }; - var bidRequest = r1adapter.buildRequests(bidRequestList, gdprBidderRequest); + var bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, gdprBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.user.ext.consent).to.equal(consentString); expect(openrtbRequest.regs.ext.gdpr).to.equal(true); }); + it('should have CCPA Consent if defined', function () { + const ccpaBidderRequest = this.defaultBidderRequest; + ccpaBidderRequest.uspConsent = '1YYN'; + const bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, ccpaBidderRequest); + const openrtbRequest = JSON.parse(bidRequest.data); + + expect(openrtbRequest.regs.ext.us_privacy).to.equal('1YYN'); + }); + + it('should submit coppa if set in config', function () { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); + const requestparse = JSON.parse(request.data); + expect(requestparse.regs.coppa).to.equal(1); + config.getConfig.restore(); + }); + + it('should process floors module if available', function() { + const floorBidderRequest = this.defaultBidRequestList; + const floorInfo = { + currency: 'USD', + floor: 1.20 + }; + floorBidderRequest[0].getFloor = () => floorInfo; + const request = marsAdapter.buildRequests(floorBidderRequest, this.defaultBidderRequest); + const requestparse = JSON.parse(request.data); + expect(requestparse.imp[0].bidfloor).to.equal(1.20); + }); + + it('should have 0 bidfloor value', function() { + const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); + const requestparse = JSON.parse(request.data); + expect(requestparse.imp[0].bidfloor).to.equal(0); + }); + it('prefer 2.0 sizes', function () { var bidRequestList = [ { @@ -245,7 +304,7 @@ describe('marsmedia adapter tests', function () { 'sizes': [[300, 600]] } }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'adUnitCode': 'Unit-Code', 'sizes': [[300, 250]], 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', @@ -255,7 +314,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); @@ -274,7 +333,7 @@ describe('marsmedia adapter tests', function () { 'sizes': [[300]] } }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', 'auctionId': '18fd8b8b0bd757', @@ -283,7 +342,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); expect(bidRequest.method).to.be.undefined; }); @@ -297,7 +356,7 @@ describe('marsmedia adapter tests', function () { 'mediaTypes': { 'banner': {} }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', 'auctionId': '18fd8b8b0bd757', @@ -306,7 +365,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); expect(bidRequest.method).to.be.undefined; }); @@ -320,7 +379,7 @@ describe('marsmedia adapter tests', function () { 'mediaTypes': { 'banner': {'sizes': [['400', '500'], ['4n0', '5g0']]} }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', 'auctionId': '18fd8b8b0bd757', @@ -329,35 +388,15 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.imp[0].banner.format.length).to.equal(1); }); it('dnt is correctly set to 1', function () { - var bidRequestList = [ - { - 'bidder': 'marsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 600]] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - var dntStub = sinon.stub(utils, 'getDNT').returns(1); - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); dntStub.restore(); @@ -378,7 +417,7 @@ describe('marsmedia adapter tests', function () { 'playerSize': ['600', '300'] } }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', 'auctionId': '18fd8b8b0bd757', @@ -387,7 +426,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.imp[0].video.w).to.equal(600); @@ -407,7 +446,7 @@ describe('marsmedia adapter tests', function () { 'playerSize': ['badWidth', 'badHeight'] } }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', 'auctionId': '18fd8b8b0bd757', @@ -416,7 +455,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.imp[0].video.w).to.be.undefined; @@ -435,7 +474,7 @@ describe('marsmedia adapter tests', function () { 'context': 'instream' } }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', 'auctionId': '18fd8b8b0bd757', @@ -444,7 +483,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.imp[0].video.w).to.be.undefined; @@ -453,52 +492,74 @@ describe('marsmedia adapter tests', function () { it('should return empty site data when refererInfo is missing', function() { delete this.defaultBidderRequest.refererInfo; - var bidRequestList = [ - { - 'bidder': 'marsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.site.domain).to.equal(''); expect(openrtbRequest.site.page).to.equal(''); expect(openrtbRequest.site.ref).to.equal(''); }); + + context('when element is fully in view', function() { + it('returns 100', function() { + Object.assign(element, { width: 600, height: 400 }); + const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(request.data); + expect(openrtbRequest.imp[0].ext.viewability).to.equal(100); + }); + }); + + context('when element is out of view', function() { + it('returns 0', function() { + Object.assign(element, { x: -300, y: 0, width: 207, height: 320 }); + const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(request.data); + expect(openrtbRequest.imp[0].ext.viewability).to.equal(0); + }); + }); + + context('when element is partially in view', function() { + it('returns percentage', function() { + Object.assign(element, { width: 800, height: 800 }); + const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(request.data); + expect(openrtbRequest.imp[0].ext.viewability).to.equal(75); + }); + }); + + context('when nested iframes', function() { + it('returns \'na\'', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + utils.getWindowSelf.restore(); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns({}); + + const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(request.data); + expect(openrtbRequest.imp[0].ext.viewability).to.equal('na'); + }); + }); + + context('when tab is inactive', function() { + it('returns 0', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + win.document.visibilityState = 'hidden'; + sandbox.stub(utils, 'getWindowTop').returns(win); + + const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(request.data); + expect(openrtbRequest.imp[0].ext.viewability).to.equal(0); + }); + }); }); it('should return empty site.domain and site.page when refererInfo.stack is empty', function() { this.defaultBidderRequest.refererInfo.stack = []; - var bidRequestList = [ - { - 'bidder': 'marsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.site.domain).to.equal(''); @@ -508,24 +569,7 @@ describe('marsmedia adapter tests', function () { it('should secure correctly', function() { this.defaultBidderRequest.refererInfo.stack[0] = ['https://securesite.dvl']; - var bidRequestList = [ - { - 'bidder': 'marsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.imp[0].secure).to.equal(1); @@ -551,9 +595,12 @@ describe('marsmedia adapter tests', function () { 'params': { 'zoneId': 9999 }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', 'bidderRequestId': '418b37f85e772c', 'auctionId': '18fd8b8b0bd757', @@ -563,7 +610,7 @@ describe('marsmedia adapter tests', function () { } ]; - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + var bidRequest = marsAdapter.buildRequests(bidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.source.ext.schain).to.deep.equal(schain); @@ -571,7 +618,7 @@ describe('marsmedia adapter tests', function () { describe('misc interpretResponse', function () { it('No bid response', function() { - var noBidResponse = r1adapter.interpretResponse({ + var noBidResponse = marsAdapter.interpretResponse({ 'body': '' }); expect(noBidResponse.length).to.equal(0); @@ -589,22 +636,22 @@ describe('marsmedia adapter tests', function () { 'sizes': [[300, 250]] } }, - 'adUnitCode': 'bannerDiv' + 'adUnitCode': 'Unit-Code' }; it('should return true when required params found', function () { - expect(r1adapter.isBidRequestValid(bid)).to.equal(true); + expect(marsAdapter.isBidRequestValid(bid)).to.equal(true); }); it('should return false when placementId missing', function () { delete bid.params.zoneId; - expect(r1adapter.isBidRequestValid(bid)).to.equal(false); + expect(marsAdapter.isBidRequestValid(bid)).to.equal(false); }); }); describe('getUserSyncs', function () { it('returns an empty string', function () { - expect(r1adapter.getUserSyncs()).to.deep.equal([]); + expect(marsAdapter.getUserSyncs()).to.deep.equal([]); }); }); diff --git a/test/spec/modules/mass_spec.js b/test/spec/modules/mass_spec.js new file mode 100644 index 00000000000..d8d27348bde --- /dev/null +++ b/test/spec/modules/mass_spec.js @@ -0,0 +1,130 @@ +import { expect } from 'chai'; +import { + init, + addBidResponseHook, + addListenerOnce, + getRenderPayload, + render, + listenerAdded, + massEnabled +} from 'modules/mass'; +import { logInfo } from 'src/utils.js'; + +// mock a MASS bid: +const mockedMassBids = [ + { + bidder: 'ix', + bidId: 'mass-bid-1', + requestId: 'mass-bid-1', + bidderRequestId: 'bidder-request-id-1', + dealId: 'MASS1234', + ad: 'mass://provider/product/etc...', + meta: {} + }, + { + bidder: 'ix', + bidId: 'mass-bid-2', + requestId: 'mass-bid-2', + bidderRequestId: 'bidder-request-id-1', + dealId: '1234', + ad: 'mass://provider/product/etc...', + meta: { + mass: true + } + }, +]; + +// mock non-MASS bids: +const mockedNonMassBids = [ + { + bidder: 'ix', + bidId: 'non-mass-bid-1', + requstId: 'non-mass-bid-1', + bidderRequestId: 'bidder-request-id-1', + dealId: 'MASS1234', + ad: '', + meta: { + mass: true + } + }, + { + bidder: 'ix', + bidId: 'non-mass-bid-2', + requestId: 'non-mass-bid-2', + bidderRequestId: 'bidder-request-id-1', + dealId: '1234', + ad: 'mass://provider/product/etc...', + meta: {} + }, +]; + +// mock bidder request: +const mockedBidderRequest = { + bidderCode: 'ix', + bidderRequestId: 'bidder-request-id-1' +}; + +const noop = function() {}; + +describe('MASS Module', function() { + let bidderRequest = Object.assign({}, mockedBidderRequest); + + it('should be enabled by default', function() { + expect(massEnabled).to.equal(true); + }); + + it('can be disabled', function() { + init({enabled: false}); + expect(massEnabled).to.equal(false); + }); + + it('should only affect MASS bids', function() { + init({renderUrl: 'http://...'}); + mockedNonMassBids.forEach(function(mockedBid) { + const originalBid = Object.assign({}, mockedBid); + const bid = Object.assign({}, originalBid); + + bidderRequest.bids = [bid]; + + addBidResponseHook.call({bidderRequest}, noop, 'ad-code-id', bid); + + expect(bid).to.deep.equal(originalBid); + }); + }); + + it('should only update the ad markup field', function() { + init({renderUrl: 'http://...'}); + mockedMassBids.forEach(function(mockedBid) { + const originalBid = Object.assign({}, mockedBid); + const bid = Object.assign({}, originalBid); + + bidderRequest.bids = [bid]; + + addBidResponseHook.call({bidderRequest}, noop, 'ad-code-id', bid); + + expect(bid.ad).to.not.equal(originalBid.ad); + + delete bid.ad; + delete originalBid.ad; + + expect(bid).to.deep.equal(originalBid); + }); + }); + + it('should add a message listener', function() { + addListenerOnce(); + expect(listenerAdded).to.equal(true); + }); + + it('should get correct bid in render payload', function() { + const payload = getRenderPayload({data: {massBidId: 'mass-bid-1'}}); + expect(payload.type).to.equal('prebid'); + expect(payload.bid.bidId).to.equal('mass-bid-1'); + }); + + it('should load the bootloader on rendering', function() { + render({}); + expect(window.mass).to.be.an('object'); + expect(window.mass.bootloader.loaded).to.equal(true); + }); +}); diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js new file mode 100644 index 00000000000..026e79c6d5a --- /dev/null +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -0,0 +1,131 @@ +import { expect } from 'chai'; +import { spec, _getPlatform } from 'modules/missenaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('Missena Adapter', function () { + const adapter = newBidder(spec); + + const bidId = 'abc'; + + const bid = { + bidder: 'missena', + bidId: bidId, + sizes: [[1, 1]], + params: { + apiKey: 'PA-34745704', + }, + }; + + describe('codes', function () { + it('should return a bidder code of missena', function () { + expect(spec.code).to.equal('missena'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true if the apiKey param is present', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false if the apiKey is missing', function () { + expect( + spec.isBidRequestValid(Object.assign(bid, { params: {} })) + ).to.equal(false); + }); + + it('should return false if the apiKey is an empty string', function () { + expect( + spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })) + ).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const consentString = 'AAAAAAAAA=='; + + const bidderRequest = { + gdprConsent: { + consentString: consentString, + gdprApplies: true, + }, + refererInfo: { + referer: 'https://referer', + canonicalUrl: 'https://canonical', + }, + }; + + const requests = spec.buildRequests([bid, bid], bidderRequest); + const request = requests[0]; + const payload = JSON.parse(request.data); + + it('should return as many server requests as bidder requests', function () { + expect(requests.length).to.equal(2); + }); + + it('should have a post method', function () { + expect(request.method).to.equal('POST'); + }); + + it('should send the bidder id', function () { + expect(payload.request_id).to.equal(bidId); + }); + + it('should send referer information to the request', function () { + expect(payload.referer).to.equal('https://referer'); + expect(payload.referer_canonical).to.equal('https://canonical'); + }); + + it('should send gdpr consent information to the request', function () { + expect(payload.consent_string).to.equal(consentString); + expect(payload.consent_required).to.equal(true); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + requestId: bidId, + cpm: 0.5, + currency: 'USD', + ad: '', + }; + + const serverTimeoutResponse = { + requestId: bidId, + timeout: true, + ad: '', + }; + + const serverEmptyAdResponse = { + requestId: bidId, + cpm: 0.5, + currency: 'USD', + ad: '', + }; + + it('should return a proper bid response', function () { + const result = spec.interpretResponse({ body: serverResponse }, bid); + + expect(result.length).to.equal(1); + + expect(Object.keys(result[0])).to.have.members( + Object.keys(serverResponse) + ); + }); + + it('should return an empty response when the server answers with a timeout', function () { + const result = spec.interpretResponse( + { body: serverTimeoutResponse }, + bid + ); + expect(result).to.deep.equal([]); + }); + + it('should return an empty response when the server answers with an empty ad', function () { + const result = spec.interpretResponse( + { body: serverEmptyAdResponse }, + bid + ); + expect(result).to.deep.equal([]); + }); + }); +}); diff --git a/test/spec/modules/mwOpenLinkIdSystem_spec.js b/test/spec/modules/mwOpenLinkIdSystem_spec.js new file mode 100644 index 00000000000..fb082b8cd16 --- /dev/null +++ b/test/spec/modules/mwOpenLinkIdSystem_spec.js @@ -0,0 +1,20 @@ +import { writeCookie, mwOpenLinkIdSubModule } from 'modules/mwOpenLinkIdSystem.js'; + +const P_CONFIG_MOCK = { + params: { + accountId: '123', + partnerId: '123' + } +}; + +describe('mwOpenLinkId module', function () { + beforeEach(function() { + writeCookie(''); + }); + + it('getId() should return a MediaWallah openLink Id when the MediaWallah openLink first party cookie exists', function () { + writeCookie({eid: 'XX-YY-ZZ-123'}); + const id = mwOpenLinkIdSubModule.getId(P_CONFIG_MOCK); + expect(id).to.be.deep.equal({id: {eid: 'XX-YY-ZZ-123'}}); + }); +}); diff --git a/test/spec/modules/novatiqIdSystem_spec.js b/test/spec/modules/novatiqIdSystem_spec.js new file mode 100644 index 00000000000..60c82626450 --- /dev/null +++ b/test/spec/modules/novatiqIdSystem_spec.js @@ -0,0 +1,70 @@ +import { novatiqIdSubmodule } from 'modules/novatiqIdSystem.js'; +import * as utils from 'src/utils.js'; +import { server } from 'test/mocks/xhr.js'; + +describe('novatiqIdSystem', function () { + describe('getSrcId', function() { + it('getSrcId should set srcId value to 000 due to undefined parameter in config section', function() { + const config = { params: { } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('000'); + }); + + it('getSrcId should set srcId value to 000 due to missing value in config section', function() { + const config = { params: { sourceid: '' } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('000'); + }); + + it('getSrcId should set value to 000 due to null value in config section', function() { + const config = { params: { sourceid: null } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('000'); + }); + + it('getSrcId should set value to 001 due to wrong length in config section max 3 chars', function() { + const config = { params: { sourceid: '1234' } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('001'); + }); + + it('getSrcId should set value to 002 due to wrong format in config section', function() { + const config = { params: { sourceid: '1xc' } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('002'); + }); + }); + + describe('getId', function() { + it('should log message if novatiqId has wrong format', function() { + const config = { params: { sourceid: '123' } }; + const response = novatiqIdSubmodule.getId(config); + expect(response.id).to.have.length(40); + }); + + it('should log message if novatiqId not provided', function() { + const config = { params: { sourceid: '123' } }; + const response = novatiqIdSubmodule.getId(config); + expect(response.id).should.be.not.empty; + }); + }); + + describe('decode', function() { + it('should log message if novatiqId has wrong format', function() { + const novatiqId = '81b001ec-8914-488c-a96e-8c220d4ee08895ef'; + const response = novatiqIdSubmodule.decode(novatiqId); + expect(response.novatiq.snowflake).to.have.length(40); + }); + + it('should log message if novatiqId has wrong format', function() { + const novatiqId = '81b001ec-8914-488c-a96e-8c220d4ee08895ef'; + const response = novatiqIdSubmodule.decode(novatiqId); + expect(response.novatiq.snowflake).should.be.not.empty; + }); + }); +}) diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index bc37ea15255..9ee1045a6e8 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -221,7 +221,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.0.5'; + const VERSION = '3.0.6'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -567,7 +567,6 @@ describe('OneVideoBidAdapter', function () { requestId: bidRequest.bidId, bidderCode: spec.code, cpm: serverResponse.seatbid[0].bid[0].price, - adId: serverResponse.seatbid[0].bid[0].adid, creativeId: serverResponse.seatbid[0].bid[0].crid, vastXml: serverResponse.seatbid[0].bid[0].adm, width: 640, diff --git a/test/spec/modules/optimonAnalyticsAdapter_spec.js b/test/spec/modules/optimonAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..b5b76ce3fde --- /dev/null +++ b/test/spec/modules/optimonAnalyticsAdapter_spec.js @@ -0,0 +1,40 @@ +import * as utils from 'src/utils.js'; +import { expect } from 'chai'; +import optimonAnalyticsAdapter from '../../../modules/optimonAnalyticsAdapter.js'; +import adapterManager from 'src/adapterManager'; +import events from 'src/events'; +import constants from 'src/constants.json' + +const AD_UNIT_CODE = 'demo-adunit-1'; +const PUBLISHER_CONFIG = { + pubId: 'optimon_test', + pubAdxAccount: 123456789, + pubTimezone: 'Asia/Jerusalem' +}; + +describe('Optimon Analytics Adapter', () => { + const optmn_currentWindow = utils.getWindowSelf(); + let optmn_queue = []; + + beforeEach(() => { + optmn_currentWindow.OptimonAnalyticsAdapter = (...optmn_args) => optmn_queue.push(optmn_args); + adapterManager.enableAnalytics({ + provider: 'optimon' + }); + optmn_queue = [] + }); + + afterEach(() => { + optimonAnalyticsAdapter.disableAnalytics(); + }); + + it('should forward all events to the queue', () => { + const optmn_arguments = [AD_UNIT_CODE, PUBLISHER_CONFIG]; + + events.emit(constants.EVENTS.AUCTION_END, optmn_arguments) + events.emit(constants.EVENTS.BID_TIMEOUT, optmn_arguments) + events.emit(constants.EVENTS.BID_WON, optmn_arguments) + + expect(optmn_queue.length).to.eql(3); + }); +}); diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index c1022608b4a..10b8ce31d28 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -6,12 +6,13 @@ import {getGranularityKeyName, getGranularityObject} from '../../../modules/ozon import * as utils from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; + /* NOTE - use firefox console to deep copy the objects to use here */ -var originalPropertyBag = {'lotameWasOverridden': 0, 'pageId': null}; +var originalPropertyBag = {'pageId': null}; var validBidRequests = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -21,7 +22,7 @@ var validBidRequests = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } @@ -36,7 +37,7 @@ var validBidRequestsMulti = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' }, @@ -49,11 +50,14 @@ var validBidRequestsMulti = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c0', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; +// use 'pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId', 'criteortus' +// NOTE THAT criteortus is no longer referenced anywhere - should be removed asap +// see http://prebid.org/dev-docs/modules/userId.html var validBidRequestsWithUserIdData = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -63,10 +67,83 @@ var validBidRequestsWithUserIdData = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87', - userId: {'pubcid': '12345678', 'id5id': { 'uid': 'ID5-someId' }, 'criteortus': {'ozone': {'userid': 'critId123'}}, 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, 'parrableId': {eid: 'parrableid123'}} + userId: { + 'pubcid': '12345678', + 'tdid': '1111tdid', + 'id5id': 'ID5-someId', + 'criteortus': {'ozone': {'userid': 'critId123'}}, + 'criteoId': '1111criteoId', + 'idl_env': 'liverampId', + 'lipb': {'lipbid': 'lipbidId123'}, + 'parrableId': {'eid': '01.5678.parrableid'} + }, + userIdAsEids: [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '12345678', + 'atype': 1 + } + ] + }, + { + 'source': 'adserver.org', + 'uids': [{ + 'id': '1111tdid', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }, + { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-someId', + 'atype': 1, + }] + }, + { + 'source': 'criteortus', + 'uids': [{ + 'id': {'ozone': {'userid': 'critId123'}}, + 'atype': 1, + }] + }, + { + 'source': 'criteoId', + 'uids': [{ + 'id': '1111criteoId', + 'atype': 1, + }] + }, + { + 'source': 'idl_env', + 'uids': [{ + 'id': 'liverampId', + 'atype': 1, + }] + }, + { + 'source': 'lipb', + 'uids': [{ + 'id': {'lipbid': 'lipbidId123'}, + 'atype': 1, + }] + }, + { + 'source': 'parrableId', + 'uids': [{ + 'id': {'eid': '01.5678.parrableid'}, + 'atype': 1, + }] + } + ] + } ]; var validBidRequestsMinimal = [ @@ -91,7 +168,7 @@ var validBidRequestsNoSizes = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; @@ -105,7 +182,7 @@ var validBidRequestsWithBannerMediaType = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, mediaTypes: {banner: {sizes: [[300, 250], [300, 600]]}}, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } @@ -119,7 +196,7 @@ var validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, video: {skippable: true, playback_method: ['auto_play_sound_off'], targetDiv: 'some-different-div-id-to-my-adunitcode'} } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, video: {skippable: true, playback_method: ['auto_play_sound_off'], targetDiv: 'some-different-div-id-to-my-adunitcode'} } ] }, mediaTypes: {video: {mimes: ['video/mp4'], 'context': 'outstream', 'sizes': [640, 480], playerSize: [640, 480]}, native: {info: 'dummy data'}}, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } @@ -161,43 +238,21 @@ var validBidRequests1OutstreamVideo2020 = [ } } ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } + 'userId': { + 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' + }, + 'userIdAsEids': [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56', + 'atype': 1 + } + ] } - } - }, - 'userId': { - 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' + ] }, - 'userIdAsEids': [ - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56', - 'atype': 1 - } - ] - } - ], 'mediaTypes': { 'video': { 'playerSize': [ @@ -272,32 +327,10 @@ var validBidderRequest1OutstreamVideo2020 = { 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'userId': { - 'id5id': { uid: 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA' }, + 'id5id': 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA', 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' }, 'userIdAsEids': [ @@ -370,7 +403,7 @@ var validBidderRequest = { bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' }], @@ -399,7 +432,7 @@ var bidderRequestWithFullGdpr = { bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' }], @@ -491,17 +524,6 @@ var bidderRequestWithPartialGdpr = { params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], - lotameData: { - 'Profile': { - 'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', - 'Audiences': { - 'Audience': [{'id': '99999', 'abbr': 'sports'}, { - 'id': '88888', - 'abbr': 'movie' - }, {'id': '77777', 'abbr': 'blogger'}] - } - } - }, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', @@ -982,29 +1004,7 @@ var multiRequest1 = [ 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1069,29 +1069,7 @@ var multiRequest1 = [ 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1165,29 +1143,7 @@ var multiBidderRequest1 = { 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1252,29 +1208,7 @@ var multiBidderRequest1 = { 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1588,8 +1522,7 @@ describe('ozone Adapter', function () { placementId: '1310000099', publisherId: '9876abcd12-3', siteId: '1234567890', - customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], - lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, + customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}] }, siteId: 1234567890 } @@ -1883,26 +1816,11 @@ describe('ozone Adapter', function () { it('should not validate customParams - this is a renamed key', function () { expect(spec.isBidRequestValid(xBadCustomParams)).to.equal(false); }); - - var xBadLotame = { - bidder: BIDDER_CODE, - params: { - 'placementId': '1234567890', - 'publisherId': '9876abcd12-3', - 'lotameData': 'this should be an object', - siteId: '1234567890' - } - }; - it('should not validate lotameData being sent', function () { - expect(spec.isBidRequestValid(xBadLotame)).to.equal(false); - }); - var xBadVideoContext2 = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', 'publisherId': '9876abcd12-3', - 'lotameData': {}, siteId: '1234567890' }, mediaTypes: { @@ -1937,35 +1855,6 @@ describe('ozone Adapter', function () { instreamVid.mediaTypes.video.context = 'instream'; expect(spec.isBidRequestValid(instreamVid)).to.equal(true); }); - // validate lotame override parameters - it('should validate lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': 'tpid123'}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(true); - }); - it('should validate missing lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123'}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(false); - }); - it('should validate invalid lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123 "this ain\\t right!" eee'}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(false); - }); - it('should validate no lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(true); - }); }); describe('buildRequests', function () { @@ -1989,19 +1878,27 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); - expect(data.ext.ozone.lotameData).to.be.an('object'); + expect(data.imp[0].ext.ozone.customData).to.be.an('array'); + expect(request).not.to.have.key('lotameData'); + expect(request).not.to.have.key('customData'); + }); + + it('adds all parameters inside the ext object only - lightning', function () { + let localBidReq = JSON.parse(JSON.stringify(validBidRequests)); + const request = spec.buildRequests(localBidReq, validBidderRequest.bidderRequest); + expect(request.data).to.be.a('string'); + var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); it('ignores ozoneData in & after version 2.1.1', function () { - let validBidRequestsWithOzoneData = validBidRequests; + let validBidRequestsWithOzoneData = JSON.parse(JSON.stringify(validBidRequests)); validBidRequestsWithOzoneData[0].params.ozoneData = {'networkID': '3048', 'dfpSiteID': 'd.thesun', 'sectionID': 'homepage', 'path': '/', 'sec_id': 'null', 'sec': 'sec', 'topics': 'null', 'kw': 'null', 'aid': 'null', 'search': 'null', 'article_type': 'null', 'hide_ads': '', 'article_slug': 'null'}; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsWithOzoneData, validBidderRequest.bidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); - expect(data.ext.ozone.lotameData).to.be.an('object'); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.ozoneData).to.be.undefined; expect(request).not.to.have.key('lotameData'); @@ -2018,7 +1915,7 @@ describe('ozone Adapter', function () { expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('handles no ozone, lotame or custom data', function () { + it('handles no ozone or custom data', function () { const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); @@ -2080,6 +1977,7 @@ describe('ozone Adapter', function () { expect(payload.regs.ext.gdpr).to.equal(1); expect(payload.user.ext.consent).to.equal(consentString); }); + it('should set regs.ext.gdpr flag to 0 when gdprApplies is false', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; let bidderRequest = validBidderRequest.bidderRequest; @@ -2118,13 +2016,14 @@ describe('ozone Adapter', function () { bidRequests[0]['userId'] = { 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': {'uid': '2222'}, + 'id5id': '2222', 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, - 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, + 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', 'pubcid': '5555', 'tdid': '6666' }; + bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); let firstBid = payload.imp[0].ext.ozone; @@ -2138,62 +2037,121 @@ describe('ozone Adapter', function () { bidRequests[0]['userId'] = { 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': {'uid': '2222'}, + 'id5id': '2222', 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, - 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, + 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', // 'pubcid': '5555', // remove pubcid from here to emulate the OLD module & cause the failover code to kick in 'tdid': '6666' }; + bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, validBidderRequest.bidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.pubcid).to.equal(bidRequests[0]['crumbs']['pubcid']); delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests }); - it('should add a user.ext.eids object to contain user ID data in the new location (Nov 2019)', function() { + it('should add a user.ext.eids object to contain user ID data in the new location (Nov 2019) Updated Aug 2020', function() { const request = spec.buildRequests(validBidRequestsWithUserIdData, validBidderRequest.bidderRequest); + /* + 'pubcid': '12345678', + 'tdid': '1111tdid', + 'id5id': 'ID5-someId', + 'criteortus': {'ozone': {'userid': 'critId123'}}, + 'criteoId': '1111criteoId', + 'idl_env': 'liverampId', + 'lipb': {'lipbid': 'lipbidId123'}, + 'parrableId': {'eid': '01.5678.parrableid'} + */ + const payload = JSON.parse(request.data); expect(payload.user).to.exist; expect(payload.user.ext).to.exist; expect(payload.user.ext.eids).to.exist; - expect(payload.user.ext.eids[0]['source']).to.equal('pubcid'); + expect(payload.user.ext.eids[0]['source']).to.equal('pubcid.org'); expect(payload.user.ext.eids[0]['uids'][0]['id']).to.equal('12345678'); - expect(payload.user.ext.eids[1]['source']).to.equal('pubcommon'); - expect(payload.user.ext.eids[1]['uids'][0]['id']).to.equal('12345678'); + expect(payload.user.ext.eids[1]['source']).to.equal('adserver.org'); + expect(payload.user.ext.eids[1]['uids'][0]['id']).to.equal('1111tdid'); expect(payload.user.ext.eids[2]['source']).to.equal('id5-sync.com'); expect(payload.user.ext.eids[2]['uids'][0]['id']).to.equal('ID5-someId'); - expect(payload.user.ext.eids[3]['source']).to.equal('criteortus'); - expect(payload.user.ext.eids[3]['uids'][0]['id']).to.equal('critId123'); - expect(payload.user.ext.eids[4]['source']).to.equal('liveramp.com'); - expect(payload.user.ext.eids[4]['uids'][0]['id']).to.equal('liverampId'); - expect(payload.user.ext.eids[5]['source']).to.equal('liveintent.com'); - expect(payload.user.ext.eids[5]['uids'][0]['id']).to.equal('lipbidId123'); - expect(payload.user.ext.eids[6]['source']).to.equal('parrable.com'); - expect(payload.user.ext.eids[6]['uids'][0]['id']).to.equal('parrableid123'); + expect(payload.user.ext.eids[3]['source']).to.equal('criteortus'); // this is deprecated + expect(payload.user.ext.eids[3]['uids'][0]['id']['ozone']['userid']).to.equal('critId123'); + expect(payload.user.ext.eids[4]['source']).to.equal('criteoId'); + expect(payload.user.ext.eids[4]['uids'][0]['id']).to.equal('1111criteoId'); + expect(payload.user.ext.eids[5]['source']).to.equal('idl_env'); + expect(payload.user.ext.eids[5]['uids'][0]['id']).to.equal('liverampId'); + expect(payload.user.ext.eids[6]['source']).to.equal('lipb'); + expect(payload.user.ext.eids[6]['uids'][0]['id']['lipbid']).to.equal('lipbidId123'); + expect(payload.user.ext.eids[7]['source']).to.equal('parrableId'); + expect(payload.user.ext.eids[7]['uids'][0]['id']['eid']).to.equal('01.5678.parrableid'); + }); + + it('replaces the auction url for a config override', function () { + spec.propertyBag.whitelabel = null; + let fakeOrigin = 'http://sometestendpoint'; + config.setConfig({'ozone': {'endpointOverride': {'origin': fakeOrigin}}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); + expect(request.method).to.equal('POST'); + config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); + spec.propertyBag.whitelabel = null; }); + it('replaces the renderer url for a config override', function () { + spec.propertyBag.whitelabel = null; + let fakeUrl = 'http://renderer.com'; + config.setConfig({'ozone': {'endpointOverride': {'rendererUrl': fakeUrl}}}); + const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest1OutstreamVideo2020.bidderRequest); + const result = spec.interpretResponse(getCleanValidVideoResponse(), validBidderRequest1OutstreamVideo2020); + const bid = result[0]; + expect(bid.renderer).to.be.an.instanceOf(Renderer); + expect(bid.renderer.url).to.equal(fakeUrl); + config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); + spec.propertyBag.whitelabel = null; + }); + + it('replaces the kvp prefix ', function () { + spec.propertyBag.whitelabel = null; + config.setConfig({'ozone': {'kvpPrefix': 'test'}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.ozone).to.haveOwnProperty('test_rw'); + config.setConfig({'ozone': {'kvpPrefix': null}}); + spec.propertyBag.whitelabel = null; + }); + + it('handles an alias ', function () { + spec.propertyBag.whitelabel = null; + config.setConfig({'lmc': {'kvpPrefix': 'test'}}); + let br = JSON.parse(JSON.stringify(validBidRequests)); + br[0]['bidder'] = 'lmc'; + const request = spec.buildRequests(br, validBidderRequest.bidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.lmc).to.haveOwnProperty('test_rw'); + config.setConfig({'lmc': {'kvpPrefix': null}}); // I cant remove the key so set the value to null + spec.propertyBag.whitelabel = null; + }); + var specMock = utils.deepClone(spec); it('should use oztestmode GET value if set', function() { // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: - spec.getGetParametersAsObject = function() { + specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); }); it('should use oztestmode GET value if set, even if there is no customdata in config', function() { // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: - spec.getGetParametersAsObject = function() { + specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); }); - var specMock = utils.deepClone(spec); it('should use a valid ozstoredrequest GET value if set to override the placementId values, and set oz_rw if we find it', function() { // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: specMock.getGetParametersAsObject = function() { @@ -2215,54 +2173,6 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1310000099'); }); - it('should pick up the value of valid lotame override parameters when there is a lotame object', function () { - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123eee'}; - }; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.lotameData.Profile.Audiences.Audience[0].id).to.equal('123abc'); - expect(payload.ext.ozone.oz_lot_rw).to.equal(1); - }); - it('should pick up the value of valid lotame override parameters when there is an empty lotame object', function () { - let nolotameBidReq = JSON.parse(JSON.stringify(validBidRequests)); - nolotameBidReq[0].params.lotameData = {}; - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123eeetpid'}; - }; - const request = spec.buildRequests(nolotameBidReq, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.lotameData.Profile.Audiences.Audience[0].id).to.equal('123abc'); - expect(payload.ext.ozone.lotameData.Profile.tpid).to.equal('123eeetpid'); - expect(payload.ext.ozone.lotameData.Profile.pid).to.equal('pid123'); - expect(payload.ext.ozone.oz_lot_rw).to.equal(1); - }); - it('should pick up the value of valid lotame override parameters when there is NO "lotame" key at all', function () { - let nolotameBidReq = JSON.parse(JSON.stringify(validBidRequests)); - delete (nolotameBidReq[0].params['lotameData']); - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123eeetpid'}; - }; - const request = spec.buildRequests(nolotameBidReq, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.lotameData.Profile.Audiences.Audience[0].id).to.equal('123abc'); - expect(payload.ext.ozone.lotameData.Profile.tpid).to.equal('123eeetpid'); - expect(payload.ext.ozone.lotameData.Profile.pid).to.equal('pid123'); - expect(payload.ext.ozone.oz_lot_rw).to.equal(1); - spec.propertyBag = originalPropertyBag; // tidy up - }); - // NOTE - only one negative test case; - // you can't send invalid lotame params to buildRequests because 'validate' will have rejected them - it('should not use lotame override parameters if they dont exist', function () { - expect(spec.propertyBag.lotameWasOverridden).to.equal(0); - spec.getGetParametersAsObject = function() { - return {}; // no lotame override params - }; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.oz_lot_rw).to.equal(0); - }); - it('should pick up the config value of coppa & set it in the request', function () { config.setConfig({'coppa': true}); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); @@ -2609,6 +2519,14 @@ describe('ozone Adapter', function () { const result = playerSizeIsNestedArray(obj); expect(result).to.be.null; }); + it('should add oz_appnexus_dealid into ads request if dealid exists in the auction response', function () { + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + let validres = JSON.parse(JSON.stringify(validResponse2Bids)); + validres.body.seatbid[0].bid[0].dealid = '1234'; + const result = spec.interpretResponse(validres, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_dealid')).to.equal('1234'); + expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_dealid', '')).to.equal(''); + }); }); describe('default size', function () { @@ -2660,44 +2578,6 @@ describe('ozone Adapter', function () { config.resetConfig(); }); }); - describe('makeLotameObjectFromOverride', function() { - it('should update an object with valid lotame data', function () { - let objLotameOverride = {'oz_lotametpid': '1234', 'oz_lotameid': '12345', 'oz_lotamepid': '123456'}; - let result = spec.makeLotameObjectFromOverride( - objLotameOverride, - {'Profile': {'pid': 'originalpid', 'tpid': 'originaltpid', 'Audiences': {'Audience': [{'id': 'aud1'}]}}} - ); - expect(result.Profile.Audiences.Audience).to.be.an('array'); - expect(result.Profile.Audiences.Audience[0]).to.be.an('object'); - expect(result.Profile.Audiences.Audience[0]).to.deep.include({'id': '12345', 'abbr': '12345'}); - }); - it('should return the original object if it seems weird', function () { - let objLotameOverride = {'oz_lotametpid': '1234', 'oz_lotameid': '12345', 'oz_lotamepid': '123456'}; - let objLotameOriginal = {'Profile': {'pid': 'originalpid', 'tpid': 'originaltpid', 'somethingstrange': [{'id': 'aud1'}]}}; - let result = spec.makeLotameObjectFromOverride( - objLotameOverride, - objLotameOriginal - ); - expect(result).to.equal(objLotameOriginal); - }); - }); - describe('lotameDataIsValid', function() { - it('should allow a valid minimum lotame object', function() { - let obj = {'Profile': {'pid': '', 'tpid': '', 'Audiences': {'Audience': []}}}; - let result = spec.isLotameDataValid(obj); - expect(result).to.be.true; - }); - it('should allow a valid lotame object', function() { - let obj = {'Profile': {'pid': '12345', 'tpid': '45678', 'Audiences': {'Audience': [{'id': '1234', 'abbr': '567'}, {'id': '9999', 'abbr': '1111'}]}}}; - let result = spec.isLotameDataValid(obj); - expect(result).to.be.true; - }); - it('should disallow a lotame object without an Audience.id', function() { - let obj = {'Profile': {'tpid': '', 'pid': '', 'Audiences': {'Audience': [{'abbr': 'marktest'}]}}}; - let result = spec.isLotameDataValid(obj); - expect(result).to.be.false; - }); - }); describe('getPageId', function() { it('should return the same Page ID for multiple calls', function () { let result = spec.getPageId(); @@ -2720,24 +2600,6 @@ describe('ozone Adapter', function () { expect(result).to.equal('outstream'); }); }); - describe('getLotameOverrideParams', function() { - it('should get 3 valid lotame params that exist in GET params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': 'tpid123'}; - }; - let result = spec.getLotameOverrideParams(); - expect(Object.keys(result).length).to.equal(3); - }); - it('should get only 1 valid lotame param that exists in GET params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'xoz_lotamepid': 'pid123', 'xoz_lotametpid': 'tpid123'}; - }; - let result = spec.getLotameOverrideParams(); - expect(Object.keys(result).length).to.equal(1); - }); - }); describe('unpackVideoConfigIntoIABformat', function() { it('should correctly unpack a usual video config', function () { let mediaTypes = { diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 5a10529778c..3f517a66c68 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -210,6 +210,38 @@ describe('Parrable ID System', function() { removeParrableCookie(); }); }); + + describe('GDPR consent', () => { + let callbackSpy = sinon.spy(); + + const config = { + params: { + partner: 'partner' + } + }; + + const gdprConsentTestCases = [ + { consentData: { gdprApplies: true, consentString: 'expectedConsentString' }, expected: { gdpr: 1, gdpr_consent: 'expectedConsentString' } }, + { consentData: { gdprApplies: false, consentString: 'expectedConsentString' }, expected: { gdpr: 0 } }, + { consentData: { gdprApplies: true, consentString: undefined }, expected: { gdpr: 1, gdpr_consent: '' } }, + { consentData: { gdprApplies: 'yes', consentString: 'expectedConsentString' }, expected: { gdpr: 0 } }, + { consentData: undefined, expected: { gdpr: 0 } } + ]; + + gdprConsentTestCases.forEach((testCase, index) => { + it(`should call user sync url with the gdprConsent - case ${index}`, () => { + parrableIdSubmodule.getId(config, testCase.consentData).callback(callbackSpy); + + if (testCase.expected.gdpr === 1) { + expect(server.requests[0].url).to.contain('gdpr=' + testCase.expected.gdpr); + expect(server.requests[0].url).to.contain('gdpr_consent=' + testCase.expected.gdpr_consent); + } else { + expect(server.requests[0].url).to.contain('gdpr=' + testCase.expected.gdpr); + expect(server.requests[0].url).to.not.contain('gdpr_consent'); + } + }) + }); + }); }); describe('parrableIdSystem.decode()', function() { @@ -273,6 +305,20 @@ describe('Parrable ID System', function() { expect(resolvedOptions.called).to.equal(true); }); + it('permits an impression from a lower cased allowed timezone', function() { + const allowedZone = 'America/New_York'; + const resolvedOptions = sinon.stub().returns({ timeZone: allowedZone }); + Intl.DateTimeFormat.returns({ resolvedOptions }); + + expect(parrableIdSubmodule.getId({ params: { + partner: 'prebid-test', + timezoneFilter: { + allowedZones: [ allowedZone.toLowerCase() ] + } + } })).to.have.property('callback'); + expect(resolvedOptions.called).to.equal(true); + }); + it('permits an impression from a timezone that is not blocked', function() { const blockedZone = 'America/New_York'; const resolvedOptions = sinon.stub().returns({ timeZone: 'Iceland' }); @@ -301,6 +347,20 @@ describe('Parrable ID System', function() { expect(resolvedOptions.called).to.equal(true); }); + it('does not permit an impression from a lower cased blocked timezone', function() { + const blockedZone = 'America/New_York'; + const resolvedOptions = sinon.stub().returns({ timeZone: blockedZone }); + Intl.DateTimeFormat.returns({ resolvedOptions }); + + expect(parrableIdSubmodule.getId({ params: { + partner: 'prebid-test', + timezoneFilter: { + blockedZones: [ blockedZone.toLowerCase() ] + } + } })).to.equal(null); + expect(resolvedOptions.called).to.equal(true); + }); + it('does not permit an impression from a blocked timezone even when also allowed', function() { const timezone = 'America/New_York'; const resolvedOptions = sinon.stub().returns({ timeZone: timezone }); diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js new file mode 100644 index 00000000000..cf1f3861eab --- /dev/null +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -0,0 +1,397 @@ +import { permutiveSubmodule, storage, getSegments, initSegments, isAcEnabled, isPermutiveOnPage } from 'modules/permutiveRtdProvider.js' +import { deepAccess } from '../../../src/utils.js' + +describe('permutiveRtdProvider', function () { + before(function () { + const data = getTargetingData() + setLocalStorage(data) + }) + + after(function () { + const data = getTargetingData() + removeLocalStorage(data) + }) + + describe('permutiveSubmodule', function () { + it('should initalise and return true', function () { + expect(permutiveSubmodule.init()).to.equal(true) + }) + }) + + describe('Getting segments', function () { + it('should retrieve segments in the expected structure', function () { + const data = transformedTargeting() + expect(getSegments(250)).to.deep.equal(data) + }) + it('should enforce max segments', function () { + const max = 1 + const segments = getSegments(max) + + for (const key in segments) { + expect(segments[key]).to.have.length(max) + } + }) + }) + + describe('Default segment targeting', function () { + it('sets segment targeting for Xandr', function () { + const data = transformedTargeting() + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'appnexus') { + expect(deepAccess(params, 'keywords.permutive')).to.eql(data.appnexus) + expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac) + } + }) + }) + } + }) + it('sets segment targeting for Magnite', function () { + const data = transformedTargeting() + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'rubicon') { + expect(deepAccess(params, 'visitor.permutive')).to.eql(data.rubicon) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + } + }) + }) + } + }) + it('sets segment targeting for Ozone', function () { + const data = transformedTargeting() + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'ozone') { + expect(deepAccess(params, 'customData.0.targeting.p_standard')).to.eql(data.ac) + } + }) + }) + } + }) + it('sets segment targeting for TrustX', function () { + const data = transformedTargeting() + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'trustx') { + expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac) + } + }) + }) + } + }) + }) + + describe('Custom segment targeting', function () { + it('sets custom segment targeting for Magnite', function () { + const data = transformedTargeting() + const adUnits = getAdUnits() + const config = getConfig() + + config.params.overwrites = { + rubicon: function (bid, data, acEnabled, utils, defaultFn) { + if (defaultFn) { + bid = defaultFn(bid, data, acEnabled) + } + if (data.gam && data.gam.length) { + utils.deepSetValue(bid, 'params.visitor.permutive', data.gam) + } + } + } + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'rubicon') { + expect(deepAccess(params, 'visitor.permutive')).to.eql(data.gam) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + } + }) + }) + } + }) + }) + + describe('Existing key-value targeting', function () { + it('doesn\'t overwrite existing key-values for Xandr', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'appnexus') { + expect(deepAccess(params, 'keywords.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + it('doesn\'t overwrite existing key-values for Magnite', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'rubicon') { + expect(deepAccess(params, 'visitor.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + it('doesn\'t overwrite existing key-values for Ozone', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'ozone') { + expect(deepAccess(params, 'customData.0.targeting.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + it('doesn\'t overwrite existing key-values for TrustX', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'trustx') { + expect(deepAccess(params, 'keywords.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + }) + + describe('Permutive on page', function () { + it('checks if Permutive is on page', function () { + expect(isPermutiveOnPage()).to.equal(false) + }) + }) + + describe('AC is enabled', function () { + it('checks if AC is enabled for Xandr', function () { + expect(isAcEnabled({ params: { acBidders: ['appnexus'] } }, 'appnexus')).to.equal(true) + expect(isAcEnabled({ params: { acBidders: ['kjdbfkvb'] } }, 'appnexus')).to.equal(false) + }) + it('checks if AC is enabled for Magnite', function () { + expect(isAcEnabled({ params: { acBidders: ['rubicon'] } }, 'rubicon')).to.equal(true) + expect(isAcEnabled({ params: { acBidders: ['kjdbfkb'] } }, 'rubicon')).to.equal(false) + }) + it('checks if AC is enabled for Ozone', function () { + expect(isAcEnabled({ params: { acBidders: ['ozone'] } }, 'ozone')).to.equal(true) + expect(isAcEnabled({ params: { acBidders: ['kjdvb'] } }, 'ozone')).to.equal(false) + }) + }) +}) + +function setLocalStorage (data) { + for (const key in data) { + storage.setDataInLocalStorage(key, JSON.stringify(data[key])) + } +} + +function removeLocalStorage (data) { + for (const key in data) { + storage.removeDataFromLocalStorage(key) + } +} + +function getConfig () { + return { + name: 'permutive', + waitForIt: true, + params: { + acBidders: ['appnexus', 'rubicon', 'ozone', 'trustx'], + maxSegs: 500 + } + } +} + +function transformedTargeting () { + const data = getTargetingData() + + return { + ac: [...data._pcrprs, ...data._ppam, ...data._psegs.filter(seg => seg >= 1000000)], + appnexus: data._papns, + rubicon: data._prubicons, + gam: data._pdfps + } +} + +function getTargetingData () { + return { + _pdfps: ['gam1', 'gam2'], + _prubicons: ['rubicon1', 'rubicon2'], + _papns: ['appnexus1', 'appnexus2'], + _psegs: ['1234', '1000001', '1000002'], + _ppam: ['ppam1', 'ppam2'], + _pcrprs: ['pcrprs1', 'pcrprs2'] + } +} + +function getAdUnits () { + const div_1_sizes = [ + [300, 250], + [300, 600] + ] + const div_2_sizes = [ + [728, 90], + [970, 250] + ] + return [ + { + code: '/19968336/header-bid-tag-0', + mediaTypes: { + banner: { + sizes: div_1_sizes + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: 13144370, + keywords: { + test_kv: ['true'] + } + } + }, + { + bidder: 'rubicon', + params: { + accountId: '9840', + siteId: '123564', + zoneId: '583584', + inventory: { + area: ['home'] + }, + visitor: { + test_kv: ['true'] + } + } + }, + { + bidder: 'ozone', + params: { + publisherId: 'OZONEGMG0001', + siteId: '4204204209', + placementId: '0420420500', + customData: [ + { + settings: {}, + targeting: { + test_kv: ['true'] + } + } + ], + ozoneData: {} + } + }, + { + bidder: 'trustx', + params: { + uid: 45, + keywords: { + test_kv: ['true'] + } + } + } + ] + }, + { + code: '/19968336/header-bid-tag-1', + mediaTypes: { + banner: { + sizes: div_2_sizes + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: 13144370, + keywords: { + test_kv: ['true'] + } + } + }, + { + bidder: 'ozone', + params: { + publisherId: 'OZONEGMG0001', + siteId: '4204204209', + placementId: '0420420500', + customData: [ + { + targeting: { + test_kv: ['true'] + } + } + ] + } + } + ] + } + ] +} diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index f17cd3ab14f..809e3933eb9 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -270,6 +270,7 @@ const RESPONSE_OPENRTB = { 'win': 'http://wurl.org?id=333' } }, + 'dchain': { 'ver': '1.0', 'complete': 0, 'nodes': [ { 'asi': 'magnite.com', 'bsid': '123456789', } ] }, 'bidder': { 'appnexus': { 'brand_id': 1, @@ -638,7 +639,7 @@ describe('S2S Adapter', function () { expect(requestBid.gdpr_consent).is.undefined; }); - it('checks gdpr info gets added to cookie_sync request: consent data unknown', function () { + it('checks gdpr info gets added to cookie_sync request: applies is false', function () { let cookieSyncConfig = utils.deepClone(CONFIG); cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; @@ -648,7 +649,7 @@ describe('S2S Adapter', function () { let gdprBidRequest = utils.deepClone(BID_REQUESTS); gdprBidRequest[0].gdprConsent = { consentString: undefined, - gdprApplies: undefined + gdprApplies: false }; const s2sBidRequest = utils.deepClone(REQUEST); @@ -657,7 +658,7 @@ describe('S2S Adapter', function () { adapter.callBids(s2sBidRequest, gdprBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); - expect(requestBid.gdpr).is.undefined; + expect(requestBid.gdpr).is.equal(0); expect(requestBid.gdpr_consent).is.undefined; }); }); @@ -829,6 +830,101 @@ describe('S2S Adapter', function () { expect(requestBid.imp[0].ext.prebid.storedauctionresponse.id).to.equal('11111'); }); + describe('price floors module', function () { + function runTest(expectedFloor, expectedCur) { + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(server.requests[requestCount].requestBody); + expect(requestBid.imp[0].bidfloor).to.equal(expectedFloor); + expect(requestBid.imp[0].bidfloorcur).to.equal(expectedCur); + requestCount += 1; + } + + let getFloorResponse, requestCount; + beforeEach(function () { + getFloorResponse = {}; + requestCount = 0; + }); + + it('should NOT pass bidfloor and bidfloorcur when getFloor not present or returns invalid response', function () { + const _config = { + s2sConfig: CONFIG, + }; + + config.setConfig(_config); + + // if no get floor + runTest(undefined, undefined); + + // if getFloor returns empty object + BID_REQUESTS[0].bids[0].getFloor = () => getFloorResponse; + sinon.spy(BID_REQUESTS[0].bids[0], 'getFloor'); + + runTest(undefined, undefined); + // make sure getFloor was called + expect( + BID_REQUESTS[0].bids[0].getFloor.calledWith({ + currency: 'USD', + }) + ).to.be.true; + + // if getFloor does not return number + getFloorResponse = {currency: 'EUR', floor: 'not a number'}; + runTest(undefined, undefined); + + // if getFloor does not return currency + getFloorResponse = {floor: 1.1}; + runTest(undefined, undefined); + }); + + it('should correctly pass bidfloor and bidfloorcur', function () { + const _config = { + s2sConfig: CONFIG, + }; + + config.setConfig(_config); + + BID_REQUESTS[0].bids[0].getFloor = () => getFloorResponse; + sinon.spy(BID_REQUESTS[0].bids[0], 'getFloor'); + + // returns USD and string floor + getFloorResponse = {currency: 'USD', floor: '1.23'}; + runTest(1.23, 'USD'); + // make sure getFloor was called + expect( + BID_REQUESTS[0].bids[0].getFloor.calledWith({ + currency: 'USD', + }) + ).to.be.true; + + // returns non USD and number floor + getFloorResponse = {currency: 'EUR', floor: 0.85}; + runTest(0.85, 'EUR'); + }); + + it('should correctly pass adServerCurrency when set to getFloor not default', function () { + config.setConfig({ + s2sConfig: CONFIG, + currency: { adServerCurrency: 'JPY' }, + }); + + // we have to start requestCount at 1 because a conversion rates fetch occurs when adServerCur is not USD! + requestCount = 1; + + BID_REQUESTS[0].bids[0].getFloor = () => getFloorResponse; + sinon.spy(BID_REQUESTS[0].bids[0], 'getFloor'); + + // returns USD and string floor + getFloorResponse = {currency: 'JPY', floor: 97.2}; + runTest(97.2, 'JPY'); + // make sure getFloor was called with JPY + expect( + BID_REQUESTS[0].bids[0].getFloor.calledWith({ + currency: 'JPY', + }) + ).to.be.true; + }); + }); + it('adds device.w and device.h even if the config lacks a device object', function () { const _config = { s2sConfig: CONFIG, @@ -953,17 +1049,15 @@ describe('S2S Adapter', function () { adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.ext).to.deep.equal({ - prebid: { - aliases: { - brealtime: 'appnexus' - }, - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true - } + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.include({ + aliases: { + brealtime: 'appnexus' + }, + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true } }); }); @@ -985,17 +1079,15 @@ describe('S2S Adapter', function () { adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.ext).to.deep.equal({ - prebid: { - aliases: { - [alias]: 'appnexus' - }, - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true - } + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.include({ + aliases: { + [alias]: 'appnexus' + }, + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true } }); }); @@ -1376,7 +1468,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.equal({ + expect(requestBid.ext.prebid).to.deep.include({ auctiontimestamp: 1510852447530, foo: 'bar', targeting: { @@ -1410,7 +1502,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.equal({ + expect(requestBid.ext.prebid).to.deep.include({ auctiontimestamp: 1510852447530, targeting: { includewinners: false, @@ -1446,7 +1538,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.equal({ + expect(requestBid.ext.prebid).to.deep.include({ auctiontimestamp: 1510852447530, cache: { vastxml: 'vastxml-set-though-extPrebid.cache.vastXml' @@ -1515,20 +1607,43 @@ describe('S2S Adapter', function () { const expected = allowedBidders.map(bidder => ({ bidders: [ bidder ], - config: { fpd: { site: context, user } } + config: { + ortb2: { + site: { + content: { userrating: 4 }, + ext: { + data: { + pageType: 'article', + category: 'tools' + } + } + }, + user: { + yob: '1984', + geo: { country: 'ca' }, + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + } })); + const commonContextExpected = utils.mergeDeep({'page': 'http://mytestpage.com', 'publisher': {'id': '1'}}, commonContext); config.setConfig({ fpd: { context: commonContext, user: commonUser } }); config.setBidderConfig({ bidders: allowedBidders, config: { fpd: { context, user } } }); adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.ext.prebid.bidderconfig).to.deep.equal(expected); - expect(parsedRequestBody.site.ext.data).to.deep.equal(commonContext); - expect(parsedRequestBody.user.ext.data).to.deep.equal(commonUser); + expect(parsedRequestBody.site).to.deep.equal(commonContextExpected); + expect(parsedRequestBody.user).to.deep.equal(commonUser); }); describe('pbAdSlot config', function () { - it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context\" is undefined', function () { + it('should not send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); @@ -1538,30 +1653,32 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.pbadslot'); }); - it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" is undefined', function () { + it('should not send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = {}; + bidRequest.ad_units[0].ortb2Imp = {}; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.pbadslot'); }); - it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" is empty string', function () { + it('should not send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" is empty string', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - pbAdSlot: '' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + pbadslot: '' + } } }; @@ -1570,16 +1687,18 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.pbadslot'); }); - it('should send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" value is a non-empty string', function () { + it('should send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a non-empty string', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - pbAdSlot: '/a/b/c' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + pbadslot: '/a/b/c' + } } }; @@ -1588,13 +1707,13 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.pbadslot'); - expect(parsedRequestBody.imp[0].ext.context.data.pbadslot).to.equal('/a/b/c'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.data.pbadslot'); + expect(parsedRequestBody.imp[0].ext.data.pbadslot).to.equal('/a/b/c'); }); }); describe('GAM ad unit config', function () { - it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context\" is undefined', function () { + it('should not send \"imp.ext.data.adserver.adslot\" if \"ortb2Imp.ext\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); @@ -1604,31 +1723,33 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.adslot'); }); - it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context.adserver.adSlot\" is undefined', function () { + it('should not send \"imp.ext.data.adserver.adslot\" if \"ortb2Imp.ext.data.adserver.adslot\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = {}; + bidRequest.ad_units[0].ortb2Imp = {}; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.adslot'); }); - it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context.adserver.adSlot\" is empty string', function () { + it('should not send \"imp.ext.data.adserver.adslot\" if \"ortb2Imp.ext.data.adserver.adslot\" is empty string', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - adServer: { - adSlot: '' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '' + } } } }; @@ -1638,18 +1759,20 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.adslot'); }); - it('should send both \"adslot\" and \"name\" from \"imp.ext.context.data.adserver\" if \"fpd.context.adserver.adSlot\" and \"fpd.context.adserver.name\" values are non-empty strings', function () { + it('should send both \"adslot\" and \"name\" from \"imp.ext.data.adserver\" if \"ortb2Imp.ext.data.adserver.adslot\" and \"ortb2Imp.ext.data.adserver.name\" values are non-empty strings', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - adserver: { - adSlot: '/a/b/c', - name: 'adserverName1' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '/a/b/c', + name: 'adserverName1' + } } } }; @@ -1659,10 +1782,10 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adserver.adslot'); - expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adserver.name'); - expect(parsedRequestBody.imp[0].ext.context.data.adserver.adslot).to.equal('/a/b/c'); - expect(parsedRequestBody.imp[0].ext.context.data.adserver.name).to.equal('adserverName1'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.data.adserver.adslot'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.data.adserver.name'); + expect(parsedRequestBody.imp[0].ext.data.adserver.adslot).to.equal('/a/b/c'); + expect(parsedRequestBody.imp[0].ext.data.adserver.name).to.equal('adserverName1'); }); }); }); @@ -1845,6 +1968,9 @@ describe('S2S Adapter', function () { expect(response).to.have.property('meta'); expect(response.meta).to.have.property('advertiserDomains'); expect(response.meta.advertiserDomains[0]).to.equal('appnexus.com'); + expect(response.meta).to.have.property('dchain'); + expect(response.meta.dchain.ver).to.equal('1.0'); + expect(response.meta.dchain.nodes[0].asi).to.equal('magnite.com'); expect(response).to.not.have.property('vastUrl'); expect(response).to.not.have.property('videoCacheKey'); expect(response).to.have.property('ttl', 60); @@ -2438,5 +2564,36 @@ describe('S2S Adapter', function () { const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.bidders).to.deep.equal(['appnexus', 'rubicon']); }); + + it('should add cooperative sync flag to cookie_sync request if property is present', function () { + let s2sConfig = utils.deepClone(CONFIG); + s2sConfig.coopSync = false; + s2sConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = s2sConfig; + + let bidRequest = utils.deepClone(BID_REQUESTS); + + adapter.callBids(s2sBidRequest, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.coopSync).to.equal(false); + }); + + it('should not add cooperative sync flag to cookie_sync request if property is not present', function () { + let s2sConfig = utils.deepClone(CONFIG); + s2sConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = s2sConfig; + + let bidRequest = utils.deepClone(BID_REQUESTS); + + adapter.callBids(s2sBidRequest, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.coopSync).to.be.undefined; + }); }); }); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 37deb0bca9c..09573fe9f85 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -2614,7 +2614,7 @@ describe('PubMatic adapter', function () { } expect(response[0].dealId).to.equal(bidResponses.body.seatbid[0].bid[0].dealid); expect(response[0].currency).to.equal('USD'); - expect(response[0].netRevenue).to.equal(false); + expect(response[0].netRevenue).to.equal(true); expect(response[0].ttl).to.equal(300); expect(response[0].meta.networkId).to.equal(123); expect(response[0].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-987'); @@ -2638,7 +2638,7 @@ describe('PubMatic adapter', function () { } expect(response[1].dealId).to.equal(bidResponses.body.seatbid[1].bid[0].dealid); expect(response[1].currency).to.equal('USD'); - expect(response[1].netRevenue).to.equal(false); + expect(response[1].netRevenue).to.equal(true); expect(response[1].ttl).to.equal(300); expect(response[1].meta.networkId).to.equal(422); expect(response[1].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-789'); diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js index 91c81dcae8d..95245f2c6c9 100644 --- a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -27,6 +27,8 @@ describe('pubxai analytics adapter', function() { pubxId: '6c415fc0-8b0e-4cf5-be73-01526a4db625' }; + let location = utils.getWindowLocation(); + let prebidEvent = { 'auctionInit': { 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', @@ -219,6 +221,12 @@ describe('pubxai analytics adapter', function() { 'originalCpm': 0.5, 'originalCurrency': 'USD', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -381,6 +389,12 @@ describe('pubxai analytics adapter', function() { 'originalCpm': 0.5, 'originalCurrency': 'USD', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -449,6 +463,12 @@ describe('pubxai analytics adapter', function() { 'originalCpm': 0.5, 'originalCurrency': 'USD', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -488,9 +508,13 @@ describe('pubxai analytics adapter', function() { 'params': [{ 'placementId': 13144370 }] - } + }, + 'pageDetail': { + 'host': location.host, + 'path': location.pathname, + 'search': location.search + }, }; - let location = utils.getWindowLocation(); let expectedAfterBid = { 'bids': [{ @@ -509,6 +533,12 @@ describe('pubxai analytics adapter', function() { 'mediaType': 'banner', 'statusMessage': 'Bid available', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -527,118 +557,21 @@ describe('pubxai analytics adapter', function() { 'timeToRespond': 267, 'responseTimestamp': 1603865707449, 'platform': navigator.platform, + 'placementId': 13144370, 'deviceType': getDeviceType() }], - 'auctionInit': { + 'pageDetail': { 'host': location.host, 'path': location.pathname, - 'search': location.search, - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'timestamp': 1603865707180, - 'auctionStatus': 'inProgress', - 'adUnits': [{ - 'code': '/19968336/header-bid-tag-1', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ] - ] - } - }, - 'bids': [{ - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'floorData': { - 'skipped': false, - 'skipRate': 0, - 'modelVersion': 'new model 1.0', - 'location': 'fetch', - 'floorProvider': 'PubXFloor', - 'fetchStatus': 'success' - } - }], - 'sizes': [ - [ - 300, - 250 - ] - ], - 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294' - }], - 'adUnitCodes': [ - '/19968336/header-bid-tag-1' - ], - 'bidderRequests': [{ - 'bidderCode': 'appnexus', - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'bidderRequestId': '184cbc05bb90ba', - 'bids': [{ - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'floorData': { - 'skipped': false, - 'skipRate': 0, - 'modelVersion': 'new model 1.0', - 'location': 'fetch', - 'floorProvider': 'PubXFloor', - 'fetchStatus': 'success' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ] - ] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294', - 'sizes': [ - [ - 300, - 250 - ] - ], - 'bidId': '248f9a4489835e', - 'bidderRequestId': '184cbc05bb90ba', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - }], - 'auctionStart': 1603865707180, - 'timeout': 1000, - 'refererInfo': { - 'referer': 'http://local-pnh.net:8080/stream/', - 'reachedTop': true, - 'isAmp': false, - 'numIframes': 0, - 'stack': [ - 'http://local-pnh.net:8080/stream/' - ], - 'canonicalUrl': null - }, - 'start': 1603865707182 - }], - 'noBids': [], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 1000, - 'config': { - 'samplingRate': '1', - 'pubxId': '6c415fc0-8b0e-4cf5-be73-01526a4db625' - } + 'search': location.search + }, + 'floorDetail': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false }, 'initOptions': initOptions }; @@ -660,6 +593,12 @@ describe('pubxai analytics adapter', function() { 'status': 'rendered', 'statusMessage': 'Bid available', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -680,6 +619,11 @@ describe('pubxai analytics adapter', function() { 'platform': navigator.platform, 'deviceType': getDeviceType() }, + 'pageDetail': { + 'host': location.host, + 'path': location.pathname, + 'search': location.search + }, 'initOptions': initOptions } diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 91e3cf4bfdf..bc776f7ebe7 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -70,11 +70,12 @@ describe('qwarryBidAdapter', function () { describe('buildRequests', function () { let bidRequests = [REQUEST] - const bidderRequest = spec.buildRequests(bidRequests, { bidderRequestId: '123' }) + const bidderRequest = spec.buildRequests(bidRequests, { bidderRequestId: '123', refererInfo: { referer: 'http://test.com/path.html' } }) it('sends bid request to ENDPOINT via POST', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.equal('123') + expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) expect(bidderRequest.options.contentType).to.equal('application/json') diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 3b9f07b6efc..5deb2463523 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -20,7 +20,7 @@ describe('Richaudience adapter tests', function () { bidfloor: 0.5, pid: 'ADb1f40rmi', supplyType: 'site', - keywords: 'coche=mercedes;coche=audi' + keywords: 'key1=value1;key2=value2' }, auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', bidRequestsCount: 1, @@ -75,26 +75,19 @@ describe('Richaudience adapter tests', function () { user: {} }]; - var DEFAULT_PARAMS_VIDEO_OUT_PARAMS = [{ + var DEFAULT_PARAMS_BANNER_OUTSTREAM = [{ adUnitCode: 'test-div', bidId: '2c7c8e9c900244', mediaTypes: { - video: { - context: 'outstream', - playerSize: [640, 480], - mimes: ['video/mp4'] + banner: { + sizes: [[300, 250], [600, 300]] } }, bidder: 'richaudience', params: { bidfloor: 0.5, pid: 'ADb1f40rmi', - supplyType: 'site', - player: { - init: 'close', - end: 'close', - skin: 'dark' - } + supplyType: 'site' }, auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', bidRequestsCount: 1, @@ -242,7 +235,7 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('numIframes').and.to.equal(0); expect(typeof requestContent.scr_rsl === 'string') expect(typeof requestContent.cpuc === 'number') - expect(requestContent).to.have.property('kws').and.to.equal('coche=mercedes;coche=audi'); + expect(requestContent).to.have.property('kws').and.to.equal('key1=value1;key2=value2'); }) it('Verify build request to prebid video inestream', function() { @@ -262,8 +255,6 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('demand').and.to.equal('video'); expect(requestContent.videoData).to.have.property('format').and.to.equal('instream'); - // expect(requestContent.videoData.playerSize[0][0]).to.equal('640'); - // expect(requestContent.videoData.playerSize[0][0]).to.equal('480'); }) it('Verify build request to prebid video outstream', function() { @@ -281,6 +272,7 @@ describe('Richaudience adapter tests', function () { expect(request[0]).to.have.property('method').and.to.equal('POST'); const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('demand').and.to.equal('video'); expect(requestContent.videoData).to.have.property('format').and.to.equal('outstream'); }) @@ -623,9 +615,19 @@ describe('Richaudience adapter tests', function () { }); const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request[0]); + expect(bids).to.have.lengthOf(1); const bid = bids[0]; + expect(bid.cpm).to.equal(1.50); expect(bid.mediaType).to.equal('video'); expect(bid.vastXml).to.equal(''); + expect(bid.cpm).to.equal(1.50); + expect(bid.width).to.equal(1); + expect(bid.height).to.equal(1); + expect(bid.creativeId).to.equal('189198063'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(300); + expect(bid.dealId).to.equal('dealId'); }); it('no banner media response outstream', function () { @@ -640,6 +642,35 @@ describe('Richaudience adapter tests', function () { } }); + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.50); + expect(bid.mediaType).to.equal('video'); + expect(bid.vastXml).to.equal(''); + expect(bid.renderer.url).to.equal('https://cdn3.richaudience.com/prebidVideo/player.js'); + expect(bid.cpm).to.equal(1.50); + expect(bid.width).to.equal(1); + expect(bid.height).to.equal(1); + expect(bid.creativeId).to.equal('189198063'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(300); + expect(bid.dealId).to.equal('dealId'); + }); + + it('banner media and response VAST', function () { + const request = spec.buildRequests(DEFAULT_PARAMS_BANNER_OUTSTREAM, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'https://domain.com', + numIframes: 0 + } + }); + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request[0]); const bid = bids[0]; expect(bid.mediaType).to.equal('video'); @@ -656,6 +687,16 @@ describe('Richaudience adapter tests', function () { expect(spec.aliases[0]).to.equal('ra'); }); + it('Verifies bidder gvlid', function () { + expect(spec.gvlid).to.equal(108); + }); + + it('Verifies bidder supportedMediaTypes', function () { + expect(spec.supportedMediaTypes).to.have.lengthOf(2); + expect(spec.supportedMediaTypes[0]).to.equal('banner'); + expect(spec.supportedMediaTypes[1]).to.equal('video'); + }); + it('Verifies if bid request is valid', function () { expect(spec.isBidRequestValid(DEFAULT_PARAMS_NEW_SIZES[0])).to.equal(true); expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true); @@ -717,6 +758,34 @@ describe('Richaudience adapter tests', function () { bidfloor: 0.50, } })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + pid: ['1gCB5ZC4XL', '1a40xk8qSV'], + bidfloor: 0.50, + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + pid: ['1gCB5ZC4XL', '1a40xk8qSV'], + supplyType: 'site', + bidfloor: 0.50, + } + })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + supplyType: 'site', + bidfloor: 0.50, + ifa: 'AAAAAAAAA-BBBB-CCCC-1111-222222220000', + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + pid: ['1gCB5ZC4XL', '1a40xk8qSV'], + supplyType: 'site', + bidfloor: 0.50, + ifa: 'AAAAAAAAA-BBBB-CCCC-1111-222222220000', + } + })).to.equal(true); }); it('Verifies user syncs iframe', function () { diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 176437c4f27..b3257cbda9d 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -75,6 +75,8 @@ describe('riseAdapter', function () { bidderCode: 'rise', } + const customSessionId = '12345678'; + it('sends bid request to ENDPOINT via GET', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { @@ -83,6 +85,22 @@ describe('riseAdapter', function () { } }); + it('sends the is_wrapper query param', function () { + bidRequests[0].params.isWrapper = true; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.is_wrapper).to.equal(true); + } + }); + + it('sends the custom session id as a query param', function () { + bidRequests[0].params.sessionId = customSessionId; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.session_id).to.equal(customSessionId); + } + }); + it('sends bid request to test ENDPOINT via GET', function () { const requests = spec.buildRequests(testModeBidRequests, bidderRequest); for (const request of requests) { diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 71e5446ed06..a9c1eeef4de 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -636,6 +636,7 @@ describe('rubicon analytics adapter', function () { } }); expect(rubiConf).to.deep.equal({ + analyticsEventDelay: 0, pvid: '12345678', wrapperName: '1001_general', int_type: 'dmpbjs', @@ -654,6 +655,7 @@ describe('rubicon analytics adapter', function () { } }); expect(rubiConf).to.deep.equal({ + analyticsEventDelay: 0, pvid: '12345678', wrapperName: '1001_general', int_type: 'dmpbjs', @@ -674,6 +676,7 @@ describe('rubicon analytics adapter', function () { } }); expect(rubiConf).to.deep.equal({ + analyticsEventDelay: 0, pvid: '12345678', wrapperName: '1001_general', int_type: 'dmpbjs', @@ -849,6 +852,69 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].bids[0].bidResponse.dimensions).to.equal(undefined); }); + it('should pass along adomians correctly', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + + // 1 adomains + let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); + bidResponse1.meta = { + advertiserDomains: ['magnite.com'] + } + + // two adomains + let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); + bidResponse2.meta = { + advertiserDomains: ['prebid.org', 'magnite.com'] + } + + // make sure we only pass max 10 adomains + bidResponse2.meta.advertiserDomains = [...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains] + + events.emit(BID_RESPONSE, bidResponse1); + events.emit(BID_RESPONSE, bidResponse2); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + let message = JSON.parse(server.requests[0].requestBody); + validate(message); + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.adomains).to.deep.equal(['magnite.com']); + expect(message.auctions[0].adUnits[1].bids[0].bidResponse.adomains).to.deep.equal(['prebid.org', 'magnite.com', 'prebid.org', 'magnite.com', 'prebid.org', 'magnite.com', 'prebid.org', 'magnite.com', 'prebid.org', 'magnite.com']); + }); + + it('should NOT pass along adomians correctly when edge cases', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + + // empty => nothing + let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); + bidResponse1.meta = { + advertiserDomains: [] + } + + // not array => nothing + let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); + bidResponse2.meta = { + advertiserDomains: 'prebid.org' + } + + events.emit(BID_RESPONSE, bidResponse1); + events.emit(BID_RESPONSE, bidResponse2); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + let message = JSON.parse(server.requests[0].requestBody); + validate(message); + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.adomains).to.be.undefined; + expect(message.auctions[0].adUnits[1].bids[0].bidResponse.adomains).to.be.undefined; + }); + function performFloorAuction(provider) { let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); auctionInit.bidderRequests[0].bids[0].floorData = { @@ -1370,8 +1436,8 @@ describe('rubicon analytics adapter', function () { slot: gptSlot0, isEmpty: false, advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333 + sourceAgnosticCreativeId: 2222, + sourceAgnosticLineItemId: 3333 } }; @@ -1382,8 +1448,8 @@ describe('rubicon analytics adapter', function () { slot: gptSlot1, isEmpty: false, advertiserId: 4444, - creativeId: 5555, - lineItemId: 6666 + sourceAgnosticCreativeId: 5555, + sourceAgnosticLineItemId: 6666 } }; }); @@ -1449,8 +1515,8 @@ describe('rubicon analytics adapter', function () { slot: gptSlot1, isEmpty: false, advertiserId: 0, - creativeId: 0, - lineItemId: 0 + sourceAgnosticCreativeId: 0, + sourceAgnosticLineItemId: 0 } }]); expect(server.requests.length).to.equal(1); @@ -1474,6 +1540,38 @@ describe('rubicon analytics adapter', function () { expect(message).to.deep.equal(expectedMessage); }); + it('should pick backup Ids if no sourceAgnostic available first', function () { + performStandardAuction([gptSlotRenderEnded0, { + eventName: 'slotRenderEnded', + params: { + slot: gptSlot1, + isEmpty: false, + advertiserId: 0, + lineItemId: 1234, + creativeId: 5678 + } + }]); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.auctions[0].adUnits[0].gam = { + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333, + adSlot: '/19968336/header-bid-tag-0' + }; + expectedMessage.auctions[0].adUnits[1].gam = { + advertiserId: 0, + creativeId: 5678, + lineItemId: 1234, + adSlot: '/19968336/header-bid-tag1' + }; + expect(message).to.deep.equal(expectedMessage); + }); + it('should correctly set adUnit for associated slots', function () { performStandardAuction([gptSlotRenderEnded0, gptSlotRenderEnded1]); expect(server.requests.length).to.equal(1); @@ -1500,7 +1598,7 @@ describe('rubicon analytics adapter', function () { it('should send request when waitForGamSlots is present but no bidWons are sent', function () { config.setConfig({ rubicon: { - waitForGamSlots: true + waitForGamSlots: true, } }); events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); @@ -1511,7 +1609,7 @@ describe('rubicon analytics adapter', function () { events.emit(AUCTION_END, MOCK.AUCTION_END); events.emit(SET_TARGETING, MOCK.SET_TARGETING); - // should not send if just slotRenderEnded is emmitted for both + // should send if just slotRenderEnded is emmitted for both mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params); mockGpt.emitEvent(gptSlotRenderEnded1.eventName, gptSlotRenderEnded1.params); @@ -1536,6 +1634,51 @@ describe('rubicon analytics adapter', function () { }; expect(message).to.deep.equal(expectedMessage); }); + + it('should delay the event call depending on analyticsEventDelay config', function () { + config.setConfig({ + rubicon: { + waitForGamSlots: true, + analyticsEventDelay: 2000 + } + }); + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + + // should send if just slotRenderEnded is emmitted for both + mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params); + mockGpt.emitEvent(gptSlotRenderEnded1.eventName, gptSlotRenderEnded1.params); + + // Should not be sent until delay + expect(server.requests.length).to.equal(0); + + // tick the clock and it should fire + clock.tick(2000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + let expectedGam0 = { + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333, + adSlot: '/19968336/header-bid-tag-0' + }; + let expectedGam1 = { + advertiserId: 4444, + creativeId: 5555, + lineItemId: 6666, + adSlot: '/19968336/header-bid-tag1' + }; + expect(expectedGam0).to.deep.equal(message.auctions[0].adUnits[0].gam); + expect(expectedGam1).to.deep.equal(message.auctions[0].adUnits[1].gam); + }); }); it('should correctly overwrite bidId if seatBidId is on the bidResponse', function () { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 8c25d97dada..36890a2891b 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -827,27 +827,39 @@ describe('the rubicon adapter', function () { }); it('should merge first party data from getConfig with the bid params, if present', () => { - const context = { + const site = { keywords: 'e,f', rating: '4-star', - data: { - page: 'home' + ext: { + data: { + page: 'home' + } } }; const user = { + data: [{ + 'name': 'www.dataprovider1.com', + 'ext': { 'taxonomyname': 'IAB Audience Taxonomy' }, + 'segment': [ + { 'id': '687' }, + { 'id': '123' } + ] + }], gender: 'M', yob: '1984', geo: {country: 'ca'}, keywords: 'd', - data: { - age: 40 + ext: { + data: { + age: 40 + } } }; sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context, + ortb2: { + site, user } }; @@ -861,8 +873,9 @@ describe('the rubicon adapter', function () { 'tg_v.likes': 'sports,video games', 'tg_v.gender': 'M', 'tg_v.age': '40', + 'tg_v.iab': '687,123', 'tg_v.yob': '1984', - 'tg_i.rating': '5-star', + 'tg_i.rating': '4-star,5-star', 'tg_i.page': 'home', 'tg_i.prodtype': 'tech,mobile', }; @@ -1287,12 +1300,12 @@ describe('the rubicon adapter', function () { describe('Prebid AdSlot', function () { beforeEach(function () { // enforce that the bid at 0 does not have a 'context' property - if (bidderRequest.bids[0].hasOwnProperty('fpd')) { - delete bidderRequest.bids[0].fpd; + if (bidderRequest.bids[0].hasOwnProperty('ortb2Imp')) { + delete bidderRequest.bids[0].ortb2Imp; } }); - it('should not send \"tg_i.pbadslot’\" if \"fpd.context\" object is not valid', function () { + it('should not send \"tg_i.pbadslot’\" if \"ortb2Imp.ext.data\" object is not valid', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1300,8 +1313,8 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.pbadslot’'); }); - it('should not send \"tg_i.pbadslot’\" if \"fpd.context.pbAdSlot\" is undefined', function () { - bidderRequest.bids[0].fpd = {}; + it('should not send \"tg_i.pbadslot’\" if \"ortb2Imp.ext.data.pbadslot\" is undefined', function () { + bidderRequest.bids[0].ortb2Imp = {}; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1310,10 +1323,12 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.pbadslot’'); }); - it('should not send \"tg_i.pbadslot’\" if \"fpd.context.pbAdSlot\" value is an empty string', function () { - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: '' + it('should not send \"tg_i.pbadslot’\" if \"ortb2Imp.ext.data.pbadslot\" value is an empty string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: '' + } } }; @@ -1324,10 +1339,12 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.pbadslot'); }); - it('should send \"tg_i.pbadslot\" if \"fpd.context.pbAdSlot\" value is a valid string', function () { - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: 'abc' + it('should send \"tg_i.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a valid string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: 'abc' + } } } @@ -1339,12 +1356,14 @@ describe('the rubicon adapter', function () { expect(data['tg_i.pbadslot']).to.equal('abc'); }); - it('should send \"tg_i.pbadslot\" if \"fpd.context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: '/a/b/c' + it('should send \"tg_i.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: '/a/b/c' + } } - }; + } const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1358,12 +1377,12 @@ describe('the rubicon adapter', function () { describe('GAM ad unit', function () { beforeEach(function () { // enforce that the bid at 0 does not have a 'context' property - if (bidderRequest.bids[0].hasOwnProperty('fpd')) { - delete bidderRequest.bids[0].fpd; + if (bidderRequest.bids[0].hasOwnProperty('ortb2Imp')) { + delete bidderRequest.bids[0].ortb2Imp; } }); - it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context\" object is not valid', function () { + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"ortb2Imp.ext.data\" object is not valid', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1371,8 +1390,8 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code’'); }); - it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context.adServer.adSlot\" is undefined', function () { - bidderRequest.bids[0].fpd = {}; + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"ortb2Imp.ext.data.adServer.adslot\" is undefined', function () { + bidderRequest.bids[0].ortb2Imp = {}; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1381,11 +1400,13 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code’'); }); - it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context.adServer.adSlot\" value is an empty string', function () { - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: '' + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"ortb2Imp.ext.data.adServer.adslot\" value is an empty string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '' + } } } }; @@ -1397,11 +1418,13 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.adServer.adSlot\" value is a valid string', function () { - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: 'abc' + it('should send \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: 'abc' + } } } } @@ -1414,11 +1437,13 @@ describe('the rubicon adapter', function () { expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.adServer.adSlot\" value is a valid string, but all leading slash characters should be removed', function () { - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: 'a/b/c' + it('should send \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: 'a/b/c' + } } } }; @@ -1871,27 +1896,36 @@ describe('the rubicon adapter', function () { it('should include first party data', () => { createVideoBidderRequest(); - const context = { - data: { - page: 'home' + const site = { + ext: { + data: { + page: 'home' + } + }, + content: { + data: [{foo: 'bar'}] }, keywords: 'e,f', - rating: '4-star' + rating: '4-star', + data: [{foo: 'bar'}] }; const user = { - data: { - age: 31 + ext: { + data: { + age: 31 + } }, keywords: 'd', gender: 'M', yob: '1984', - geo: {country: 'ca'} + geo: {country: 'ca'}, + data: [{foo: 'bar'}] }; sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context, + ortb2: { + site, user } }; @@ -1901,19 +1935,19 @@ describe('the rubicon adapter', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const expected = { - site: Object.assign({}, context, context.data, bidderRequest.bids[0].params.inventory), - user: Object.assign({}, user, user.data, bidderRequest.bids[0].params.visitor) + site: Object.assign({}, site, {keywords: bidderRequest.bids[0].params.keywords.join(',')}), + user: Object.assign({}, user), + siteData: Object.assign({}, site.ext.data, bidderRequest.bids[0].params.inventory), + userData: Object.assign({}, user.ext.data, bidderRequest.bids[0].params.visitor), }; - delete expected.site.data; - delete expected.user.data; - delete expected.site.keywords; - delete expected.user.keywords; + delete request.data.site.page; + delete request.data.site.content.language; expect(request.data.site.keywords).to.deep.equal('a,b,c'); expect(request.data.user.keywords).to.deep.equal('d'); - expect(request.data.site.ext.data).to.deep.equal(expected.site); - expect(request.data.user.ext.data).to.deep.equal(expected.user); + expect(request.data.site.ext.data).to.deep.equal(expected.siteData); + expect(request.data.user.ext.data).to.deep.equal(expected.userData); }); it('should include storedAuctionResponse in video bid request', function () { @@ -1932,29 +1966,33 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].ext.prebid.storedauctionresponse.id).to.equal('11111'); }); - it('should include pbAdSlot in bid request', function () { + it('should include pbadslot in bid request', function () { createVideoBidderRequest(); - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: '1234567890' + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: '1234567890' + } } - }; + } sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].ext.context.data.pbadslot).to.equal('1234567890'); + expect(request.data.imp[0].ext.data.pbadslot).to.equal('1234567890'); }); it('should include GAM ad unit in bid request', function () { createVideoBidderRequest(); - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: '1234567890', - name: 'adServerName1' + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '1234567890', + name: 'adServerName1' + } } } }; @@ -1964,8 +2002,8 @@ describe('the rubicon adapter', function () { ); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].ext.context.data.adserver.adslot).to.equal('1234567890'); - expect(request.data.imp[0].ext.context.data.adserver.name).to.equal('adServerName1'); + expect(request.data.imp[0].ext.data.adserver.adslot).to.equal('1234567890'); + expect(request.data.imp[0].ext.data.adserver.name).to.equal('adServerName1'); }); it('should use the integration type provided in the config instead of the default', () => { diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 8957bde6bd9..b43d2fa8a5f 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai' import { spec, getTimeoutUrl } from 'modules/seedtagBidAdapter.js' +import * as utils from 'src/utils.js' const PUBLISHER_ID = '0000-0000-01' const ADUNIT_ID = '000000' @@ -42,7 +43,7 @@ describe('Seedtag Adapter', function() { } ) } - const placements = ['banner', 'video', 'inImage', 'inScreen'] + const placements = ['banner', 'video', 'inImage', 'inScreen', 'inArticle'] placements.forEach(placement => { it('should be ' + placement, function() { const isBidRequestValid = spec.isBidRequestValid( @@ -53,7 +54,7 @@ describe('Seedtag Adapter', function() { }) }) }) - describe('when video slot has all mandatory params.', function() { + describe('when video slot has all mandatory params', function() { it('should return true, when video mediatype object are correct.', function() { const slotConfig = getSlotConfigs( { @@ -116,7 +117,7 @@ describe('Seedtag Adapter', function() { expect(isBidRequestValid).to.equal(false) }) }) - describe('when video mediaType object is not correct.', function() { + describe('when video mediaType object is not correct', function() { function createVideoSlotconfig(mediaType) { return getSlotConfigs(mediaType, { publisherId: PUBLISHER_ID, @@ -301,7 +302,7 @@ describe('Seedtag Adapter', function() { expect(typeof bids).to.equal('object') expect(bids.length).to.equal(0) }) - it('should return a void array, when the server response have not got bids.', function() { + it('should return a void array, when the server response have no bids.', function() { const request = { data: JSON.stringify({}) } const serverResponse = { body: { bids: [] } } const bids = spec.interpretResponse(serverResponse, request) @@ -323,7 +324,8 @@ describe('Seedtag Adapter', function() { width: 728, height: 90, mediaType: 'display', - ttl: 360 + ttl: 360, + nurl: 'testurl.com/nurl' } ], cookieSync: { url: '' } @@ -338,6 +340,7 @@ describe('Seedtag Adapter', function() { expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) expect(bids[0].ad).to.equal('content') + expect(bids[0].nurl).to.equal('testurl.com/nurl') }) }) describe('the bid is a video', function() { @@ -354,7 +357,8 @@ describe('Seedtag Adapter', function() { width: 728, height: 90, mediaType: 'video', - ttl: 360 + ttl: 360, + nurl: undefined } ], cookieSync: { url: '' } @@ -416,4 +420,33 @@ describe('Seedtag Adapter', function() { ) }) }) + + describe('onBidWon', function () { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel') + }) + + afterEach(function() { + utils.triggerPixel.restore() + }) + + describe('without nurl', function() { + const bid = {} + + it('does not create pixel ', function() { + spec.onBidWon(bid) + expect(utils.triggerPixel.called).to.equal(false); + }) + }) + + describe('with nurl', function () { + const nurl = 'http://seedtag_domain/won' + const bid = { nurl } + + it('creates nurl pixel if bid nurl', function() { + spec.onBidWon({ nurl }) + expect(utils.triggerPixel.calledWith(nurl)).to.equal(true); + }) + }) + }) }) diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index cd9071a6098..b3451a09dde 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as utils from '../../../src/utils.js'; const spec = newBidder(sharethroughAdapterSpec).getSpec(); const bidRequests = [ @@ -15,7 +16,20 @@ const bidRequests = [ userId: { tdid: 'fake-tdid', pubcid: 'fake-pubcid', - idl_env: 'fake-identity-link' + idl_env: 'fake-identity-link', + id5id: { + uid: 'fake-id5id', + ext: { + linkType: 2 + } + }, + sharedid: { + id: 'fake-sharedid', + third: 'fake-sharedthird' + }, + lipb: { + lipbid: 'fake-lipbid' + } }, crumbs: { pubcid: 'fake-pubcid-in-crumbs-obj' @@ -329,6 +343,15 @@ describe('sharethrough adapter spec', function() { expect(bidRequest.data.pubcid).to.eq('fake-pubcid'); }); + it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + + ' crumbs object of the bidrequest', function() { + const bidData = utils.deepClone(bidRequests); + delete bidData[0].userId.pubcid; + + const bidRequest = spec.buildRequests(bidData)[0]; + expect(bidRequest.data.pubcid).to.eq('fake-pubcid-in-crumbs-obj'); + }); + it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + ' crumbs object of the bidrequest', function() { const bidRequest = spec.buildRequests(bidRequests)[0]; @@ -341,6 +364,23 @@ describe('sharethrough adapter spec', function() { expect(bidRequest.data.idluid).to.eq('fake-identity-link'); }); + it('should add the id5uid parameter if a bid request contains a value for ID5', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.id5uid.id).to.eq('fake-id5id'); + expect(bidRequest.data.id5uid.linkType).to.eq(2); + }); + + it('should add the shduid parameter if a bid request contains a value for Shared ID', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.shduid.id).to.eq('fake-sharedid'); + expect(bidRequest.data.shduid.third).to.eq('fake-sharedthird'); + }); + + it('should add the liuid parameter if a bid request contains a value for LiveIntent ID', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.liuid).to.eq('fake-lipbid'); + }); + it('should add Sharethrough specific parameters', function() { const builtBidRequests = spec.buildRequests(bidRequests); expect(builtBidRequests[0]).to.deep.include({ diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 6af0a855800..1bc77fc9572 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -42,7 +42,7 @@ const ADTYPE_IMG = 'Img'; const ADTYPE_RICHMEDIA = 'Richmedia'; const ADTYPE_VIDEO = 'Video'; -const context = { +const site = { keywords: 'power tools,drills' }; @@ -439,8 +439,8 @@ describe('smaatoBidAdapterTest', () => { it('sends fp data', () => { this.sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context, + ortb2: { + site, user } }; diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index e3bca240a47..749de43b9af 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -115,7 +115,41 @@ describe('Smart bid adapter tests', function () { ttl: 300, adUrl: 'http://awesome.fake.url', ad: '< --- awesome script --- >', - cSyncUrl: 'http://awesome.fake.csync.url' + cSyncUrl: 'http://awesome.fake.csync.url', + isNoAd: false + } + }; + + var BID_RESPONSE_IS_NO_AD = { + body: { + cpm: 12, + width: 300, + height: 250, + creativeId: 'zioeufg', + currency: 'GBP', + isNetCpm: true, + ttl: 300, + adUrl: 'http://awesome.fake.url', + ad: '< --- awesome script --- >', + cSyncUrl: 'http://awesome.fake.csync.url', + isNoAd: true + } + }; + + var BID_RESPONSE_IMAGE_SYNC = { + body: { + cpm: 12, + width: 300, + height: 250, + creativeId: 'zioeufg', + currency: 'GBP', + isNetCpm: true, + ttl: 300, + adUrl: 'http://awesome.fake.url', + ad: '< --- awesome script --- >', + cSyncUrl: 'http://awesome.fake.csync.url', + isNoAd: false, + dspPixels: ['pixelOne', 'pixelTwo', 'pixelThree'] } }; @@ -149,6 +183,18 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('ckid').and.to.equal(42); }); + it('Verify parse response with no ad', function () { + const request = spec.buildRequests(DEFAULT_PARAMS); + const bids = spec.interpretResponse(BID_RESPONSE_IS_NO_AD, request[0]); + expect(bids).to.have.lengthOf(0); + + expect(function () { + spec.interpretResponse(BID_RESPONSE_IS_NO_AD, { + data: 'invalid Json' + }) + }).to.not.throw(); + }); + it('Verify parse response', function () { const request = spec.buildRequests(DEFAULT_PARAMS); const bids = spec.interpretResponse(BID_RESPONSE, request[0]); @@ -258,6 +304,27 @@ describe('Smart bid adapter tests', function () { expect(syncs).to.have.lengthOf(0); }); + it('Verifies user sync using dspPixels', function () { + var syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE_IMAGE_SYNC]); + expect(syncs).to.have.lengthOf(3); + expect(syncs[0].type).to.equal('image'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE_IMAGE_SYNC]); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, []); + expect(syncs).to.have.lengthOf(0); + }); + describe('gdpr tests', function () { afterEach(function () { config.resetConfig(); diff --git a/test/spec/modules/smartrtbBidAdapter_spec.js b/test/spec/modules/smartrtbBidAdapter_spec.js index cb5ceee0870..a7f30bdec6e 100644 --- a/test/spec/modules/smartrtbBidAdapter_spec.js +++ b/test/spec/modules/smartrtbBidAdapter_spec.js @@ -69,9 +69,6 @@ describe('SmartRTBBidAdapter', function () { it('should return a bidder code of smartrtb', function () { expect(spec.code).to.equal('smartrtb') }) - it('should alias smrtb', function () { - expect(spec.aliases.length > 0 && spec.aliases[0] === 'smrtb').to.be.true - }) }) describe('isBidRequestValid', function () { @@ -79,8 +76,8 @@ describe('SmartRTBBidAdapter', function () { expect(spec.isBidRequestValid(bannerRequest)).to.be.true }) - it('should return false if any zone id missing', function () { - expect(spec.isBidRequestValid(Object.assign(bannerRequest, { params: { zoneId: null } }))).to.be.false + it('should return false if any zone id and pub id missing', function () { + expect(spec.isBidRequestValid(Object.assign(bannerRequest, { params: { pubId: null, zoneId: null } }))).to.be.false }) }) diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index 82c6642bd74..abc06b48ce7 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -421,6 +421,8 @@ describe('The smartx adapter', function () { it('should return an array of bid responses', function () { var responses = spec.interpretResponse(serverResponse, bidderRequestObj); expect(responses).to.be.an('array').with.length(2); + expect(bidderRequestObj).to.be.an('Object'); + expect(bidderRequestObj.bidRequest.bids).to.be.an('array').with.length(2); expect(responses[0].requestId).to.equal(123); expect(responses[0].currency).to.equal('USD'); expect(responses[0].cpm).to.equal(12); @@ -505,7 +507,7 @@ describe('The smartx adapter', function () { responses[0].renderer.render(responses[0]); expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); - expect(scriptTag.getAttribute('src')).to.equal('https://dco.smartclip.net/?plc=7777777'); + expect(scriptTag.getAttribute('src')).to.equal('https://dco.smartclip.net/?plc=7777778'); window.document.getElementById.restore(); }); diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 2780e88255d..8804050134a 100644 --- a/test/spec/modules/smartyadsBidAdapter_spec.js +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -1,5 +1,6 @@ import {expect} from 'chai'; import {spec} from '../../../modules/smartyadsBidAdapter.js'; +import { config } from '../../../src/config.js'; describe('SmartyadsAdapter', function () { let bid = { @@ -38,9 +39,10 @@ describe('SmartyadsAdapter', function () { it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'coppa'); expect(data.deviceWidth).to.be.a('number'); expect(data.deviceHeight).to.be.a('number'); + expect(data.coppa).to.be.a('number'); expect(data.language).to.be.a('string'); expect(data.secure).to.be.within(0, 1); expect(data.host).to.be.a('string'); @@ -57,6 +59,23 @@ describe('SmartyadsAdapter', function () { expect(data.placements).to.be.an('array').that.is.empty; }); }); + + describe('with COPPA', function() { + beforeEach(function() { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + }); + afterEach(function() { + config.getConfig.restore(); + }); + + it('should send the Coppa "required" flag set to "1" in the request', function () { + let serverRequest = spec.buildRequests([bid]); + expect(serverRequest.data.coppa).to.equal(1); + }); + }); + describe('interpretResponse', function () { it('Should interpret banner response', function () { const banner = { diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 52821072a21..d1ac200394c 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -414,15 +414,92 @@ describe('SonobiBidAdapter', function () { expect(JSON.parse(bidRequests.data.schain)).to.deep.equal(bidRequest[0].schain) }); + it('should return a properly formatted request with eids as a JSON-encoded set of eids', function () { + bidRequest[0].userIdAsEids = [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '97b1ff9b-6bf1-41fc-95de-acfd33dbb95a', + 'atype': 1 + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01ERJ6W40EXJZNQJVJZWASEG7J', + 'atype': 1, + 'ext': { + 'third': '01ERJ6W40EXJZNQJVJZWASEG7J' + } + } + ] + } + ]; + bidRequest[1].userIdAsEids = [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '97b1ff9b-6bf1-41fc-95de-acfd33dbb95a', + 'atype': 1 + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01ERJ6W40EXJZNQJVJZWASEG7J', + 'atype': 1, + 'ext': { + 'third': '01ERJ6W40EXJZNQJVJZWASEG7J' + } + } + ] + } + ]; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(JSON.parse(bidRequests.data.eids)).to.eql([ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '97b1ff9b-6bf1-41fc-95de-acfd33dbb95a', + 'atype': 1 + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01ERJ6W40EXJZNQJVJZWASEG7J', + 'atype': 1, + 'ext': { + 'third': '01ERJ6W40EXJZNQJVJZWASEG7J' + } + } + ] + } + ]); + }); + it('should return a properly formatted request with userid as a JSON-encoded set of User ID results', function () { - bidRequest[0].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}; - bidRequest[1].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}; + bidRequest[0].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101', 'id5id': {'uid': 'ID5-ZHMOrVeUVTUKgrZ-a2YGxeh5eS_pLzHCQGYOEAiTBQ', 'ext': {'linkType': 2}}}; + bidRequest[1].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101', 'id5id': {'uid': 'ID5-ZHMOrVeUVTUKgrZ-a2YGxeh5eS_pLzHCQGYOEAiTBQ', 'ext': {'linkType': 2}}}; const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); expect(bidRequests.method).to.equal('GET'); expect(bidRequests.data.ref).not.to.be.empty; expect(bidRequests.data.s).not.to.be.empty; - expect(JSON.parse(bidRequests.data.userid)).to.eql({'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}); + expect(JSON.parse(bidRequests.data.userid)).to.eql({'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101', 'id5id': 'ID5-ZHMOrVeUVTUKgrZ-a2YGxeh5eS_pLzHCQGYOEAiTBQ'}); }); it('should return a properly formatted request with userid omitted if there are no userIds', function () { @@ -469,7 +546,7 @@ describe('SonobiBidAdapter', function () { ]; const bidRequests = spec.buildRequests(bRequest, bidderRequests); expect(bidRequests.url).to.equal('https://iad-2-apex.go.sonobi.com/trinity.json'); - }) + }); }); describe('.interpretResponse', function () { diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 927599ac986..873914441aa 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -379,6 +379,32 @@ describe('the spotx adapter', function () { expect(request.data.site.page).to.equal('prebid.js'); }); + + it('should set ext.wrap_response to 0 when cache url is set and ignoreBidderCacheKey is true', function() { + var request; + + var origGetConfig = config.getConfig; + sinon.stub(config, 'getConfig').callsFake(function (key) { + if (key === 'cache') { + return { + url: 'prebidCacheLocation', + ignoreBidderCacheKey: true + }; + } + if (key === 'cache.url') { + return 'prebidCacheLocation'; + } + if (key === 'cache.ignoreBidderCacheKey') { + return true; + } + return origGetConfig.apply(config, arguments); + }); + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.ext.wrap_response).to.equal(0); + config.getConfig.restore(); + }); }); describe('interpretResponse', function() { @@ -469,7 +495,6 @@ describe('the spotx adapter', function () { expect(responses[0].requestId).to.equal(123); expect(responses[0].ttl).to.equal(360); expect(responses[0].vastUrl).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache123'); - expect(responses[0].videoCacheKey).to.equal('cache123'); expect(responses[0].width).to.equal(400); expect(responses[1].cache_key).to.equal('cache124'); expect(responses[1].channel_id).to.equal(12345); @@ -483,7 +508,6 @@ describe('the spotx adapter', function () { expect(responses[1].requestId).to.equal(124); expect(responses[1].ttl).to.equal(360); expect(responses[1].vastUrl).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache124'); - expect(responses[1].videoCacheKey).to.equal('cache124'); expect(responses[1].width).to.equal(200); }); }); diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js index a0765a0d396..2e1d65f0533 100644 --- a/test/spec/modules/sublimeBidAdapter_spec.js +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -18,6 +18,9 @@ describe('Sublime Adapter', function() { 'puid', 'trId', 'pbav', + 'pubpbv', + 'device', + 'pubtimeout', ]; beforeEach(function () { @@ -139,6 +142,7 @@ describe('Sublime Adapter', function() { describe('interpretResponse', function() { let serverResponse = { 'request_id': '3db3773286ee59', + 'sspname': 'foo', 'cpm': 0.5, 'ad': '', }; @@ -160,9 +164,10 @@ describe('Sublime Adapter', function() { creativeId: 1, dealId: 1, currency: 'USD', + sspname: 'foo', netRevenue: true, ttl: 600, - pbav: '0.7.0', + pbav: '0.7.1', ad: '', }, ]; @@ -173,6 +178,7 @@ describe('Sublime Adapter', function() { it('should get correct default size for 1x1', function() { let serverResponse = { 'requestId': 'xyz654_2', + 'sspname': 'sublime', 'cpm': 0.5, 'ad': '', }; @@ -204,7 +210,8 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.0', + pbav: '0.7.1', + sspname: 'sublime' }; expect(result[0]).to.deep.equal(expectedResponse); @@ -224,6 +231,7 @@ describe('Sublime Adapter', function() { it('should return bid with default value in response', function () { let serverResponse = { 'requestId': 'xyz654_2', + 'sspname': 'sublime', 'ad': '', }; @@ -251,10 +259,11 @@ describe('Sublime Adapter', function() { creativeId: 1, dealId: 1, currency: 'EUR', + sspname: 'sublime', netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.0', + pbav: '0.7.1', }; expect(result[0]).to.deep.equal(expectedResponse); diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js new file mode 100644 index 00000000000..c4410d8ce5e --- /dev/null +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -0,0 +1,86 @@ +import { assert } from 'chai'; +import {spec} from 'modules/tappxBidAdapter'; + +describe('Tappx adapter tests', function () { + describe('isBidRequestValid', function () { + let bid = { bidder: 'tappx', params: { host: 'testing.ssp.tappx.com', tappxkey: 'pub-1234-test-1234', endpoint: 'ZZ1234PBJS', bidfloor: 0.005 } }; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when required params are missing', function () { + const bid = { + host: 'testing.ssp.tappx.com' + }; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequest', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + // Web Test + let validBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 480]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] + // App Test + let validAppBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5], 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 50]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 50]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] + let bidderRequest = {'bidderCode': 'tappx', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'bidderRequestId': '1ae5c6a02684df', 'bids': [{'bidder': 'tappx', 'params': {'host': 'tests.tappx.com', 'tappxkey': 'pub-1234-test-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'banner-ad-div', 'transactionId': 'c44cdbde-ab6d-47a0-8dde-6b4ff7909a35', 'sizes': [[320, 50]], 'bidId': '2e3a5feb30cfe4', 'bidderRequestId': '1ae5c6a02684df', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1611308859094, 'timeout': 700, 'refererInfo': {'referer': 'http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': consentString, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; + + it('should add gdpr/usp consent information to the request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request[0].data); + + expect(payload.regs.gdpr).to.exist.and.to.be.true; + expect(payload.regs.consent).to.exist.and.to.equal(consentString); + expect(payload.regs.ext.us_privacy).to.exist; + }); + + it('should properly build a banner request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); + expect(request[0].method).to.equal('POST'); + + const data = JSON.parse(request[0].data); + expect(data.site).to.not.equal(null); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor, data).to.not.be.null; + expect(data.imp[0].banner).to.not.equal(null); + expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); + expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); + }); + + it('should properly build a banner request with app params', function () { + const request = spec.buildRequests(validAppBidRequests, bidderRequest); + expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); + expect(request[0].method).to.equal('POST'); + + const data = JSON.parse(request[0].data); + expect(data.site).to.not.equal(null); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor, data).to.not.be.null; + expect(data.imp[0].banner).to.not.equal(null); + expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); + expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); + }); + }); + + describe('interpretResponse', function () { + const bidRequest = { + data: {}, + bids: [ {'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.05, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '47dd44e8-e7db-417c-a8f1-621a2e1a117d', 'sizes': [[320, 480]], 'bidId': '2170932097e505', 'bidderRequestId': '140ba7a1ab7aeb', 'auctionId': '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0} ] + }; + + const serverResponse = {'body': {'id': '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', 'bidid': 'bid3811165568213389257', 'seatbid': [{'seat': '1', 'group': 0, 'bid': [{'id': '3811165568213389257', 'impid': 1, 'price': 0.05, 'adm': "\t", 'w': 320, 'h': 480, 'lurl': 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', 'cid': '01744fbb521e9fb10ffea926190effea', 'crid': 'a13cf884e66e7c660afec059c89d98b6', 'adomain': []}]}], 'cur': 'USD'}, 'headers': {}}; + it('receive reponse with single placement', function () { + const bids = spec.interpretResponse(serverResponse, bidRequest); + const bid = bids[0]; + expect(bid.cpm).to.exist; + expect(bid.ad).to.match(/^