From 2875a4d9752c9b3ae72c38b8288f8d68b388e463 Mon Sep 17 00:00:00 2001 From: Index Exchange 3 Prebid Team Date: Tue, 25 Jun 2019 13:37:06 -0400 Subject: [PATCH] Prebid.js Video (#3901) * Implemented changes required to provide support for video in the IX bidding adapter for Instream and Outstream contexts. * Implemented changes required to provide support for video in the IX bidding adapter for Instream and Outstream contexts. * fix find module --- modules/ixBidAdapter.js | 366 ++++++++++++++++--------- modules/ixBidAdapter.md | 128 ++++++++- test/spec/modules/ixBidAdapter_spec.js | 344 ++++++++++++++++++----- 3 files changed, 628 insertions(+), 210 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 8d76d8626550..64dfc9e8040f 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -1,37 +1,74 @@ import * as utils from '../src/utils'; -import { BANNER } from '../src/mediaTypes'; +import { BANNER, VIDEO } from '../src/mediaTypes'; +import find from 'core-js/library/fn/array/find'; import { config } from '../src/config'; import isInteger from 'core-js/library/fn/number/is-integer'; import { registerBidder } from '../src/adapters/bidderFactory'; const BIDDER_CODE = 'ix'; -const BANNER_SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus'; -const BANNER_INSECURE_BID_URL = 'http://as.casalemedia.com/cygnus'; -const SUPPORTED_AD_TYPES = [BANNER]; -const ENDPOINT_VERSION = 7.2; +const SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus'; +const INSECURE_BID_URL = 'http://as.casalemedia.com/cygnus'; +const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; +const BANNER_ENDPOINT_VERSION = 7.2; +const VIDEO_ENDPOINT_VERSION = 8.1; + const CENT_TO_DOLLAR_FACTOR = 100; -const TIME_TO_LIVE = 35; +const BANNER_TIME_TO_LIVE = 35; +const VIDEO_TIME_TO_LIVE = 3600; // 1hr const NET_REVENUE = true; const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 }; /** - * Transform valid bid request config object to impression object that will be sent to ad server. + * Transform valid bid request config object to banner impression object that will be sent to ad server. * * @param {object} bid A valid bid request config object. * @return {object} A impression object that will be sent to ad server. */ function bidToBannerImp(bid) { - const imp = {}; - - imp.id = bid.bidId; + const imp = bidToImp(bid); imp.banner = {}; imp.banner.w = bid.params.size[0]; imp.banner.h = bid.params.size[1]; imp.banner.topframe = utils.inIframe() ? 0 : 1; + return imp; +} + +/** + * Transform valid bid request config object to video impression object that will be sent to ad server. + * + * @param {object} bid A valid bid request config object. + * @return {object} A impression object that will be sent to ad server. + */ +function bidToVideoImp(bid) { + const imp = bidToImp(bid); + + imp.video = utils.deepClone(bid.params.video) + imp.video.w = bid.params.size[0]; + imp.video.h = bid.params.size[1]; + + const context = utils.deepAccess(bid, 'mediaTypes.video.context'); + if (context) { + if (context === 'instream') { + imp.video.placement = 1; + } else if (context === 'outstream') { + imp.video.placement = 4; + } else { + utils.logWarn(`ix bidder params: video context '${context}' is not supported`); + } + } + + return imp; +} + +function bidToImp(bid) { + const imp = {}; + + imp.id = bid.bidId; + imp.ext = {}; imp.ext.siteID = bid.params.siteId; @@ -57,7 +94,7 @@ function bidToBannerImp(bid) { * @param {string} currency Global currency in bid response. * @return {object} bid The parsed bid. */ -function parseBid(rawBid, currency) { +function parseBid(rawBid, currency, bidRequest) { const bid = {}; if (PRICE_TO_DOLLAR_FACTOR.hasOwnProperty(currency)) { @@ -67,15 +104,27 @@ function parseBid(rawBid, currency) { } bid.requestId = rawBid.impid; - bid.width = rawBid.w; - bid.height = rawBid.h; - bid.ad = rawBid.adm; + bid.dealId = utils.deepAccess(rawBid, 'ext.dealid'); - bid.ttl = TIME_TO_LIVE; bid.netRevenue = NET_REVENUE; bid.currency = currency; bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-'; + // in the event of a video + if (utils.deepAccess(rawBid, 'ext.vasturl')) { + bid.vastUrl = rawBid.ext.vasturl + bid.width = bidRequest.video.w; + bid.height = bidRequest.video.h; + bid.mediaType = VIDEO; + bid.ttl = VIDEO_TIME_TO_LIVE; + } else { + bid.ad = rawBid.adm; + bid.width = rawBid.w; + bid.height = rawBid.h; + bid.mediaType = BANNER; + bid.ttl = BANNER_TIME_TO_LIVE; + } + bid.meta = {}; bid.meta.networkId = utils.deepAccess(rawBid, 'ext.dspid'); bid.meta.brandId = utils.deepAccess(rawBid, 'ext.advbrandid'); @@ -132,6 +181,143 @@ function isValidBidFloorParams(bidFloor, bidFloorCur) { bidFloorCur.match(curRegex)); } +/** + * Finds the impression with the associated id. + * + * @param {*} id Id of the impression. + * @param {array} impressions List of impressions sent in the request. + * @return {object} The impression with the associated id. + */ +function getBidRequest(id, impressions) { + if (!id) { + return; + } + return find(impressions, imp => imp.id === id); +} + +/** + * Builds a request object to be sent to the ad server based on bid requests. + * + * @param {array} validBidRequests A list of valid bid request config objects. + * @param {object} bidderRequest An object containing other info like gdprConsent. + * @param {array} impressions List of impression objects describing the bids. + * @param {array} version Endpoint version denoting banner or video. + * @return {object} Info describing the request to the server. + * + */ +function buildRequest(validBidRequests, bidderRequest, impressions, version) { + const userEids = []; + + // Always start by assuming the protocol is HTTPS. This way, it will work + // whether the page protocol is HTTP or HTTPS. Then check if the page is + // actually HTTP.If we can guarantee it is, then, and only then, set protocol to + // HTTP. + let baseUrl = SECURE_BID_URL; + + // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded + // and if the data for the partner exist + if (window.headertag && typeof window.headertag.getIdentityInfo === 'function') { + let identityInfo = window.headertag.getIdentityInfo(); + if (identityInfo && typeof identityInfo === 'object') { + for (const partnerName in identityInfo) { + if (identityInfo.hasOwnProperty(partnerName)) { + let response = identityInfo[partnerName]; + if (!response.responsePending && response.data && typeof response.data === 'object' && Object.keys(response.data).length) { + userEids.push(response.data); + } + } + } + } + } + const r = {}; + + // Since bidderRequestId are the same for different bid request, just use the first one. + r.id = validBidRequests[0].bidderRequestId; + + r.imp = impressions; + + r.site = {}; + r.ext = {}; + r.ext.source = 'prebid'; + if (userEids.length > 0) { + r.user = {}; + r.user.eids = userEids; + } + + if (document.referrer && document.referrer !== '') { + r.site.ref = document.referrer; + } + + // Apply GDPR information to the request if GDPR is enabled. + if (bidderRequest) { + if (bidderRequest.gdprConsent) { + const gdprConsent = bidderRequest.gdprConsent; + + if (gdprConsent.hasOwnProperty('gdprApplies')) { + r.regs = { + ext: { + gdpr: gdprConsent.gdprApplies ? 1 : 0 + } + }; + } + + if (gdprConsent.hasOwnProperty('consentString')) { + r.user = r.user || {}; + r.user.ext = { + consent: gdprConsent.consentString || '' + }; + } + } + + if (bidderRequest.refererInfo) { + r.site.page = bidderRequest.refererInfo.referer; + + if (bidderRequest.refererInfo.referer && bidderRequest.refererInfo.referer.indexOf('https') !== 0) { + baseUrl = INSECURE_BID_URL; + } + } + } + + const payload = {}; + + // Parse additional runtime configs. + const otherIxConfig = config.getConfig('ix'); + if (otherIxConfig) { + // Append firstPartyData to r.site.page if firstPartyData exists. + if (typeof otherIxConfig.firstPartyData === 'object') { + const firstPartyData = otherIxConfig.firstPartyData; + let firstPartyString = '?'; + for (const key in firstPartyData) { + if (firstPartyData.hasOwnProperty(key)) { + firstPartyString += `${encodeURIComponent(key)}=${encodeURIComponent(firstPartyData[key])}&`; + } + } + firstPartyString = firstPartyString.slice(0, -1); + + r.site.page += firstPartyString; + } + + // Create t in payload if timeout is configured. + if (typeof otherIxConfig.timeout === 'number') { + payload.t = otherIxConfig.timeout; + } + } + + // Use the siteId in the first bid request as the main siteId. + payload.s = validBidRequests[0].params.siteId; + payload.v = version; + payload.r = JSON.stringify(r); + payload.ac = 'j'; + payload.sd = 1; + payload.nf = 1; + + return { + method: 'GET', + url: baseUrl, + data: payload + }; +} + export const spec = { code: BIDDER_CODE, @@ -145,22 +331,25 @@ export const spec = { */ isBidRequestValid: function (bid) { if (!isValidSize(bid.params.size)) { + utils.logError('ix bidder params: bid size has invalid format.'); return false; } if (!includesSize(bid.sizes, bid.params.size)) { + utils.logError('ix bidder params: bid size is not included in ad unit sizes.'); return false; } - if (bid.hasOwnProperty('mediaType') && bid.mediaType !== 'banner') { + if (bid.hasOwnProperty('mediaType') && !(utils.contains(SUPPORTED_AD_TYPES, bid.mediaType))) { return false; } - if (bid.hasOwnProperty('mediaTypes') && !utils.deepAccess(bid, 'mediaTypes.banner.sizes')) { + if (bid.hasOwnProperty('mediaTypes') && !(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || utils.deepAccess(bid, 'mediaTypes.video.playerSize'))) { return false; } if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') { + utils.logError('ix bidder params: siteId must be string or number value.'); return false; } @@ -168,8 +357,10 @@ export const spec = { const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur'); if (hasBidFloor || hasBidFloorCur) { - return hasBidFloor && hasBidFloorCur && - isValidBidFloorParams(bid.params.bidFloor, bid.params.bidFloorCur); + if (!(hasBidFloor && hasBidFloorCur && isValidBidFloorParams(bid.params.bidFloor, bid.params.bidFloorCur))) { + utils.logError('ix bidder params: bidFloor / bidFloorCur parameter has invalid format.'); + return false; + } } return true; @@ -179,139 +370,49 @@ export const spec = { * Make a server request from the list of BidRequests. * * @param {array} validBidRequests A list of valid bid request config objects. - * @param {object} options A object contains bids and other info like gdprConsent. + * @param {object} bidderRequest A object contains bids and other info like gdprConsent. * @return {object} Info describing the request to the server. */ - buildRequests: function (validBidRequests, options) { - const bannerImps = []; - const userEids = []; + buildRequests: function (validBidRequests, bidderRequest) { + let reqs = []; + let bannerImps = []; + let videoImps = []; let validBidRequest = null; - let bannerImp = null; - - // Always start by assuming the protocol is HTTPS. This way, it will work - // whether the page protocol is HTTP or HTTPS. Then check if the page is - // actually HTTP.If we can guarantee it is, then, and only then, set protocol to - // HTTP. - let baseUrl = BANNER_SECURE_BID_URL; for (let i = 0; i < validBidRequests.length; i++) { validBidRequest = validBidRequests[i]; - // Transform the bid request based on the banner format. - bannerImp = bidToBannerImp(validBidRequest); - bannerImps.push(bannerImp); - } - - // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded - // and if the data for the partner exist - if (window.headertag && typeof window.headertag.getIdentityInfo === 'function') { - let identityInfo = window.headertag.getIdentityInfo(); - if (identityInfo && typeof identityInfo === 'object') { - for (const partnerName in identityInfo) { - if (identityInfo.hasOwnProperty(partnerName)) { - let response = identityInfo[partnerName]; - if (!response.responsePending && response.data && typeof response.data === 'object' && Object.keys(response.data).length) { - userEids.push(response.data); - } - } - } - } - } - const r = {}; - - // Since bidderRequestId are the same for different bid request, just use the first one. - r.id = validBidRequests[0].bidderRequestId; - - r.imp = bannerImps; - r.site = {}; - r.ext = {}; - r.ext.source = 'prebid'; - if (userEids.length > 0) { - r.user = {}; - r.user.eids = userEids; - } - - if (document.referrer && document.referrer !== '') { - r.site.ref = document.referrer; - } - - // Apply GDPR information to the request if GDPR is enabled. - if (options) { - if (options.gdprConsent) { - const gdprConsent = options.gdprConsent; - - if (gdprConsent.hasOwnProperty('gdprApplies')) { - r.regs = { - ext: { - gdpr: gdprConsent.gdprApplies ? 1 : 0 - } - }; - } - - if (gdprConsent.hasOwnProperty('consentString')) { - r.user = r.user || {}; - r.user.ext = { - consent: gdprConsent.consentString || '' - }; + if (validBidRequest.mediaType === VIDEO || utils.deepAccess(validBidRequest, 'mediaTypes.video')) { + if (validBidRequest.mediaType === VIDEO || includesSize(validBidRequest.mediaTypes.video.playerSize, validBidRequest.params.size)) { + videoImps.push(bidToVideoImp(validBidRequest)); + } else { + utils.logError('Bid size is not included in video playerSize') } } - - if (options.refererInfo) { - r.site.page = options.refererInfo.referer; - - if (options.refererInfo.referer && options.refererInfo.referer.indexOf('https') !== 0) { - baseUrl = BANNER_INSECURE_BID_URL; - } + if (validBidRequest.mediaType === BANNER || utils.deepAccess(validBidRequest, 'mediaTypes.banner') || + (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { + bannerImps.push(bidToBannerImp(validBidRequest)); } } - const payload = {}; - - // Parse additional runtime configs. - const otherIxConfig = config.getConfig('ix'); - if (otherIxConfig) { - // Append firstPartyData to r.site.page if firstPartyData exists. - if (typeof otherIxConfig.firstPartyData === 'object') { - const firstPartyData = otherIxConfig.firstPartyData; - let firstPartyString = '?'; - for (const key in firstPartyData) { - if (firstPartyData.hasOwnProperty(key)) { - firstPartyString += `${encodeURIComponent(key)}=${encodeURIComponent(firstPartyData[key])}&`; - } - } - firstPartyString = firstPartyString.slice(0, -1); - - r.site.page += firstPartyString; - } - - // Create t in payload if timeout is configured. - if (typeof otherIxConfig.timeout === 'number') { - payload.t = otherIxConfig.timeout; - } + if (bannerImps.length > 0) { + reqs.push(buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); + } + if (videoImps.length > 0) { + reqs.push(buildRequest(validBidRequests, bidderRequest, videoImps, VIDEO_ENDPOINT_VERSION)); } - // Use the siteId in the first bid request as the main siteId. - payload.s = validBidRequests[0].params.siteId; - - payload.v = ENDPOINT_VERSION; - payload.r = JSON.stringify(r); - payload.ac = 'j'; - payload.sd = 1; - - return { - method: 'GET', - url: baseUrl, - data: payload - }; + return reqs; }, /** * Unpack the response from the server into a list of bids. * * @param {object} serverResponse A successful response from the server. + * @param {object} bidderRequest The bid request sent to the server. * @return {array} An array of bids which were nested inside the server. */ - interpretResponse: function (serverResponse) { + interpretResponse: function (serverResponse, bidderRequest) { const bids = []; let bid = null; @@ -328,8 +429,11 @@ export const spec = { // Transform rawBid in bid response to the format that will be accepted by prebid. const innerBids = seatbid[i].bid; + let requestBid = JSON.parse(bidderRequest.data.r); + for (let j = 0; j < innerBids.length; j++) { - bid = parseBid(innerBids[j], responseBody.cur); + const bidRequest = getBidRequest(innerBids[j].impid, requestBid.imp); + bid = parseBid(innerBids[j], responseBody.cur, bidRequest); bids.push(bid); } } diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index e99c42408f2a..7bd60ac413b0 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -42,16 +42,21 @@ var adUnits = [{ ```javascript var adUnits = [{ // ... - mediaTypes: { banner: { sizes: [ [300, 250], [300, 600] ] + }, + video: { + context: 'instream', + playerSize: [ + [300, 250], + [300, 600] + ] } - } - + }, // ... }]; ``` @@ -61,7 +66,7 @@ var adUnits = [{ | Type | Support | --- | --- | Banner | Fully supported for all IX approved sizes. -| Video | Not supported. +| Video | Fully supported for all IX approved sizes. | Native | Not supported. # Bid Parameters @@ -76,6 +81,17 @@ object are detailed here. | siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` | size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.banner.sizes`. Examples: `[300, 250]`, `[300, 600]`, `[728, 90]` +### Video + +| Key | Scope | Type | Description +| --- | --- | --- | --- +| siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` +| size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.video.playerSize`. Examples: `[300, 250]`, `[300, 600]` +| video | Required | Hash | The video object will serve as the properties of the video ad. You can create any field under the video object that is mentioned in the `OpenRTB Spec v2.5`. Some fields like `mimes, protocols, minduration, maxduration` are required. +| video.mimes | Required | String[] | Array list of content MIME types supported. Popular MIME types include, but are not limited to, `"video/x-ms- wmv"` for Windows Media and `"video/x-flv"` for Flash Video. +|video.minduration| Required | Integer | Minimum video ad duration in seconds. +|video.maxduration| Required | Integer | Maximum video ad duration in seconds. +|video.protocol / video.protocols| Required | Integer / Integer[] | Either a single protocol provided as an integer, or protocols provided as a list of integers. `2` - VAST 2.0, `3` - VAST 3.0, `5` - VAST 2.0 Wrapper, `6` - VAST 3.0 Wrapper Setup Guide @@ -84,7 +100,9 @@ Setup Guide Follow these steps to configure and add the IX module to your Prebid.js integration. -The examples in this guide assume the following starting configuration: +The examples in this guide assume the following starting configuration (you may remove banner or video, if either does not apply). + +In regards to video, `context` can either be `'instream'` or `'outstream'`. Note that `outstream` requires additional configuration on the adUnit. ```javascript var adUnits = [{ @@ -98,6 +116,19 @@ var adUnits = [{ } }, bids: [] +}, +{ + code: 'video-div-a', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [300, 250], + [300, 600] + ] + } + }, + bids: [] }]; ``` @@ -119,7 +150,9 @@ bid objects under `adUnits[].bids`: Set `params.siteId` and `params.size` in each bid object to the values provided by your IX representative. -**Example** +**Examples** + +**Banner:** ```javascript var adUnits = [{ code: 'banner-div-a', @@ -146,18 +179,94 @@ var adUnits = [{ }] }]; ``` - +**Video (Instream):** +```javascript +var adUnits = [{ + code: 'video-div-a', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [300, 250], + [300, 600] + ] + } + }, + bids: [{ + bidder: 'ix', + params: { + siteId: '12345', + size: [300, 250], + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [6] + } + } + }, { + bidder: 'ix', + params: { + siteId: '12345', + size: [300, 600], + video: { + // openrtb v2.5 compatible video obj + } + } + }] +}]; +``` Please note that you can re-use the existing `siteId` within the same flex position. +**Video (Outstream):** +Note that currently, outstream video rendering must be configured by the publisher. In the adUnit, a `renderer` object must be defined, which includes a `url` pointing to the video rendering script, and a `render` function for creating the video player. See http://prebid.org/dev-docs/show-outstream-video-ads.html for more information. +```javascript +var adUnits = [{ + code: 'video-div-a', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[300, 250]] + } + }, + renderer: { + url: 'https://test.com/my-video-player.js', + render: function (bid) { + ... + } + }, + bids: [{ + bidder: 'ix', + params: { + siteId: '12345', + size: [300, 250], + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [6] + } + } + }] +}]; +``` ##### 2. Include `ixBidAdapter` in your build process -When running the build command, include `ixBidAdapter` as a module. +When running the build command, include `ixBidAdapter` as a module, as well as `dfpAdServerVideo` if you require video support. ``` -gulp build --modules=ixBidAdapter,fooBidAdapter,bazBidAdapter +gulp build --modules=ixBidAdapter,dfpAdServerVideo,fooBidAdapter,bazBidAdapter ``` If a JSON file is being used to specify the bidder modules, add `"ixBidAdapter"` @@ -166,6 +275,7 @@ to the top-level array in that file. ```json [ "ixBidAdapter", + "dfpAdServerVideo", "fooBidAdapter", "bazBidAdapter" ] diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 38e64e8d3387..634d5041e6e0 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -7,7 +7,8 @@ import { spec } from 'modules/ixBidAdapter'; describe('IndexexchangeAdapter', function () { const IX_INSECURE_ENDPOINT = 'http://as.casalemedia.com/cygnus'; const IX_SECURE_ENDPOINT = 'https://as-sec.casalemedia.com/cygnus'; - const BIDDER_VERSION = 7.2; + const VIDEO_ENDPOINT_VERSION = 8.1; + const BANNER_ENDPOINT_VERSION = 7.2; const DEFAULT_BANNER_VALID_BID = [ { @@ -29,17 +30,37 @@ describe('IndexexchangeAdapter', function () { auctionId: '1aa2bb3cc4dd' } ]; - const DEFAULT_BANNER_OPTION = { - gdprConsent: { - gdprApplies: true, - consentString: '3huaa11=qu3198ae', - vendorData: {} - }, - refererInfo: { - referer: 'http://www.prebid.org', - canonicalUrl: 'http://www.prebid.org/the/link/to/the/page' + + const DEFAULT_VIDEO_VALID_BID = [ + { + bidder: 'ix', + params: { + siteId: '456', + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0 + }, + size: [400, 100] + }, + sizes: [[400, 100], [200, 400]], + mediaTypes: { + video: { + context: 'instream', + playerSize: [[400, 100], [200, 400]] + } + }, + adUnitCode: 'div-gpt-ad-1460505748562-0', + transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', + bidId: '1a2b3c4e', + bidderRequestId: '11a22b33c44e', + auctionId: '1aa2bb3cc4de' } - }; + ]; + const DEFAULT_BANNER_BID_RESPONSE = { cur: 'USD', id: '11a22b33c44d', @@ -69,6 +90,48 @@ describe('IndexexchangeAdapter', function () { } ] }; + + const DEFAULT_VIDEO_BID_RESPONSE = { + cur: 'USD', + id: '1aa2bb3cc4de', + seatbid: [ + { + bid: [ + { + crid: '12346', + adomain: ['www.abcd.com'], + adid: '14851456', + impid: '1a2b3c4e', + cid: '3051267', + price: 110, + id: '2', + ext: { + vasturl: 'www.abcd.com/vast', + errorurl: 'www.abcd.com/error', + dspid: 51, + pricelevel: '_110', + advbrandid: 303326, + advbrand: 'OECTB' + } + } + ], + seat: '3971' + } + ] + }; + + const DEFAULT_OPTION = { + gdprConsent: { + gdprApplies: true, + consentString: '3huaa11=qu3198ae', + vendorData: {} + }, + refererInfo: { + referer: 'http://www.prebid.org', + canonicalUrl: 'http://www.prebid.org/the/link/to/the/page' + } + }; + const DEFAULT_IDENTITY_RESPONSE = { IdentityIp: { responsePending: false, @@ -83,6 +146,31 @@ describe('IndexexchangeAdapter', function () { } }; + const DEFAULT_BIDDER_REQUEST_DATA = { + ac: 'j', + r: JSON.stringify({ + id: '345', + imp: [ + { + id: '1a2b3c4e', + video: { + w: 640, + h: 480, + placement: 1 + } + } + ], + site: { + ref: 'http://ref.com/ref.html', + page: 'http://page.com' + }, + }), + s: '21', + sd: 1, + t: 1000, + v: 8.1 + }; + describe('inherited functions', function () { it('should exists and is a function', function () { const adapter = newBidder(spec); @@ -91,11 +179,12 @@ describe('IndexexchangeAdapter', function () { }); describe('isBidRequestValid', function () { - it('should return true when required params found for a banner ad', function () { + it('should return true when required params found for a banner or video ad', function () { expect(spec.isBidRequestValid(DEFAULT_BANNER_VALID_BID[0])).to.equal(true); + expect(spec.isBidRequestValid(DEFAULT_VIDEO_VALID_BID[0])).to.equal(true); }); - it('should return true when optional params found for a banner ad', function () { + it('should return true when optional bidFloor params found for an ad', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; bid.params.bidFloorCur = 'USD'; @@ -136,10 +225,10 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when mediaTypes is not banner', function () { + it('should return false when mediaTypes is not banner or video', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.mediaTypes = { - video: { + native: { sizes: [[300, 250]] } }; @@ -156,19 +245,13 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when mediaType is not banner', function () { - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - delete bid.params.mediaTypes; - bid.mediaType = 'banne'; - bid.sizes = [[300, 250]]; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when mediaType is video', function () { - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - delete bid.params.mediaTypes; - bid.mediaType = 'video'; - bid.sizes = [[300, 250]]; + it('should return false when mediaTypes.video does not have sizes', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.mediaTypes = { + video: { + size: [[300, 250]] + } + }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -195,6 +278,14 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); + it('should return true when mediaType is video', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + delete bid.mediaTypes; + bid.mediaType = 'video'; + bid.sizes = [[400, 100]]; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return false when there is only bidFloor', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; @@ -232,7 +323,7 @@ describe('IndexexchangeAdapter', function () { window.headertag.getIdentityInfo = function() { return testCopy; }; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; }); afterEach(function() { @@ -343,7 +434,7 @@ describe('IndexexchangeAdapter', function () { window.headertag.getIdentityInfo = function() { return undefined; }; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -356,7 +447,7 @@ describe('IndexexchangeAdapter', function () { responsePending: true, data: {} } - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -366,7 +457,7 @@ describe('IndexexchangeAdapter', function () { it('payload should not have any user eids if identity data is pending for all partners', function () { testCopy.IdentityIp.responsePending = true; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -377,7 +468,7 @@ describe('IndexexchangeAdapter', function () { it('payload should not have any user eids if identity data is pending or not available for all partners', function () { testCopy.IdentityIp.responsePending = false; testCopy.IdentityIp.data = {}; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -387,8 +478,8 @@ describe('IndexexchangeAdapter', function () { }); }); - describe('buildRequestsBanner', function () { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + describe('buildRequests', function () { + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const requestUrl = request.url; const requestMethod = request.method; const query = request.data; @@ -396,7 +487,7 @@ describe('IndexexchangeAdapter', function () { const bidWithoutMediaType = utils.deepClone(DEFAULT_BANNER_VALID_BID); delete bidWithoutMediaType[0].mediaTypes; bidWithoutMediaType[0].sizes = [[300, 250], [300, 600]]; - const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType, DEFAULT_BANNER_OPTION); + const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType, DEFAULT_OPTION)[0]; const queryWithoutMediaType = requestWithoutMediaType.data; it('request should be made to IX endpoint with GET method', function () { @@ -405,11 +496,12 @@ describe('IndexexchangeAdapter', function () { }); it('query object (version, siteID and request) should be correct', function () { - expect(query.v).to.equal(BIDDER_VERSION); + expect(query.v).to.equal(BANNER_ENDPOINT_VERSION); expect(query.s).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId); expect(query.r).to.exist; expect(query.ac).to.equal('j'); expect(query.sd).to.equal(1); + expect(query.nf).to.equal(1); }); it('payload should have correct format and value', function () { @@ -417,7 +509,7 @@ describe('IndexexchangeAdapter', function () { expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); expect(payload.site).to.exist; - expect(payload.site.page).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); expect(payload.ext).to.exist; expect(payload.ext.source).to.equal('prebid'); @@ -445,7 +537,7 @@ describe('IndexexchangeAdapter', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; bid.params.bidFloorCur = 'USD'; - const requestBidFloor = spec.buildRequests([bid]); + const requestBidFloor = spec.buildRequests([bid])[0]; const impression = JSON.parse(requestBidFloor.data.r).imp[0]; expect(impression.bidfloor).to.equal(bid.params.bidFloor); @@ -457,7 +549,7 @@ describe('IndexexchangeAdapter', function () { expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); expect(payload.site).to.exist; - expect(payload.site.page).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); expect(payload.ext).to.exist; expect(payload.ext.source).to.equal('prebid'); @@ -484,7 +576,7 @@ describe('IndexexchangeAdapter', function () { it('impression should have sid if id is configured as number', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.id = 50; - const requestBidFloor = spec.buildRequests([bid]); + const requestBidFloor = spec.buildRequests([bid])[0]; const impression = JSON.parse(requestBidFloor.data.r).imp[0]; expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); @@ -501,7 +593,7 @@ describe('IndexexchangeAdapter', function () { it('impression should have sid if id is configured as string', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.id = 'abc'; - const requestBidFloor = spec.buildRequests([bid]); + const requestBidFloor = spec.buildRequests([bid])[0]; const impression = JSON.parse(requestBidFloor.data.r).imp[0]; expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); expect(impression.banner).to.exist; @@ -526,9 +618,9 @@ describe('IndexexchangeAdapter', function () { } }); - const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithFirstPartyData.data.r).site.page; - const expectedPageUrl = DEFAULT_BANNER_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; + const expectedPageUrl = DEFAULT_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; expect(pageUrl).to.equal(expectedPageUrl); }); @@ -540,10 +632,10 @@ describe('IndexexchangeAdapter', function () { } }); - const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestFirstPartyDataNumber.data.r).site.page; - expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); }); it('should not set first party or timeout if it is not present', function () { @@ -551,18 +643,18 @@ describe('IndexexchangeAdapter', function () { ix: {} }); - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(requestWithoutConfig.data.t).to.be.undefined; }); it('should not set first party or timeout if it is setConfig is not called', function () { - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(requestWithoutConfig.data.t).to.be.undefined; }); @@ -572,7 +664,7 @@ describe('IndexexchangeAdapter', function () { timeout: 500 } }); - const requestWithTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + const requestWithTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; expect(requestWithTimeout.data.t).to.equal(500); }); @@ -583,14 +675,98 @@ describe('IndexexchangeAdapter', function () { timeout: '500' } }); - const requestStringTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + const requestStringTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; expect(requestStringTimeout.data.t).to.be.undefined; }); + + it('request should contain both banner and video requests', function () { + const request = spec.buildRequests([DEFAULT_BANNER_VALID_BID[0], DEFAULT_VIDEO_VALID_BID[0]]); + + const bannerImp = JSON.parse(request[0].data.r).imp[0]; + expect(JSON.parse(request[0].data.v)).to.equal(BANNER_ENDPOINT_VERSION); + expect(bannerImp.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); + expect(bannerImp.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); + expect(bannerImp.banner).to.exist; + expect(bannerImp.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); + expect(bannerImp.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); + + const videoImp = JSON.parse(request[1].data.r).imp[0]; + expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); + expect(videoImp.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(videoImp.video).to.exist; + expect(videoImp.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); + expect(videoImp.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + }); }); - describe('interpretResponseBanner', function () { - it('should get correct bid response', function () { + describe('buildRequestVideo', function () { + const request = spec.buildRequests(DEFAULT_VIDEO_VALID_BID, DEFAULT_OPTION); + const query = request[0].data; + + it('query object (version, siteID and request) should be correct', function () { + expect(query.v).to.equal(VIDEO_ENDPOINT_VERSION); + expect(query.s).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId); + expect(query.r).to.exist; + expect(query.ac).to.equal('j'); + expect(query.sd).to.equal(1); + }); + + it('impression should have correct format and value', function () { + const impression = JSON.parse(query.r).imp[0]; + const sidValue = `${DEFAULT_VIDEO_VALID_BID[0].params.size[0].toString()}x${DEFAULT_VIDEO_VALID_BID[0].params.size[1].toString()}`; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video).to.exist; + expect(impression.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); + expect(impression.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + expect(impression.video.placement).to.exist; + expect(impression.video.placement).to.equal(1); + expect(impression.video.minduration).to.exist; + expect(impression.video.minduration).to.equal(0); + expect(impression.video.mimes).to.exist; + expect(impression.video.mimes[0]).to.equal('video/mp4'); + expect(impression.video.mimes[1]).to.equal('video/webm'); + + expect(impression.video.skippable).to.equal(false); + expect(impression.ext).to.exist; + expect(impression.ext.siteID).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId.toString()); + expect(impression.ext.sid).to.equal(sidValue); + }); + + it('impression should have correct format when mediaType is specified.', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + delete bid.mediaTypes; + bid.mediaType = 'video'; + const requestBidFloor = spec.buildRequests([bid])[0]; + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + const sidValue = `${DEFAULT_VIDEO_VALID_BID[0].params.size[0].toString()}x${DEFAULT_VIDEO_VALID_BID[0].params.size[1].toString()}`; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video).to.exist; + expect(impression.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); + expect(impression.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + expect(impression.video.placement).to.not.exist; + expect(impression.ext).to.exist; + expect(impression.ext.siteID).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId.toString()); + expect(impression.ext.sid).to.equal(sidValue); + }); + + it('should set correct placement if context is outstream', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.mediaTypes.video.context = 'outstream'; + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video).to.exist; + expect(impression.video.placement).to.exist; + expect(impression.video.placement).to.equal(4); + }); + }); + + describe('interpretResponse', function () { + it('should get correct bid response for banner ad', function () { const expectedParse = [ { requestId: '1a2b3c4d', @@ -598,6 +774,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '12345', width: 300, height: 250, + mediaType: 'banner', ad: '', currency: 'USD', ttl: 35, @@ -610,7 +787,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }); + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -624,6 +801,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '-', width: 300, height: 250, + mediaType: 'banner', ad: '', currency: 'USD', ttl: 35, @@ -636,8 +814,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }); - expect(result[0]).to.deep.equal(expectedParse[0]); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); }); it('should set Japanese price correctly', function () { @@ -650,6 +827,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '12345', width: 300, height: 250, + mediaType: 'banner', ad: '', currency: 'JPY', ttl: 35, @@ -662,7 +840,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -676,6 +854,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '12345', width: 300, height: 250, + mediaType: 'banner', ad: '', currency: 'USD', ttl: 35, @@ -688,13 +867,38 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + expect(result[0]).to.deep.equal(expectedParse[0]); + }); + + it('should get correct bid response for video ad', function () { + const expectedParse = [ + { + requestId: '1a2b3c4e', + cpm: 1.1, + creativeId: '12346', + mediaType: 'video', + width: 640, + height: 480, + currency: 'USD', + ttl: 3600, + netRevenue: true, + dealId: undefined, + vastUrl: 'www.abcd.com/vast', + meta: { + networkId: 51, + brandId: 303326, + brandName: 'OECTB' + } + } + ]; + const result = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); expect(result[0]).to.deep.equal(expectedParse[0]); }); it('bidrequest should have consent info if gdprApplies and consentString exist', function () { - const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs.ext.gdpr).to.equal(1); expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); @@ -708,7 +912,7 @@ describe('IndexexchangeAdapter', function () { } }; const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs.ext.gdpr).to.equal(1); expect(requestWithConsent.user).to.be.undefined; @@ -722,7 +926,7 @@ describe('IndexexchangeAdapter', function () { } }; const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs).to.be.undefined; expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); @@ -731,7 +935,7 @@ describe('IndexexchangeAdapter', function () { it('bidrequest should not have consent info if options.gdprConsent is undefined', function () { const options = {}; const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs).to.be.undefined; expect(requestWithConsent.user).to.be.undefined; @@ -740,10 +944,10 @@ describe('IndexexchangeAdapter', function () { it('bidrequest should not have page if options is undefined', function () { const options = {}; const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo[0].data.r); expect(requestWithoutreferInfo.site.page).to.be.undefined; - expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); it('bidrequest should not have page if options.refererInfo is an empty object', function () { @@ -751,10 +955,10 @@ describe('IndexexchangeAdapter', function () { refererInfo: {} }; const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo[0].data.r); expect(requestWithoutreferInfo.site.page).to.be.undefined; - expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); it('bidrequest should sent to secure endpoint if page url is secure', function () { @@ -764,10 +968,10 @@ describe('IndexexchangeAdapter', function () { } }; const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo[0].data.r); expect(requestWithoutreferInfo.site.page).to.equal(options.refererInfo.referer); - expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); }); });