From c8b4b5b374e2d4871bb90f2802671403251f6b1b Mon Sep 17 00:00:00 2001 From: danijel-ristic <168181386+danijel-ristic@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:08:32 +0200 Subject: [PATCH] TargetVideo Bid Adapter : add video support (#11867) * Add video support * Refactor code to library * Fix lint errors * Fix code duplication * Fix lint errors --------- Co-authored-by: Danijel Ristic --- libraries/targetVideoUtils/bidderUtils.js | 178 ++++++++++++ libraries/targetVideoUtils/constants.js | 23 ++ modules/bridBidAdapter.js | 94 +++--- modules/nextMillenniumBidAdapter.js | 28 +- modules/targetVideoBidAdapter.js | 272 ++++++++---------- modules/targetVideoBidAdapter.md | 23 +- .../modules/targetVideoBidAdapter_spec.js | 123 +++++++- 7 files changed, 501 insertions(+), 240 deletions(-) create mode 100644 libraries/targetVideoUtils/bidderUtils.js create mode 100644 libraries/targetVideoUtils/constants.js diff --git a/libraries/targetVideoUtils/bidderUtils.js b/libraries/targetVideoUtils/bidderUtils.js new file mode 100644 index 00000000000..f18540818cb --- /dev/null +++ b/libraries/targetVideoUtils/bidderUtils.js @@ -0,0 +1,178 @@ +import {VIDEO} from '../../src/mediaTypes.js'; +import {getRefererInfo} from '../../src/refererDetection.js'; +import {createTrackPixelHtml, deepAccess, getBidRequest} from '../../src/utils.js'; + +export function getSizes(request) { + let sizes = request.sizes; + if (!sizes && request.mediaTypes && request.mediaTypes.banner && request.mediaTypes.banner.sizes) { + sizes = request.mediaTypes.banner.sizes; + } + if (Array.isArray(sizes) && !Array.isArray(sizes[0])) { + sizes = [sizes[0], sizes[1]]; + } + if (!Array.isArray(sizes) || !Array.isArray(sizes[0])) { + sizes = [[0, 0]]; + } + + return sizes; +} + +export function formatRequest({payload, url, bidderRequest, bidId}) { + const request = { + method: 'POST', + data: JSON.stringify(payload), + url, + options: { + withCredentials: true, + } + } + + if (bidderRequest) { + request.bidderRequest = bidderRequest; + } + + if (bidId) { + request.bidId = bidId; + } + + return request; +} + +export function createVideoTag(bid) { + const tag = {}; + tag.id = parseInt(bid.params.placementId, 10); + tag.gpid = 'targetVideo'; + tag.sizes = getSizes(bid); + tag.primary_size = tag.sizes[0]; + tag.ad_types = [VIDEO]; + tag.uuid = bid.bidId; + tag.allow_smaller_sizes = false; + tag.use_pmt_rule = false; + tag.prebid = true; + tag.disable_psa = true; + tag.hb_source = 1; + tag.require_asset_url = true; + tag.video = { + playback_method: 2, + skippable: true + }; + + return tag; +} + +export function bannerBid(serverBid, rtbBid, bidderRequest, margin) { + const bidRequest = getBidRequest(serverBid.uuid, [bidderRequest]); + const sizes = getSizes(bidRequest); + const bid = { + requestId: serverBid.uuid, + cpm: rtbBid.cpm / margin, + creativeId: rtbBid.creative_id, + dealId: rtbBid.deal_id, + currency: 'USD', + netRevenue: true, + width: sizes[0][0], + height: sizes[0][1], + ttl: 300, + adUnitCode: bidRequest.adUnitCode, + appnexus: { + buyerMemberId: rtbBid.buyer_member_id, + dealPriority: rtbBid.deal_priority, + dealCode: rtbBid.deal_code + } + }; + + if (rtbBid.rtb.video) { + Object.assign(bid, { + vastImpUrl: rtbBid.notify_url, + ad: getBannerHtml(rtbBid.notify_url + '&redir=' + encodeURIComponent(rtbBid.rtb.video.asset_url)), + ttl: 3600 + }); + } + + return bid; +} + +export function videoBid(serverBid, requestId, currency, params, ttl) { + const {ad, adUrl, vastUrl, vastXml} = getAd(serverBid); + + const bid = { + requestId, + params, + currency, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.adid || serverBid.crid, + netRevenue: false, + ttl, + meta: { + advertiserDomains: serverBid.adomain || [] + } + }; + + if (vastUrl || vastXml) { + bid.mediaType = VIDEO; + if (vastUrl) bid.vastUrl = vastUrl; + if (vastXml) bid.vastXml = vastXml; + } else { + bid.ad = ad; + bid.adUrl = adUrl; + }; + + return bid; +} + +export function getRtbBid(tag) { + return tag && tag.ads && tag.ads.length && tag.ads.find(ad => ad.rtb); +} + +export function getBannerHtml(vastUrl) { + return ` + + + + + + + +
+ + + + `; +} + +export function getAd(bid) { + let ad, adUrl, vastXml, vastUrl; + + switch (deepAccess(bid, 'ext.prebid.type')) { + case VIDEO: + if (bid.adm.substr(0, 4) === 'http') { + vastUrl = bid.adm; + } else { + vastXml = bid.adm; + }; + break; + default: + if (bid.adm && bid.nurl) { + ad = bid.adm; + ad += createTrackPixelHtml(decodeURIComponent(bid.nurl)); + } else if (bid.adm) { + ad = bid.adm; + } else if (bid.nurl) { + adUrl = bid.nurl; + }; + } + + return {ad, adUrl, vastXml, vastUrl}; +} + +export function getSiteObj() { + const refInfo = (getRefererInfo && getRefererInfo()) || {}; + + return { + page: refInfo.page, + ref: refInfo.ref, + domain: refInfo.domain + } +} diff --git a/libraries/targetVideoUtils/constants.js b/libraries/targetVideoUtils/constants.js new file mode 100644 index 00000000000..8ce94c0eaeb --- /dev/null +++ b/libraries/targetVideoUtils/constants.js @@ -0,0 +1,23 @@ +const SOURCE = 'pbjs'; +const GVLID = 786; +const MARGIN = 1.35; +const BIDDER_CODE = 'targetVideo'; + +const TIME_TO_LIVE = 300; +const BANNER_ENDPOINT_URL = 'https://ib.adnxs.com/ut/v3/prebid'; +const VIDEO_ENDPOINT_URL = 'https://pbs.prebrid.tv/openrtb2/auction'; +const VIDEO_PARAMS = [ + 'api', 'linearity', 'maxduration', 'mimes', 'minduration', + 'plcmt', 'playbackmethod', 'protocols', 'startdelay' +]; + +export { + SOURCE, + GVLID, + MARGIN, + BIDDER_CODE, + TIME_TO_LIVE, + BANNER_ENDPOINT_URL, + VIDEO_ENDPOINT_URL, + VIDEO_PARAMS +} diff --git a/modules/bridBidAdapter.js b/modules/bridBidAdapter.js index f3fe1541886..527cb9d5d5d 100644 --- a/modules/bridBidAdapter.js +++ b/modules/bridBidAdapter.js @@ -1,7 +1,7 @@ -import {createTrackPixelHtml, _each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js'; +import {_each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js'; import {VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import {getAd, getSiteObj} from '../libraries/targetVideoUtils/bidderUtils.js' /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -180,50 +180,50 @@ export const spec = { } -/** - * Helper function to get ad - * - * @param {object} bid The bid. - * @return {object} ad object. - */ -function getAd(bid) { - let ad, adUrl, vastXml, vastUrl; - - switch (deepAccess(bid, 'ext.prebid.type')) { - case VIDEO: - if (bid.adm.substr(0, 4) === 'http') { - vastUrl = bid.adm; - } else { - vastXml = bid.adm; - }; - break; - default: - if (bid.adm && bid.nurl) { - ad = bid.adm; - ad += createTrackPixelHtml(decodeURIComponent(bid.nurl)); - } else if (bid.adm) { - ad = bid.adm; - } else if (bid.nurl) { - adUrl = bid.nurl; - }; - } - - return {ad, adUrl, vastXml, vastUrl}; -} - -/** - * Helper function to get site object - * - * @return {object} siteObj. - */ -function getSiteObj() { - const refInfo = (getRefererInfo && getRefererInfo()) || {}; - - return { - page: refInfo.page, - ref: refInfo.ref, - domain: refInfo.domain - }; -} +// /** +// * Helper function to get ad +// * +// * @param {object} bid The bid. +// * @return {object} ad object. +// */ +// function getAd(bid) { +// let ad, adUrl, vastXml, vastUrl; + +// switch (deepAccess(bid, 'ext.prebid.type')) { +// case VIDEO: +// if (bid.adm.substr(0, 4) === 'http') { +// vastUrl = bid.adm; +// } else { +// vastXml = bid.adm; +// }; +// break; +// default: +// if (bid.adm && bid.nurl) { +// ad = bid.adm; +// ad += createTrackPixelHtml(decodeURIComponent(bid.nurl)); +// } else if (bid.adm) { +// ad = bid.adm; +// } else if (bid.nurl) { +// adUrl = bid.nurl; +// }; +// } + +// return {ad, adUrl, vastXml, vastUrl}; +// } + +// /** +// * Helper function to get site object +// * +// * @return {object} siteObj. +// */ +// function getSiteObj() { +// const refInfo = (getRefererInfo && getRefererInfo()) || {}; + +// return { +// page: refInfo.page, +// ref: refInfo.ref, +// domain: refInfo.domain +// }; +// } registerBidder(spec); diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 65f530d9e58..5e9be67c6bb 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -1,6 +1,5 @@ import { _each, - createTrackPixelHtml, deepAccess, deepSetValue, getBidIdParameter, @@ -12,6 +11,7 @@ import { parseUrl, triggerPixel, } from '../src/utils.js'; +import {getAd} from '../libraries/targetVideoUtils/bidderUtils.js'; import {getGlobal} from '../src/prebidGlobal.js'; import { EVENTS } from '../src/constants.js'; @@ -455,32 +455,6 @@ function getTopWindow(curWindow, nesting = 0) { }; } -function getAd(bid) { - let ad, adUrl, vastXml, vastUrl; - - switch (deepAccess(bid, 'ext.prebid.type')) { - case VIDEO: - if (bid.adm.substr(0, 4) === 'http') { - vastUrl = bid.adm; - } else { - vastXml = bid.adm; - }; - - break; - default: - if (bid.adm && bid.nurl) { - ad = bid.adm; - ad += createTrackPixelHtml(decodeURIComponent(bid.nurl)); - } else if (bid.adm) { - ad = bid.adm; - } else if (bid.nurl) { - adUrl = bid.nurl; - }; - }; - - return {ad, adUrl, vastXml, vastUrl}; -} - function getSiteObj() { const refInfo = (getRefererInfo && getRefererInfo()) || {}; diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index 282f322c36a..fd5d79d08b7 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -1,24 +1,19 @@ -import {find} from '../src/polyfill.js'; -import {getBidRequest} from '../src/utils.js'; +import {_each, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {formatRequest, getRtbBid, getSiteObj, videoBid, bannerBid, createVideoTag} from '../libraries/targetVideoUtils/bidderUtils.js'; +import {SOURCE, GVLID, BIDDER_CODE, VIDEO_PARAMS, BANNER_ENDPOINT_URL, VIDEO_ENDPOINT_URL, MARGIN, TIME_TO_LIVE} from '../libraries/targetVideoUtils/constants.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid */ -const SOURCE = 'pbjs'; -const BIDDER_CODE = 'targetVideo'; -const ENDPOINT_URL = 'https://ib.adnxs.com/ut/v3/prebid'; -const MARGIN = 1.35; -const GVLID = 786; - export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -37,35 +32,114 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function(bidRequests, bidderRequest) { - const tags = bidRequests.map(createVideoTag); - const schain = bidRequests[0].schain; - const payload = { - tags: tags, - sdk: { - source: SOURCE, - version: '$prebid.version$' - }, - schain: schain + const requests = []; + const sdk = { + source: SOURCE, + version: '$prebid.version$' }; - if (bidderRequest && bidderRequest.gdprConsent) { - payload.gdpr_consent = { - consent_string: bidderRequest.gdprConsent.consentString, - consent_required: bidderRequest.gdprConsent.gdprApplies - }; - - if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { - let ac = bidderRequest.gdprConsent.addtlConsent; - let acStr = ac.substring(ac.indexOf('~') + 1); - payload.gdpr_consent.addtl_consent = acStr.split('.').map(id => parseInt(id, 10)); + for (let {params, bidId, sizes, mediaTypes} of bidRequests) { + for (const mediaType in mediaTypes) { + switch (mediaType) { + case VIDEO: { + const video = mediaTypes[VIDEO]; + const placementId = params.placementId; + const site = getSiteObj(); + + if (sizes && !Array.isArray(sizes[0])) sizes = [sizes]; + + const payload = { + sdk, + id: bidderRequest.bidderRequestId, + site, + imp: [] + } + + const imp = { + ext: { + prebid: { + storedrequest: { id: placementId } + } + }, + video: getDefinedParams(video, VIDEO_PARAMS) + } + + if (video.playerSize) { + imp.video = Object.assign( + imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize[0]) || {} + ); + } else if (video.w && video.h) { + imp.video.w = video.w; + imp.video.h = video.h; + } + + payload.imp.push(imp); + + const gdprConsent = bidderRequest && bidderRequest.gdprConsent; + const uspConsent = bidderRequest && bidderRequest.uspConsent; + + if (gdprConsent || uspConsent) { + payload.regs = { ext: {} }; + + if (uspConsent) { + payload.regs.ext.us_privacy = uspConsent; + }; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies !== 'undefined') { + payload.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0; + }; + + if (typeof gdprConsent.consentString !== 'undefined') { + payload.user = { + ext: { consent: gdprConsent.consentString } + }; + }; + }; + }; + + if (bidRequests[0].schain) { + payload.schain = bidRequests[0].schain; + } + + requests.push(formatRequest({ payload, url: VIDEO_ENDPOINT_URL, bidId })); + break; + } + + case BANNER: { + const tags = bidRequests.map(createVideoTag); + const schain = bidRequests[0].schain; + + const payload = { + tags, + sdk, + schain, + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdpr_consent = { + consent_string: bidderRequest.gdprConsent.consentString, + consent_required: bidderRequest.gdprConsent.gdprApplies + }; + + if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { + let ac = bidderRequest.gdprConsent.addtlConsent; + let acStr = ac.substring(ac.indexOf('~') + 1); + payload.gdpr_consent.addtl_consent = acStr.split('.').map(id => parseInt(id, 10)); + } + } + + if (bidderRequest && bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent + } + + return formatRequest({ payload, url: BANNER_ENDPOINT_URL, bidderRequest }); + } + } } } - if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent - } - - return formatRequest(payload, bidderRequest); + return requests; }, /** @@ -74,139 +148,33 @@ export const spec = { * @param {*} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, { bidderRequest }) { + interpretResponse: function(serverResponse, { bidderRequest, ...bidRequest }) { serverResponse = serverResponse.body; + const currency = serverResponse.cur; const bids = []; if (serverResponse.tags) { serverResponse.tags.forEach(serverBid => { const rtbBid = getRtbBid(serverBid); if (rtbBid && rtbBid.cpm !== 0 && rtbBid.ad_type == VIDEO) { - bids.push(newBid(serverBid, rtbBid, bidderRequest)); + bids.push(bannerBid(serverBid, rtbBid, bidderRequest, MARGIN)); } }); } - return bids; - } - -} - -function getSizes(request) { - let sizes = request.sizes; - if (!sizes && request.mediaTypes && request.mediaTypes.banner && request.mediaTypes.banner.sizes) { - sizes = request.mediaTypes.banner.sizes; - } - if (Array.isArray(sizes) && !Array.isArray(sizes[0])) { - sizes = [sizes[0], sizes[1]]; - } - if (!Array.isArray(sizes) || !Array.isArray(sizes[0])) { - sizes = [[0, 0]]; - } - - return sizes; -} + if (serverResponse.seatbid) { + _each(serverResponse.seatbid, (resp) => { + _each(resp.bid, (bid) => { + const requestId = bidRequest.bidId; + const params = bidRequest.params; -function formatRequest(payload, bidderRequest) { - const options = { - withCredentials: true - }; - const request = { - method: 'POST', - url: ENDPOINT_URL, - data: JSON.stringify(payload), - bidderRequest, - options - }; - - return request; -} - -/** - * Create video auction. - * - * @param {*} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ -function createVideoTag(bid) { - const tag = {}; - tag.id = parseInt(bid.params.placementId, 10); - tag.gpid = 'targetVideo'; - tag.sizes = getSizes(bid); - tag.primary_size = tag.sizes[0]; - tag.ad_types = [VIDEO]; - tag.uuid = bid.bidId; - tag.allow_smaller_sizes = false; - tag.use_pmt_rule = false; - tag.prebid = true; - tag.disable_psa = true; - tag.hb_source = 1; - tag.require_asset_url = true; - tag.video = { - playback_method: 2, - skippable: true - }; - - return tag; -} - -/** - * Unpack the Server's Bid into a Prebid-compatible one. - * @param serverBid - * @param rtbBid - * @param bidderRequest - * @return Bid - */ -function newBid(serverBid, rtbBid, bidderRequest) { - const bidRequest = getBidRequest(serverBid.uuid, [bidderRequest]); - const sizes = getSizes(bidRequest); - const bid = { - requestId: serverBid.uuid, - cpm: rtbBid.cpm / MARGIN, - creativeId: rtbBid.creative_id, - dealId: rtbBid.deal_id, - currency: 'USD', - netRevenue: true, - width: sizes[0][0], - height: sizes[0][1], - ttl: 300, - adUnitCode: bidRequest.adUnitCode, - appnexus: { - buyerMemberId: rtbBid.buyer_member_id, - dealPriority: rtbBid.deal_priority, - dealCode: rtbBid.deal_code + bids.push(videoBid(bid, requestId, currency, params, TIME_TO_LIVE)); + }); + }); } - }; - - if (rtbBid.rtb.video) { - Object.assign(bid, { - vastImpUrl: rtbBid.notify_url, - ad: getBannerHtml(rtbBid.notify_url + '&redir=' + encodeURIComponent(rtbBid.rtb.video.asset_url)), - ttl: 3600 - }); - } - - return bid; -} -function getRtbBid(tag) { - return tag && tag.ads && tag.ads.length && find(tag.ads, ad => ad.rtb); -} - -function getBannerHtml(vastUrl) { - return ` - - - - - - - -
- - - - `; + return bids; + } } registerBidder(spec); diff --git a/modules/targetVideoBidAdapter.md b/modules/targetVideoBidAdapter.md index 557c9f94410..a34ad0aff27 100644 --- a/modules/targetVideoBidAdapter.md +++ b/modules/targetVideoBidAdapter.md @@ -3,17 +3,17 @@ ``` Module Name: Target Video Bid Adapter Module Type: Bidder Adapter -Maintainer: grajzer@gmail.com +Maintainers: grajzer@gmail.com, danijel.ristic@target-video.com ``` # Description Connects to Appnexus exchange for bids. -TargetVideo bid adapter supports Banner. +TargetVideo bid adapter supports Banner and Video. # Test Parameters -``` +```js var adUnits = [ // Banner adUnit { @@ -29,6 +29,23 @@ var adUnits = [ placementId: 13232361 } }] + }, + // Video adUnit + { + mediaTypes: { + video: { + playerSize: [[640, 360]], + context: 'instream', + playbackmethod: [1, 2, 3, 4] + } + }, + bids: [{ + bidder: 'targetVideo', + params: { + placementId: 12345, + reserve: 0, + } + }] } ]; ``` diff --git a/test/spec/modules/targetVideoBidAdapter_spec.js b/test/spec/modules/targetVideoBidAdapter_spec.js index 8180183e6d7..442d7e7ef0b 100644 --- a/test/spec/modules/targetVideoBidAdapter_spec.js +++ b/test/spec/modules/targetVideoBidAdapter_spec.js @@ -1,27 +1,42 @@ import { spec } from '../../../modules/targetVideoBidAdapter.js' describe('TargetVideo Bid Adapter', function() { + const bidder = 'targetVideo'; + const params = { + placementId: 12345, + }; + const bannerRequest = [{ - bidder: 'targetVideo', + bidder, + params, mediaTypes: { banner: { sizes: [[300, 250]], } }, - params: { - placementId: 12345, + }]; + + const videoRequest = [{ + bidder, + params, + mediaTypes: { + video: { + playerSize: [[640, 360]], + context: 'instream', + playbackmethod: [1, 2, 3, 4] + } } }]; it('Test the bid validation function', function() { - const validBid = spec.isBidRequestValid(bannerRequest[0]); + const validBid = spec.isBidRequestValid(bannerRequest[0]) && spec.isBidRequestValid(videoRequest[0]); const invalidBid = spec.isBidRequestValid(null); expect(validBid).to.be.true; expect(invalidBid).to.be.false; }); - it('Test the request processing function', function () { + it('Test the BANNER request processing function', function() { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); expect(request).to.not.be.empty; @@ -36,7 +51,20 @@ describe('TargetVideo Bid Adapter', function() { expect(payload.tags[0].ad_types[0]).to.equal('video'); }); - it('Handle nobid responses', function () { + it('Test the VIDEO request processing function', function() { + const request = spec.buildRequests(videoRequest, videoRequest[0]); + expect(request).to.not.be.empty; + + const payload = JSON.parse(request[0].data); + expect(payload).to.not.be.empty; + expect(payload.sdk).to.deep.equal({ + source: 'pbjs', + version: '$prebid.version$' + }); + expect(payload.imp[0].ext.prebid.storedrequest.id).to.equal(12345); + }) + + it('Handle BANNER nobid responses', function() { const responseBody = { 'version': '0.0.1', 'tags': [{ @@ -48,11 +76,24 @@ describe('TargetVideo Bid Adapter', function() { }; const bidderRequest = null; - const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); + const bidResponse = spec.interpretResponse({ body: responseBody }, { bidderRequest }); expect(bidResponse.length).to.equal(0); }); - it('Test the response parsing function', function () { + it('Handle VIDEO nobid responses', function() { + const responseBody = { + 'id': 'test-id', + 'cur': 'USD', + 'seatbid': [], + 'nbr': 0 + }; + const bidderRequest = null; + + const bidResponse = spec.interpretResponse({ body: responseBody }, { bidderRequest }); + expect(bidResponse.length).to.equal(0); + }) + + it('Test the BANNER response parsing function', function() { const responseBody = { 'tags': [{ 'uuid': '84ab500420319d', @@ -82,7 +123,7 @@ describe('TargetVideo Bid Adapter', function() { }] }; - const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); + const bidResponse = spec.interpretResponse({ body: responseBody }, { bidderRequest }); expect(bidResponse).to.not.be.empty; const bid = bidResponse[0]; @@ -94,7 +135,43 @@ describe('TargetVideo Bid Adapter', function() { expect(bid.ad).to.include('initPlayer') }); - it('Test GDPR consent information is present in the request', function () { + it('Test the VIDEO response parsing function', function() { + const responseBody = { + 'id': 'test-id', + 'cur': 'USD', + 'seatbid': [{ + 'bid': [{ + 'id': '5044997188309660254', + 'price': 10, + 'adm': 'test ad', + 'adid': '97517771', + 'crid': '97517771', + 'adomain': ['domain.com'], + 'w': 640, + 'h': 480 + }], + 'seat': 'bidder' + }] + }; + const bidderRequest = { + bidderCode: 'brid', + bidderRequestId: '22edbae2733bf6', + bids: videoRequest + }; + + const bidResponse = spec.interpretResponse({ body: responseBody }, { bidderRequest }); + expect(bidResponse).to.not.be.empty; + + const bid = bidResponse[0]; + expect(bid).to.not.be.empty; + expect(bid.ad).to.equal('test ad'); + expect(bid.cpm).to.equal(10); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(480); + expect(bid.currency).to.equal('USD'); + }) + + it('Test BANNER GDPR consent information is present in the request', function() { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { 'bidderCode': 'targetVideo', @@ -119,7 +196,7 @@ describe('TargetVideo Bid Adapter', function() { expect(payload.gdpr_consent.addtl_consent).to.exist.and.to.deep.equal([7, 12, 35, 62, 66, 70, 89, 93, 108]); }); - it('Test US Privacy string is present in the request', function() { + it('Test BANNER US Privacy string is present in the request', function() { let consentString = '1YA-'; let bidderRequest = { 'bidderCode': 'targetVideo', @@ -136,4 +213,28 @@ describe('TargetVideo Bid Adapter', function() { expect(payload.us_privacy).to.exist; expect(payload.us_privacy).to.exist.and.to.equal(consentString); }); + + it('Test VIDEO GDPR and USP consents are present in the request', function() { + let gdprConsentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let uspConsentString = '1YA-'; + let bidderRequest = { + 'bidderCode': 'targetVideo', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'uspConsent': uspConsentString, + 'gdprConsent': { + consentString: gdprConsentString, + gdprApplies: true, + addtlConsent: '1~7.12.35.62.66.70.89.93.108' + } + }; + bidderRequest.bids = videoRequest; + + const request = spec.buildRequests(videoRequest, bidderRequest); + const payload = JSON.parse(request[0].data); + + expect(payload.user.ext.consent).to.equal(gdprConsentString); + expect(payload.regs.ext.us_privacy).to.equal(uspConsentString); + expect(payload.regs.ext.gdpr).to.equal(1); + }); });