From fc1428249f00340140ddc790003288e976c7ba01 Mon Sep 17 00:00:00 2001 From: Ioan-Carol Plangu Date: Mon, 8 Aug 2022 08:05:27 +0200 Subject: [PATCH 01/22] WIP| --- modules/carodaBidAdapter.js | 246 ++++++++++++++++++++++++++++++++++++ modules/carodaBidAdapter.md | 0 2 files changed, 246 insertions(+) create mode 100644 modules/carodaBidAdapter.js create mode 100644 modules/carodaBidAdapter.md diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js new file mode 100644 index 00000000000..03755df9c9a --- /dev/null +++ b/modules/carodaBidAdapter.js @@ -0,0 +1,246 @@ +// jshint esversion: 6, es3: false, node: true +'use strict' + +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { BANNER, VIDEO } from '../src/mediaTypes.js' +import { + _map, + deepAccess, + deepSetValue, + mergeDeep, + parseSizesInput +} from '../src/utils.js' +import { config } from '../src/config.js' +import { Renderer } from '../src/Renderer.js' + +const { getConfig } = config + +const BIDDER_CODE = 'caroda' +const GVLID = 954 +const carodaDomain = 'prebid.caroda.io' + +// const OUTSTREAM_RENDERER_URL = 'https://s2.adform.net/banners/scripts/video/outstream/render.js'; + +// some state info is required to synchronize wi +const topUsableWindow = getTopUsableWindow() +const pageViewId = topUsableWindow.carodaPageViewId || + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid: bid => { + const params = bid.params || {} + const { ctok, placementId } = params + return ctok && placementId + }, + buildRequests: (validBidRequests, bidderRequest) => { + const ortbCommon = getOrtbCommon(bidderRequest) + const priceType = + setOnAny(validBidRequests, 'params.priceType') || + 'net' + const test = setOnAny(validBidRequests, 'params.test') + const currency = getConfig('currency.adServerCurrency') + const cur = currency && [currency] + const eids = setOnAny(validBidRequests, 'userIdAsEids') + const schain = setOnAny(validBidRequests, 'schain') + const request = { + id: bidderRequest.auctionId, + ...ortbCommon, + pt: priceType, + cur + } + if (test) { + request.is_debug = !!test + request.test = 1 + } + if (config.getConfig('coppa')) { + deepSetValue(request, 'privacy.coppa', 1) + } + if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { + deepSetValue( + request, + 'privacy.gdpr_consent', + bidderRequest.gdprConsent.consentString + ) + deepSetValue( + request, + 'privacy.gdpr', + bidderRequest.gdprConsent.gdprApplies & 1 + ) + } + if (bidderRequest.uspConsent) { + deepSetValue(request, 'privacy.us_privacy', bidderRequest.uspConsent) + } + if (eids) { + deepSetValue(request, 'user.eids', eids) + } + if (schain) { + deepSetValue(request, 'schain', schain) + } + return getImps(validBidRequests).map(imp => ({ + method: 'POST', + url: 'https://' + carodaDomain + '/api/hb?entry_id=' + pageViewId, + data: JSON.stringify({ + request, + ...imp + }) + })) + }, + interpretResponse: function (serverResponse, { bids }) { + if (!serverResponse.body) { + return + } + const { seatbid, cur } = serverResponse.body + + const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce( + (result, bid) => { + result[bid.impid - 1] = bid + return result + }, + [] + ) + + return bids + .map((bid, id) => { + const bidResponse = bidResponses[id] + if (bidResponse) { + const mediaType = deepAccess(bidResponse, 'ext.prebid.type') + const result = { + requestId: bid.bidId, + cpm: bidResponse.price, + creativeId: bidResponse.crid, + ttl: 360, + netRevenue: bid.netRevenue === 'net', + currency: cur, + mediaType, + width: bidResponse.w, + height: bidResponse.h, + dealId: bidResponse.dealid, + meta: { + mediaType, + advertiserDomains: bidResponse.adomain + } + } + + result[mediaType === VIDEO ? 'vastXml' : 'ad'] = bidResponse.adm + + if ( + !bid.renderer && + mediaType === VIDEO && + deepAccess(bid, 'mediaTypes.video.context') === 'outstream' + ) { + result.renderer = Renderer.install({ + id: bid.bidId, + url: OUTSTREAM_RENDERER_URL, + adUnitCode: bid.adUnitCode + }) + result.renderer.setRender(renderer) + } + + return result + } + }) + .filter(Boolean) + } +} + +registerBidder(spec) + +function setOnAny (collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = deepAccess(collection[i], key) + if (result) { + return result + } + } +} + +function flatten (arr) { + return [].concat(...arr) +} + +function renderer (bid) { + bid.renderer.push(() => { + window.Adform.renderOutstream(bid) + }) +} + +function getTopUsableWindow () { + let res = window + try { + while (window.top !== res && res.parent.location.href.length) { + res = res.parent + } + } catch (e) {} + return res +} + +function getOrtbCommon (bidderRequest) { + let app, site + const commonFpd = bidderRequest.ortb2 || {} + let { user } = commonFpd + if (typeof getConfig('app') === 'object') { + app = getConfig('app') || {} + if (commonFpd.app) { + mergeDeep(app, commonFpd.app) + } + } else { + site = getConfig('site') || {} + if (commonFpd.site) { + mergeDeep(site, commonFpd.site) + } + if (!site.page) { + site.page = bidderRequest.refererInfo.page + } + } + const device = getConfig('device') || {} + device.w = device.w || window.innerWidth + device.h = device.h || window.innerHeight + device.ua = device.ua || navigator.userAgent + return { + app, + site, + user, + device + } +} + +function getImps (validBidRequests) { + return validBidRequests.map((bid, id) => { + bid.netRevenue = pt + const floorInfo = bid.getFloor + ? bid.getFloor({ + currency: currency || 'USD' + }) + : {} + const bidfloor = floorInfo.floor + const bidfloorcur = floorInfo.currency + const { ctok, placementId } = bid.params + const imp = { + id: id + 1, + ctok, + placementId, + bidfloor, + bidfloorcur + } + const bannerParams = deepAccess(bid, 'mediaTypes.banner') + if (bannerParams && bannerParams.sizes) { + const sizes = parseSizesInput(bannerParams.sizes) + const format = sizes.map(size => { + const [width, height] = size.split('x') + const w = parseInt(width, 10) + const h = parseInt(height, 10) + return { w, h } + }) + imp.banner = { + format + } + } + const videoParams = deepAccess(bid, 'mediaTypes.video') + if (videoParams) { + imp.video = videoParams + } + return imp + }) +} \ No newline at end of file diff --git a/modules/carodaBidAdapter.md b/modules/carodaBidAdapter.md new file mode 100644 index 00000000000..e69de29bb2d From 75dc4dadd0c3114386e512bfb3c06d5b82484df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Mon, 8 Aug 2022 11:03:41 +0200 Subject: [PATCH 02/22] work on the response format --- modules/carodaBidAdapter.js | 91 ++++++++++++------------------------- 1 file changed, 30 insertions(+), 61 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 03755df9c9a..2b6a55219d7 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -7,11 +7,12 @@ import { _map, deepAccess, deepSetValue, + flatten, + logError, mergeDeep, parseSizesInput } from '../src/utils.js' import { config } from '../src/config.js' -import { Renderer } from '../src/Renderer.js' const { getConfig } = config @@ -23,7 +24,7 @@ const carodaDomain = 'prebid.caroda.io' // some state info is required to synchronize wi const topUsableWindow = getTopUsableWindow() -const pageViewId = topUsableWindow.carodaPageViewId || +const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) export const spec = { code: BIDDER_CODE, @@ -46,6 +47,7 @@ export const spec = { const schain = setOnAny(validBidRequests, 'schain') const request = { id: bidderRequest.auctionId, + hb_version: '$prebid.version$', ...ortbCommon, pt: priceType, cur @@ -82,7 +84,7 @@ export const spec = { method: 'POST', url: 'https://' + carodaDomain + '/api/hb?entry_id=' + pageViewId, data: JSON.stringify({ - request, + ...request, ...imp }) })) @@ -91,57 +93,34 @@ export const spec = { if (!serverResponse.body) { return } - const { seatbid, cur } = serverResponse.body - - const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce( - (result, bid) => { - result[bid.impid - 1] = bid - return result - }, - [] - ) - - return bids - .map((bid, id) => { - const bidResponse = bidResponses[id] - if (bidResponse) { - const mediaType = deepAccess(bidResponse, 'ext.prebid.type') - const result = { + const { ok, error } = serverResponse.body + if (error) { + return + } + try { + return JSON.parse(ok) + .map((bid) => { + return { requestId: bid.bidId, - cpm: bidResponse.price, - creativeId: bidResponse.crid, - ttl: 360, - netRevenue: bid.netRevenue === 'net', - currency: cur, - mediaType, - width: bidResponse.w, - height: bidResponse.h, - dealId: bidResponse.dealid, + cpm: bid.cpm, + creativeId: bid.crid, + ttl: 300, + netRevenue: true, + currency: bid.currency, + width: bid.w, + height: bid.h, meta: { - mediaType, - advertiserDomains: bidResponse.adomain - } - } - - result[mediaType === VIDEO ? 'vastXml' : 'ad'] = bidResponse.adm - - if ( - !bid.renderer && - mediaType === VIDEO && - deepAccess(bid, 'mediaTypes.video.context') === 'outstream' - ) { - result.renderer = Renderer.install({ - id: bid.bidId, - url: OUTSTREAM_RENDERER_URL, - adUnitCode: bid.adUnitCode - }) - result.renderer.setRender(renderer) + advertiserDomains: bid && bid.adomain ? bid.adomain : [] + }, + ad: bid.ad, + placementId: bid.placementId } - - return result - } - }) - .filter(Boolean) + }) + .filter(Boolean) + } catch (e) { + logError(BIDDER_CODE, ': caught', e) + return + } } } @@ -156,16 +135,6 @@ function setOnAny (collection, key) { } } -function flatten (arr) { - return [].concat(...arr) -} - -function renderer (bid) { - bid.renderer.push(() => { - window.Adform.renderOutstream(bid) - }) -} - function getTopUsableWindow () { let res = window try { From 33c0fb56b6eb30b1d63d717f5faf34f61358f8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Mon, 8 Aug 2022 11:03:52 +0200 Subject: [PATCH 03/22] added md file --- modules/carodaBidAdapter.md | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/modules/carodaBidAdapter.md b/modules/carodaBidAdapter.md index e69de29bb2d..35785525038 100644 --- a/modules/carodaBidAdapter.md +++ b/modules/carodaBidAdapter.md @@ -0,0 +1,43 @@ +# Overview + +Module Name: Caroda Adapter +Module Type: Bidder Adapter +Maintainer: dev@caroda.io + +# Description + +Module that connects to Caroda demand sources to fetch bids. +Banner and video formats are supported. +Use `caroda` as bidder. + +# Test Parameters +``` + var adUnits = [{ + code: '/19968336/prebid_banner_example_1', + mediaTypes: { + banner: { + sizes: [[ 300, 250 ]] + } + } + bids: [{ + bidder: 'caroda', + params: { + ctok: '230ce9490c5434354' + } + }] + }, { + code: '/19968336/prebid_video_example_1', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + } + bids: [{ + bidder: 'caroda', + params: { + ctok: '230ce9490c5434354' + } + }] + }]; +``` From b1d3bd590fbfea793b4c2cf6b922f8e9890b2be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Mon, 8 Aug 2022 11:35:15 +0200 Subject: [PATCH 04/22] some fixes and simplications --- modules/carodaBidAdapter.js | 154 +++++++++++++++++------------------- 1 file changed, 71 insertions(+), 83 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 2b6a55219d7..53e0c1deeea 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -4,10 +4,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER, VIDEO } from '../src/mediaTypes.js' import { - _map, deepAccess, deepSetValue, - flatten, logError, mergeDeep, parseSizesInput @@ -20,9 +18,7 @@ const BIDDER_CODE = 'caroda' const GVLID = 954 const carodaDomain = 'prebid.caroda.io' -// const OUTSTREAM_RENDERER_URL = 'https://s2.adform.net/banners/scripts/video/outstream/render.js'; - -// some state info is required to synchronize wi +// some state info is required to synchronize with Caroda ad server const topUsableWindow = getTopUsableWindow() const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) @@ -36,24 +32,23 @@ export const spec = { return ctok && placementId }, buildRequests: (validBidRequests, bidderRequest) => { - const ortbCommon = getOrtbCommon(bidderRequest) + const ortbCommon = getORTBCommon(bidderRequest) const priceType = setOnAny(validBidRequests, 'params.priceType') || 'net' const test = setOnAny(validBidRequests, 'params.test') const currency = getConfig('currency.adServerCurrency') - const cur = currency && [currency] const eids = setOnAny(validBidRequests, 'userIdAsEids') const schain = setOnAny(validBidRequests, 'schain') const request = { id: bidderRequest.auctionId, + currency, hb_version: '$prebid.version$', ...ortbCommon, - pt: priceType, - cur + price_type: priceType, + schain } if (test) { - request.is_debug = !!test request.test = 1 } if (config.getConfig('coppa')) { @@ -77,16 +72,10 @@ export const spec = { if (eids) { deepSetValue(request, 'user.eids', eids) } - if (schain) { - deepSetValue(request, 'schain', schain) - } - return getImps(validBidRequests).map(imp => ({ + return getImps(validBidRequests, request).map(imp => ({ method: 'POST', url: 'https://' + carodaDomain + '/api/hb?entry_id=' + pageViewId, - data: JSON.stringify({ - ...request, - ...imp - }) + data: JSON.stringify(imp) })) }, interpretResponse: function (serverResponse, { bids }) { @@ -119,7 +108,6 @@ export const spec = { .filter(Boolean) } catch (e) { logError(BIDDER_CODE, ': caught', e) - return } } } @@ -145,71 +133,71 @@ function getTopUsableWindow () { return res } -function getOrtbCommon (bidderRequest) { - let app, site - const commonFpd = bidderRequest.ortb2 || {} - let { user } = commonFpd - if (typeof getConfig('app') === 'object') { - app = getConfig('app') || {} - if (commonFpd.app) { - mergeDeep(app, commonFpd.app) - } - } else { - site = getConfig('site') || {} - if (commonFpd.site) { - mergeDeep(site, commonFpd.site) - } - if (!site.page) { - site.page = bidderRequest.refererInfo.page - } +function getORTBCommon (bidderRequest) { + let app, site + const commonFpd = bidderRequest.ortb2 || {} + let { user } = commonFpd + if (typeof getConfig('app') === 'object') { + app = getConfig('app') || {} + if (commonFpd.app) { + mergeDeep(app, commonFpd.app) } - const device = getConfig('device') || {} - device.w = device.w || window.innerWidth - device.h = device.h || window.innerHeight - device.ua = device.ua || navigator.userAgent - return { - app, - site, - user, - device + } else { + site = getConfig('site') || {} + if (commonFpd.site) { + mergeDeep(site, commonFpd.site) } + if (!site.page) { + site.page = bidderRequest.refererInfo.page + } + } + const device = getConfig('device') || {} + device.w = device.w || window.innerWidth + device.h = device.h || window.innerHeight + device.ua = device.ua || navigator.userAgent + return { + app, + site, + user, + device + } } -function getImps (validBidRequests) { - return validBidRequests.map((bid, id) => { - bid.netRevenue = pt - const floorInfo = bid.getFloor - ? bid.getFloor({ - currency: currency || 'USD' - }) - : {} - const bidfloor = floorInfo.floor - const bidfloorcur = floorInfo.currency - const { ctok, placementId } = bid.params - const imp = { - id: id + 1, - ctok, - placementId, - bidfloor, - bidfloorcur - } - const bannerParams = deepAccess(bid, 'mediaTypes.banner') - if (bannerParams && bannerParams.sizes) { - const sizes = parseSizesInput(bannerParams.sizes) - const format = sizes.map(size => { - const [width, height] = size.split('x') - const w = parseInt(width, 10) - const h = parseInt(height, 10) - return { w, h } - }) - imp.banner = { - format - } - } - const videoParams = deepAccess(bid, 'mediaTypes.video') - if (videoParams) { - imp.video = videoParams - } - return imp - }) -} \ No newline at end of file +function getImps (validBidRequests, common) { + return validBidRequests.map((bid, id) => { + const floorInfo = bid.getFloor + ? bid.getFloor({ currency: common.currency || 'EUR' }) + : {} + const bidfloor = floorInfo.floor + const bidfloorcur = floorInfo.currency + const { ctok, placementId } = bid.params + const imp = { + id: id + 1, + ctok, + bidfloor, + bidfloorcur, + ...common + } + const bannerParams = deepAccess(bid, 'mediaTypes.banner') + if (bannerParams && bannerParams.sizes) { + const sizes = parseSizesInput(bannerParams.sizes) + const format = sizes.map(size => { + const [width, height] = size.split('x') + const w = parseInt(width, 10) + const h = parseInt(height, 10) + return { w, h } + }) + imp.banner = { + format + } + } + if (placementId) { + imp.placementId = placementId + } + const videoParams = deepAccess(bid, 'mediaTypes.video') + if (videoParams) { + imp.video = videoParams + } + return imp + }) +} From f5f4e5b7b5556bbbe763372b75691891d8f15c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Mon, 8 Aug 2022 11:37:17 +0200 Subject: [PATCH 05/22] made schain optional --- modules/carodaBidAdapter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 53e0c1deeea..cec5cf9e807 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -45,12 +45,14 @@ export const spec = { currency, hb_version: '$prebid.version$', ...ortbCommon, - price_type: priceType, - schain + price_type: priceType } if (test) { request.test = 1 } + if (schain) { + request.schain = schain + } if (config.getConfig('coppa')) { deepSetValue(request, 'privacy.coppa', 1) } From a6f76c1c3bdcf9fc6c468db809a1bd07baf72e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Mon, 8 Aug 2022 17:05:19 +0200 Subject: [PATCH 06/22] tests WIP --- modules/carodaBidAdapter.js | 2 +- test/spec/modules/carodaBidAdapter_spec.js | 1052 ++++++++++++++++++++ 2 files changed, 1053 insertions(+), 1 deletion(-) create mode 100644 test/spec/modules/carodaBidAdapter_spec.js diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index cec5cf9e807..fb33c9f6831 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -20,7 +20,6 @@ const carodaDomain = 'prebid.caroda.io' // some state info is required to synchronize with Caroda ad server const topUsableWindow = getTopUsableWindow() -const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) export const spec = { code: BIDDER_CODE, @@ -32,6 +31,7 @@ export const spec = { return ctok && placementId }, buildRequests: (validBidRequests, bidderRequest) => { + const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) const ortbCommon = getORTBCommon(bidderRequest) const priceType = setOnAny(validBidRequests, 'params.priceType') || diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js new file mode 100644 index 00000000000..23cdcf7a992 --- /dev/null +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -0,0 +1,1052 @@ +// jshint esversion: 6, es3: false, node: true +import { assert } from 'chai'; +import { spec } from 'modules/carodaBidAdapter.js'; +import { config } from 'src/config.js'; +import { createEidsArray } from 'modules/userId/eids.js'; + +describe('Caroda adapter', function () { + let bids = []; + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'caroda', + 'params': { + 'ctok': 'adf232eef344' + } + }; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + + bid.params = { + ctok: 'adf232eef344', + placementId: 'someplacement' + }; + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when required params are missing', function () { + bid.params = {}; + assert.isFalse(spec.isBidRequestValid(bid)); + + bid.params = { + placementId: 'someplacement' + }; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + delete window.carodaPageViewId + }); + it('should send request with minimal structure', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { + 'ctok': 'adf232eef344' + } + }]; + window.top.carodaPageViewId = 12345 + const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0]; + assert.equal(request.method, 'POST'); + assert.equal(request.url, 'https://prebid.caroda.io/api/hb?entry_id=12345'); + assert.equal(request.options, undefined); + assert.ok(request.data); + }); + + it.only('should add test to request, if test is set in parameters', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { + 'ctok': 'adf232eef344', + 'test': 1 + } + }]; + window.top.carodaPageViewId = 12345 + const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0]; + console.error(request) + assert.equal(request.test, 1); + }); + + describe('user privacy', function () { + it('should send GDPR Consent data to adform if gdprApplies', function () { + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); + assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); + assert.equal(typeof request.regs.ext.gdpr, 'number'); + }); + + it('should send gdpr as number', function () { + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(typeof request.regs.ext.gdpr, 'number'); + assert.equal(request.regs.ext.gdpr, 1); + }); + + it('should send CCPA Consent data to adform', function () { + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { uspConsent: '1YA-', refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.ext.us_privacy, '1YA-'); + + bidderRequest = { uspConsent: '1YA-', gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.ext.us_privacy, '1YA-'); + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 1); + }); + + it('should not send GDPR Consent data to adform if gdprApplies is undefined', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let bidderRequest = { gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 0); + + bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: { page: 'page' }}; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + it('should send default GDPR Consent data to adform', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + }); + + + it('should have default request structure', function () { + let keys = 'site,device,source,ext,imp'.split(','); + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + let data = Object.keys(request); + + assert.deepEqual(keys, data); + }); + + it('should set request keys correct values', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' }, + transactionId: 'transactionId' + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.source.tid, validBidRequests[0].transactionId); + assert.equal(request.source.fd, 1); + }); + + it('should not set coppa when coppa is not provided or is set to false', function () { + config.setConfig({ + }); + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.coppa, undefined); + + config.setConfig({ + coppa: false + }); + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.coppa, undefined); + }); + + it('should set coppa to 1 when coppa is provided with value true', function () { + config.setConfig({ + coppa: true + }); + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.regs.coppa, 1); + }); + + it('should send info about device', function () { + config.setConfig({ + device: { w: 100, h: 100 } + }); + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: '1000' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.device.ua, navigator.userAgent); + assert.equal(request.device.w, 100); + assert.equal(request.device.h, 100); + }); + + it('should send app info', function () { + config.setConfig({ + app: { id: 'appid' }, + }); + const ortb2 = { app: { name: 'appname' } }; + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: '1000' }, + ortb2 + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, ortb2 }).data); + + assert.equal(request.app.id, 'appid'); + assert.equal(request.app.name, 'appname'); + assert.equal(request.site, undefined); + }); + + it('should send info about the site', function () { + config.setConfig({ + site: { + id: '123123', + publisher: { + domain: 'publisher.domain.com' + } + }, + }); + const ortb2 = { + site: { + publisher: { + name: 'publisher\'s name' + } + } + }; + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: '1000' }, + ortb2 + }]; + let refererInfo = { page: 'page' }; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo, ortb2 }).data); + + assert.deepEqual(request.site, { + page: refererInfo.page, + publisher: { + domain: 'publisher.domain.com', + name: 'publisher\'s name' + }, + id: '123123' + }); + }); + + it('should pass extended ids', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + userIdAsEids: createEidsArray({ + tdid: 'TTD_ID_FROM_USER_ID_MODULE', + pubcid: 'pubCommonId_FROM_USER_ID_MODULE' + }) + }]; + + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + assert.deepEqual(request.user.ext.eids, [ + { source: 'adserver.org', uids: [ { id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } } ] }, + { source: 'pubcid.org', uids: [ { id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 } ] } + ]); + }); + + it('should send currency if defined', function () { + config.setConfig({ currency: { adServerCurrency: 'EUR' } }); + let validBidRequests = [{ params: {} }]; + let refererInfo = { page: 'page' }; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data); + + assert.deepEqual(request.cur, [ 'EUR' ]); + }); + + it('should pass supply chain object', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + }]; + + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + assert.deepEqual(request.source.ext.schain, { + validation: 'strict', + config: { + ver: '1.0' + } + }); + }); + + describe('priceType', function () { + it('should send default priceType', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.ext.pt, 'net'); + }); + it('should send correct priceType value', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { priceType: 'net' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.ext.pt, 'net'); + }); + }); + + describe('bids', function () { + it('should add more than one bid to the request', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }, { + bidId: 'bidId2', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.imp.length, 2); + }); + it('should add incrementing values of id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: '1000' }, + mediaTypes: {video: {}} + }, { + bidId: 'bidId2', + params: { mid: '1000' }, + mediaTypes: {video: {}} + }, { + bidId: 'bidId3', + params: { mid: '1000' }, + mediaTypes: {video: {}} + }]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; + + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].id, i + 1); + } + }); + + it('should add mid', function () { + let validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }, + { bidId: 'bidId2', params: {mid: 1001}, mediaTypes: {video: {}} }, + { bidId: 'bidId3', params: {mid: 1002}, mediaTypes: {video: {}} }]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].tagid, validBidRequests[i].params.mid); + } + }); + + describe('dynamic placement tag', function () { + it('should add imp parameters correctly', function () { + const validBidRequests = [ + { bidId: 'bidId', params: { inv: 1000, mname: 'placement' }, mediaTypes: {video: {}} }, + { bidId: 'bidId', params: { mid: 1234, inv: 1002, mname: 'placement2' }, mediaTypes: {video: {}} }, + { bidId: 'bidId', params: { mid: 1234 }, mediaTypes: {video: {}} } + ]; + const [ imp1, imp2, imp3 ] = getRequestImps(validBidRequests); + + assert.equal(imp1.ext.bidder.inv, 1000); + assert.equal(imp1.ext.bidder.mname, 'placement'); + assert.equal('tagid' in imp1, false); + + assert.equal(imp2.ext.bidder.inv, 1002); + assert.equal(imp2.ext.bidder.mname, 'placement2'); + assert.equal(imp2.tagid, 1234); + + assert.ok(imp3.ext.bidder); + assert.equal('inv' in imp3.ext.bidder, false); + assert.equal('mname' in imp3.ext.bidder, false); + assert.equal(imp3.tagid, 1234); + }); + }); + + describe('price floors', function () { + it('should not add if floors module not configured', function () { + const validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, undefined); + }); + + it('should not add if floor price not defined', function () { + const validBidRequests = [ getBidWithFloor() ]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'USD'); + }); + + it('should request floor price in adserver currency', function () { + config.setConfig({ currency: { adServerCurrency: 'DKK' } }); + const validBidRequests = [ getBidWithFloor() ]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'DKK'); + }); + + it('should add correct floor values', function () { + const expectedFloors = [ 1, 1.3, 0.5 ]; + const validBidRequests = expectedFloors.map(getBidWithFloor); + let imps = getRequestImps(validBidRequests); + + expectedFloors.forEach((floor, index) => { + assert.equal(imps[index].bidfloor, floor); + assert.equal(imps[index].bidfloorcur, 'USD'); + }); + }); + + function getBidWithFloor(floor) { + return { + params: { mid: 1 }, + mediaTypes: { video: {} }, + getFloor: ({ currency }) => { + return { + currency: currency, + floor + }; + } + }; + } + }); + + describe('multiple media types', function () { + it('should use all configured media types for bidding', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + video: {} + } + }, { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaTypes: { + video: {}, + native: {} + } + }, { + bidId: 'bidId2', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 } + }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + native: {}, + video: {} + } + }]; + let [ first, second, third ] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; + + assert.ok(first.banner); + assert.ok(first.video); + assert.equal(first.native, undefined); + + assert.ok(second.video); + assert.equal(second.banner, undefined); + assert.equal(second.native, undefined); + + assert.ok(third.native); + assert.ok(third.video); + assert.ok(third.banner); + }); + }); + + describe('banner', function () { + it('should convert sizes to openrtb format', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + } + } + }]; + let { banner } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0]; + assert.deepEqual(banner, { + format: [ { w: 100, h: 100 }, { w: 200, h: 300 } ] + }); + }); + }); + + describe('video', function () { + it('should pass video mediatype config', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + } + } + }]; + let { video } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0]; + assert.deepEqual(video, { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + }); + }); + }); + + describe('native', function () { + describe('assets', function () { + it('should set correct asset id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }]; + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; + + assert.equal(assets[0].id, 0); + assert.equal(assets[1].id, 3); + assert.equal(assets[2].id, 4); + }); + it('should add required key if it is necessary', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 }, + sponsoredBy: { required: true, len: 140 } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; + + assert.equal(assets[0].required, 1); + assert.ok(!assets[1].required); + assert.ok(!assets[2].required); + assert.equal(assets[3].required, 1); + }); + + it('should map img and data assets', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: true, sizes: [150, 50] }, + icon: { required: false, sizes: [50, 50] }, + body: { required: false, len: 140 }, + sponsoredBy: { required: true }, + cta: { required: false }, + clickUrl: { required: false } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].title); + assert.equal(assets[0].title.len, 140); + assert.deepEqual(assets[1].img, { type: 3, w: 150, h: 50 }); + assert.deepEqual(assets[2].img, { type: 1, w: 50, h: 50 }); + assert.deepEqual(assets[3].data, { type: 2, len: 140 }); + assert.deepEqual(assets[4].data, { type: 1 }); + assert.deepEqual(assets[5].data, { type: 12 }); + assert.ok(!assets[6]); + }); + + describe('icon/image sizing', function () { + it('should flatten sizes and utilise first pair', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + nativeParams: { + image: { + sizes: [[200, 300], [100, 200]] + }, + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.w, 200); + assert.equal(assets[0].img.h, 300); + }); + }); + + it('should utilise aspect_ratios', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + nativeParams: { + image: { + aspect_ratios: [{ + min_width: 100, + ratio_height: 3, + ratio_width: 1 + }] + }, + icon: { + aspect_ratios: [{ + min_width: 10, + ratio_height: 5, + ratio_width: 2 + }] + } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 100); + assert.equal(assets[0].img.hmin, 300); + + assert.ok(assets[1].img); + assert.equal(assets[1].img.wmin, 10); + assert.equal(assets[1].img.hmin, 25); + }); + + it('should not throw error if aspect_ratios config is not defined', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + nativeParams: { + image: { + aspect_ratios: [] + }, + icon: { + aspect_ratios: [] + } + } + }]; + + assert.doesNotThrow(() => spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })); + }); + }); + + it('should expect any dimensions if min_width not passed', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + nativeParams: { + image: { + aspect_ratios: [{ + ratio_height: 3, + ratio_width: 1 + }] + } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 0); + assert.equal(assets[0].img.hmin, 0); + assert.ok(!assets[1]); + }); + }); + }); + + function getRequestImps(validBidRequests) { + return JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; + } + }); + + describe.skip('interpretResponse', function () { + it('should return if no body in response', function () { + let serverResponse = {}; + let bidRequest = {}; + + assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); + }); + it('should return more than one bids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{impid: '1', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}}] + }, { + bid: [{impid: '2', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 1, data: {value: 'Asset title text'}}]}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId2', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(spec.interpretResponse(serverResponse, bidRequest).length, 2); + }); + + it('should parse seatbids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [ + {impid: '1', native: {ver: '1.1', link: { url: 'link1' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}}, + {impid: '4', native: {ver: '1.1', link: { url: 'link4' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}} + ] + }, { + bid: [{impid: '2', native: {ver: '1.1', link: { url: 'link2' }, assets: [{id: 1, data: {value: 'Asset title text'}}]}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId2', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId3', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId4', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest).map(bid => { + const { requestId, native: { clickUrl } } = bid; + return [ requestId, clickUrl ]; + }); + + assert.equal(bids.length, 3); + assert.deepEqual(bids, [[ 'bidId1', 'link1' ], [ 'bidId2', 'link2' ], [ 'bidId4', 'link4' ]]); + }); + + it('should set correct values to bid', function () { + let serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ + bid: [ + { + impid: '1', + price: 93.1231, + crid: '12312312', + native: { + assets: [], + link: { url: 'link' }, + imptrackers: ['imptrackers url1', 'imptrackers url2'] + }, + dealid: 'deal-id', + adomain: [ 'demo.com' ], + ext: { + prebid: { + type: 'native' + } + } + } + ] + }], + cur: 'NOK' + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + } + ] + }; + + const bids = spec.interpretResponse(serverResponse, bidRequest); + const bid = serverResponse.body.seatbid[0].bid[0]; + assert.deepEqual(bids[0].requestId, bidRequest.bids[0].bidId); + assert.deepEqual(bids[0].cpm, bid.price); + assert.deepEqual(bids[0].creativeId, bid.crid); + assert.deepEqual(bids[0].ttl, 360); + assert.deepEqual(bids[0].netRevenue, false); + assert.deepEqual(bids[0].currency, serverResponse.body.cur); + assert.deepEqual(bids[0].mediaType, 'native'); + assert.deepEqual(bids[0].meta.mediaType, 'native'); + assert.deepEqual(bids[0].meta.advertiserDomains, [ 'demo.com' ]); + assert.deepEqual(bids[0].dealId, 'deal-id'); + }); + it('should set correct native params', function () { + const bid = [ + { + impid: '1', + price: 93.1231, + crid: '12312312', + native: { + assets: [ + { + data: null, + id: 0, + img: null, + required: 0, + title: {text: 'title', len: null}, + video: null + }, { + data: null, + id: 2, + img: {type: null, url: 'test.url.com/Files/58345/308185.jpg?bv=1', w: 30, h: 10}, + required: 0, + title: null, + video: null + }, { + data: null, + id: 3, + img: {type: null, url: 'test.url.com/Files/58345/308200.jpg?bv=1', w: 100, h: 100}, + required: 0, + title: null, + video: null + }, { + data: {type: null, len: null, value: 'body'}, + id: 4, + img: null, + required: 0, + title: null, + video: null + }, { + data: {type: null, len: null, value: 'cta'}, + id: 1, + img: null, + required: 0, + title: null, + video: null + }, { + data: {type: null, len: null, value: 'sponsoredBy'}, + id: 5, + img: null, + required: 0, + title: null, + video: null + } + ], + link: { url: 'clickUrl', clicktrackers: ['clickTracker1', 'clickTracker2'] }, + imptrackers: ['imptrackers url1', 'imptrackers url2'], + jstracker: 'jstracker' + } + } + ]; + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ bid }], + cur: 'NOK' + } + }; + let bidRequest = { + data: {}, + bids: [{ bidId: 'bidId1' }] + }; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0].native; + const native = bid[0].native; + const assets = native.assets; + assert.deepEqual({ + clickUrl: native.link.url, + clickTrackers: native.link.clicktrackers, + impressionTrackers: native.imptrackers, + javascriptTrackers: [ native.jstracker ], + title: assets[0].title.text, + icon: {url: assets[1].img.url, width: assets[1].img.w, height: assets[1].img.h}, + image: {url: assets[2].img.url, width: assets[2].img.w, height: assets[2].img.h}, + body: assets[3].data.value, + cta: assets[4].data.value, + sponsoredBy: assets[5].data.value + }, result); + }); + it('should return empty when there is no bids in response', function () { + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ bid: [] }], + cur: 'NOK' + } + }; + let bidRequest = { + data: {}, + bids: [{ bidId: 'bidId1' }] + }; + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.ok(!result); + }); + + describe('banner', function () { + it('should set ad content on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'banner' } } }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].ad, ''); + assert.equal(bids[0].mediaType, 'banner'); + assert.equal(bids[0].meta.mediaType, 'banner'); + }); + }); + + describe('video', function () { + it('should set vastXml on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'video' } } }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].vastXml, ''); + assert.equal(bids[0].mediaType, 'video'); + assert.equal(bids[0].meta.mediaType, 'video'); + }); + + it('should add renderer for outstream bids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'video' } } }, { impid: '2', adm: '', ext: { prebid: { type: 'video' } } }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaTypes: { + video: { + context: 'outstream' + } + } + }, + { + bidId: 'bidId2', + params: { mid: 1000 }, + mediaTypes: { + video: { + constext: 'instream' + } + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.ok(bids[0].renderer); + assert.equal(bids[1].renderer, undefined); + }); + }); + }); +}); From 555cde7189b5cbe7057e9feb992ac79f4879bb94 Mon Sep 17 00:00:00 2001 From: Ioan-Carol Plangu Date: Tue, 9 Aug 2022 11:57:35 +0200 Subject: [PATCH 07/22] fixed bid validation --- modules/carodaBidAdapter.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index fb33c9f6831..d4412d4417f 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -28,10 +28,12 @@ export const spec = { isBidRequestValid: bid => { const params = bid.params || {} const { ctok, placementId } = params - return ctok && placementId + return typeof ctok === 'string' && ( + typeof placementId === 'string' || + typeof placementId === 'undefined') }, buildRequests: (validBidRequests, bidderRequest) => { - const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) + const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) const ortbCommon = getORTBCommon(bidderRequest) const priceType = setOnAny(validBidRequests, 'params.priceType') || @@ -194,7 +196,7 @@ function getImps (validBidRequests, common) { } } if (placementId) { - imp.placementId = placementId + imp.placement_id = placementId } const videoParams = deepAccess(bid, 'mediaTypes.video') if (videoParams) { From 1f5a85f2e941eaa819ffb2a87a00ea0826f57201 Mon Sep 17 00:00:00 2001 From: Ioan-Carol Plangu Date: Tue, 9 Aug 2022 11:58:23 +0200 Subject: [PATCH 08/22] request common structure tests --- test/spec/modules/carodaBidAdapter_spec.js | 354 ++++++++------------- 1 file changed, 141 insertions(+), 213 deletions(-) diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index 23cdcf7a992..d2c3fe44e47 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -53,10 +53,15 @@ describe('Caroda adapter', function () { assert.equal(request.method, 'POST'); assert.equal(request.url, 'https://prebid.caroda.io/api/hb?entry_id=12345'); assert.equal(request.options, undefined); - assert.ok(request.data); + const data = JSON.parse(request.data) + assert.equal(data.ctok, 'adf232eef344'); + assert.ok(data.site); + assert.ok(data.hb_version); + assert.ok(data.device); + assert.equal(data.price_type, 'net'); }); - - it.only('should add test to request, if test is set in parameters', function () { + + it('should add test to request, if test is set in parameters', function () { const validBidRequests = [{ bidId: 'bidId', params: { @@ -66,139 +71,57 @@ describe('Caroda adapter', function () { }]; window.top.carodaPageViewId = 12345 const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0]; - console.error(request) - assert.equal(request.test, 1); - }); - - describe('user privacy', function () { - it('should send GDPR Consent data to adform if gdprApplies', function () { - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); - assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); - assert.equal(typeof request.regs.ext.gdpr, 'number'); - }); - - it('should send gdpr as number', function () { - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(typeof request.regs.ext.gdpr, 'number'); - assert.equal(request.regs.ext.gdpr, 1); - }); - - it('should send CCPA Consent data to adform', function () { - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { uspConsent: '1YA-', refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.ext.us_privacy, '1YA-'); - - bidderRequest = { uspConsent: '1YA-', gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.ext.us_privacy, '1YA-'); - assert.equal(request.user.ext.consent, 'consentDataString'); - assert.equal(request.regs.ext.gdpr, 1); - }); - - it('should not send GDPR Consent data to adform if gdprApplies is undefined', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { siteId: 'siteId' } - }]; - let bidderRequest = { gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user.ext.consent, 'consentDataString'); - assert.equal(request.regs.ext.gdpr, 0); - - bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: { page: 'page' }}; - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); - it('should send default GDPR Consent data to adform', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { siteId: 'siteId' } - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); - }); - - - it('should have default request structure', function () { - let keys = 'site,device,source,ext,imp'.split(','); - let validBidRequests = [{ - bidId: 'bidId', - params: { siteId: 'siteId' } - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - let data = Object.keys(request); - - assert.deepEqual(keys, data); + const data = JSON.parse(request.data) + assert.equal(data.test, 1); }); - it('should set request keys correct values', function () { - let validBidRequests = [{ + it('should add placement_id to request when available', function () { + const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId' }, - transactionId: 'transactionId' + params: { + 'ctok': 'adf232eef344', + 'placementId': 'opzafe342f' + } }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - - assert.equal(request.source.tid, validBidRequests[0].transactionId); - assert.equal(request.source.fd, 1); - }); - - it('should not set coppa when coppa is not provided or is set to false', function () { - config.setConfig({ - }); - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.coppa, undefined); - - config.setConfig({ - coppa: false - }); - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.coppa, undefined); - }); - - it('should set coppa to 1 when coppa is provided with value true', function () { - config.setConfig({ - coppa: true - }); - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - - assert.equal(request.regs.coppa, 1); + window.top.carodaPageViewId = 12345 + const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0]; + const data = JSON.parse(request.data) + assert.equal(data.placement_id, 'opzafe342f'); }); - + it('should send info about device', function () { config.setConfig({ device: { w: 100, h: 100 } }); - let validBidRequests = [{ + const validBidRequests = [{ + bidId: 'bidId', + params: { 'ctok': 'adf232eef344' } + }]; + const data = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); + assert.equal(data.device.ua, navigator.userAgent); + assert.equal(data.device.w, 100); + assert.equal(data.device.h, 100); + }); + + it('should pass supply chain object', function () { + const validBidRequests = [{ bidId: 'bidId', - params: { mid: '1000' } + params: {}, + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - assert.equal(request.device.ua, navigator.userAgent); - assert.equal(request.device.w, 100); - assert.equal(request.device.h, 100); + let data = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); + assert.deepEqual(data.schain, { + validation: 'strict', + config: { + ver: '1.0' + } + }); }); it('should send app info', function () { @@ -211,8 +134,7 @@ describe('Caroda adapter', function () { params: { mid: '1000' }, ortb2 }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, ortb2 }).data); - + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, ortb2 })[0].data); assert.equal(request.app.id, 'appid'); assert.equal(request.app.name, 'appname'); assert.equal(request.site, undefined); @@ -240,7 +162,7 @@ describe('Caroda adapter', function () { ortb2 }]; let refererInfo = { page: 'page' }; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo, ortb2 }).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo, ortb2 })[0].data); assert.deepEqual(request.site, { page: refererInfo.page, @@ -252,75 +174,132 @@ describe('Caroda adapter', function () { }); }); - it('should pass extended ids', function () { + it('should send correct priceType value', function () { let validBidRequests = [{ bidId: 'bidId', - params: {}, - userIdAsEids: createEidsArray({ - tdid: 'TTD_ID_FROM_USER_ID_MODULE', - pubcid: 'pubCommonId_FROM_USER_ID_MODULE' - }) + params: { priceType: 'gross' } }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - assert.deepEqual(request.user.ext.eids, [ - { source: 'adserver.org', uids: [ { id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } } ] }, - { source: 'pubcid.org', uids: [ { id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 } ] } - ]); + assert.equal(request.price_type, 'gross'); }); it('should send currency if defined', function () { config.setConfig({ currency: { adServerCurrency: 'EUR' } }); let validBidRequests = [{ params: {} }]; let refererInfo = { page: 'page' }; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo })[0].data); - assert.deepEqual(request.cur, [ 'EUR' ]); + assert.deepEqual(request.currency, 'EUR'); }); - it('should pass supply chain object', function () { + it('should pass extended ids', function () { let validBidRequests = [{ bidId: 'bidId', params: {}, - schain: { - validation: 'strict', - config: { - ver: '1.0' - } - } + userIdAsEids: createEidsArray({ + tdid: 'TTD_ID_FROM_USER_ID_MODULE', + pubcid: 'pubCommonId_FROM_USER_ID_MODULE' + }) }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - assert.deepEqual(request.source.ext.schain, { - validation: 'strict', - config: { - ver: '1.0' - } - }); + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); + assert.deepEqual(request.user.eids, [ + { source: 'adserver.org', uids: [ { id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } } ] }, + { source: 'pubcid.org', uids: [ { id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 } ] } + ]); }); - describe('priceType', function () { - it('should send default priceType', function () { + describe.skip('user privacy', function () { + it('should send GDPR Consent data to adform if gdprApplies', function () { + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); + assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); + assert.equal(typeof request.regs.ext.gdpr, 'number'); + }); + + it('should send gdpr as number', function () { + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(typeof request.regs.ext.gdpr, 'number'); + assert.equal(request.regs.ext.gdpr, 1); + }); + + it('should send CCPA Consent data to adform', function () { + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { uspConsent: '1YA-', refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.ext.us_privacy, '1YA-'); + + bidderRequest = { uspConsent: '1YA-', gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.ext.us_privacy, '1YA-'); + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 1); + }); + + it('should not send GDPR Consent data to adform if gdprApplies is undefined', function () { let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId' } }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + let bidderRequest = { gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 0); - assert.equal(request.ext.pt, 'net'); + bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: { page: 'page' }}; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); }); - it('should send correct priceType value', function () { + it('should send default GDPR Consent data to adform', function () { let validBidRequests = [{ bidId: 'bidId', - params: { priceType: 'net' } + params: { siteId: 'siteId' } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - assert.equal(request.ext.pt, 'net'); + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + it('should not set coppa when coppa is not provided or is set to false', function () { + config.setConfig({ + }); + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.coppa, undefined); + + config.setConfig({ + coppa: false + }); + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.coppa, undefined); + }); + it('should set coppa to 1 when coppa is provided with value true', function () { + config.setConfig({ + coppa: true + }); + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + + assert.equal(request.regs.coppa, 1); }); }); - describe('bids', function () { + describe.skip('bids', function () { it('should add more than one bid to the request', function () { let validBidRequests = [{ bidId: 'bidId', @@ -388,57 +367,6 @@ describe('Caroda adapter', function () { }); }); - describe('price floors', function () { - it('should not add if floors module not configured', function () { - const validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }]; - let imp = getRequestImps(validBidRequests)[0]; - - assert.equal(imp.bidfloor, undefined); - assert.equal(imp.bidfloorcur, undefined); - }); - - it('should not add if floor price not defined', function () { - const validBidRequests = [ getBidWithFloor() ]; - let imp = getRequestImps(validBidRequests)[0]; - - assert.equal(imp.bidfloor, undefined); - assert.equal(imp.bidfloorcur, 'USD'); - }); - - it('should request floor price in adserver currency', function () { - config.setConfig({ currency: { adServerCurrency: 'DKK' } }); - const validBidRequests = [ getBidWithFloor() ]; - let imp = getRequestImps(validBidRequests)[0]; - - assert.equal(imp.bidfloor, undefined); - assert.equal(imp.bidfloorcur, 'DKK'); - }); - - it('should add correct floor values', function () { - const expectedFloors = [ 1, 1.3, 0.5 ]; - const validBidRequests = expectedFloors.map(getBidWithFloor); - let imps = getRequestImps(validBidRequests); - - expectedFloors.forEach((floor, index) => { - assert.equal(imps[index].bidfloor, floor); - assert.equal(imps[index].bidfloorcur, 'USD'); - }); - }); - - function getBidWithFloor(floor) { - return { - params: { mid: 1 }, - mediaTypes: { video: {} }, - getFloor: ({ currency }) => { - return { - currency: currency, - floor - }; - } - }; - } - }); - describe('multiple media types', function () { it('should use all configured media types for bidding', function () { let validBidRequests = [{ From 9ad564762cdfcd3a1112a7bbe042df0d33f33005 Mon Sep 17 00:00:00 2001 From: Ioan-Carol Plangu Date: Tue, 9 Aug 2022 12:08:47 +0200 Subject: [PATCH 09/22] added privacy tests --- test/spec/modules/carodaBidAdapter_spec.js | 69 +++++++--------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index d2c3fe44e47..536b9e53c58 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -210,92 +210,65 @@ describe('Caroda adapter', function () { ]); }); - describe.skip('user privacy', function () { + describe('user privacy', function () { it('should send GDPR Consent data to adform if gdprApplies', function () { let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); - assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); - assert.equal(typeof request.regs.ext.gdpr, 'number'); + assert.equal(request.privacy.gdpr_consent, bidderRequest.gdprConsent.consentString); + assert.equal(request.privacy.gdpr, bidderRequest.gdprConsent.gdprApplies); + assert.equal(typeof request.privacy.gdpr, 'number'); }); it('should send gdpr as number', function () { let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - assert.equal(typeof request.regs.ext.gdpr, 'number'); - assert.equal(request.regs.ext.gdpr, 1); + assert.equal(typeof request.privacy.gdpr, 'number'); + assert.equal(request.privacy.gdpr, 1); }); - it('should send CCPA Consent data to adform', function () { + it('should send CCPA Consent data', function () { let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { uspConsent: '1YA-', refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - assert.equal(request.regs.ext.us_privacy, '1YA-'); + assert.equal(request.privacy.us_privacy, '1YA-'); bidderRequest = { uspConsent: '1YA-', gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - assert.equal(request.regs.ext.us_privacy, '1YA-'); - assert.equal(request.user.ext.consent, 'consentDataString'); - assert.equal(request.regs.ext.gdpr, 1); + assert.equal(request.privacy.us_privacy, '1YA-'); + assert.equal(request.privacy.gdpr_consent, 'consentDataString'); + assert.equal(request.privacy.gdpr, 1); }); - it('should not send GDPR Consent data to adform if gdprApplies is undefined', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { siteId: 'siteId' } - }]; - let bidderRequest = { gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user.ext.consent, 'consentDataString'); - assert.equal(request.regs.ext.gdpr, 0); - - bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: { page: 'page' }}; - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); - it('should send default GDPR Consent data to adform', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { siteId: 'siteId' } - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); it('should not set coppa when coppa is not provided or is set to false', function () { config.setConfig({ }); let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - assert.equal(request.regs.coppa, undefined); + assert.equal(request.privacy.coppa, undefined); config.setConfig({ coppa: false }); - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - assert.equal(request.regs.coppa, undefined); + assert.equal(request.privacy.coppa, undefined); }); it('should set coppa to 1 when coppa is provided with value true', function () { config.setConfig({ coppa: true }); let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); - assert.equal(request.regs.coppa, 1); + assert.equal(request.privacy.coppa, 1); }); }); From 3b9baf60812bd064a996f43aec7f8f37575c4312 Mon Sep 17 00:00:00 2001 From: Ioan-Carol Plangu Date: Tue, 9 Aug 2022 12:55:54 +0200 Subject: [PATCH 10/22] added bid specific logic tests --- modules/carodaBidAdapter.js | 3 +- test/spec/modules/carodaBidAdapter_spec.js | 310 +++++---------------- 2 files changed, 67 insertions(+), 246 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index d4412d4417f..de3701e0dec 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -168,7 +168,7 @@ function getORTBCommon (bidderRequest) { } function getImps (validBidRequests, common) { - return validBidRequests.map((bid, id) => { + return validBidRequests.map((bid) => { const floorInfo = bid.getFloor ? bid.getFloor({ currency: common.currency || 'EUR' }) : {} @@ -176,7 +176,6 @@ function getImps (validBidRequests, common) { const bidfloorcur = floorInfo.currency const { ctok, placementId } = bid.params const imp = { - id: id + 1, ctok, bidfloor, bidfloorcur, diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index 536b9e53c58..21e143b576f 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -272,79 +272,77 @@ describe('Caroda adapter', function () { }); }); - describe.skip('bids', function () { - it('should add more than one bid to the request', function () { - let validBidRequests = [{ + describe('bids', function () { + it('should be able to handle multiple bids', function () { + const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId' } + params: { ctok: 'ctok1' } }, { bidId: 'bidId2', - params: { siteId: 'siteId' } + params: { ctok: 'ctok2' } }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - - assert.equal(request.imp.length, 2); + const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }); + assert.equal(request.length, 2); + const data = request.map(r => JSON.parse(r.data)) + assert.equal(data[0].ctok, 'ctok1') + assert.equal(data[1].ctok, 'ctok2') }); - it('should add incrementing values of id', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { mid: '1000' }, - mediaTypes: {video: {}} - }, { - bidId: 'bidId2', - params: { mid: '1000' }, - mediaTypes: {video: {}} - }, { - bidId: 'bidId3', - params: { mid: '1000' }, - mediaTypes: {video: {}} - }]; - let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; - for (let i = 0; i < 3; i++) { - assert.equal(imps[i].id, i + 1); - } - }); + + describe('price floors', function () { + it('should not add if floors module not configured', function () { + const validBidRequests = [{ bidId: 'bidId', params: {ctok: 'ctok1'}, mediaTypes: {video: {}} }]; + const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, undefined); + }); - it('should add mid', function () { - let validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }, - { bidId: 'bidId2', params: {mid: 1001}, mediaTypes: {video: {}} }, - { bidId: 'bidId3', params: {mid: 1002}, mediaTypes: {video: {}} }]; - let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; - for (let i = 0; i < 3; i++) { - assert.equal(imps[i].tagid, validBidRequests[i].params.mid); - } - }); + it('should not add if floor price not defined', function () { + const validBidRequests = [ getBidWithFloor() ]; + const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'EUR'); + }); - describe('dynamic placement tag', function () { - it('should add imp parameters correctly', function () { - const validBidRequests = [ - { bidId: 'bidId', params: { inv: 1000, mname: 'placement' }, mediaTypes: {video: {}} }, - { bidId: 'bidId', params: { mid: 1234, inv: 1002, mname: 'placement2' }, mediaTypes: {video: {}} }, - { bidId: 'bidId', params: { mid: 1234 }, mediaTypes: {video: {}} } - ]; - const [ imp1, imp2, imp3 ] = getRequestImps(validBidRequests); - - assert.equal(imp1.ext.bidder.inv, 1000); - assert.equal(imp1.ext.bidder.mname, 'placement'); - assert.equal('tagid' in imp1, false); - - assert.equal(imp2.ext.bidder.inv, 1002); - assert.equal(imp2.ext.bidder.mname, 'placement2'); - assert.equal(imp2.tagid, 1234); - - assert.ok(imp3.ext.bidder); - assert.equal('inv' in imp3.ext.bidder, false); - assert.equal('mname' in imp3.ext.bidder, false); - assert.equal(imp3.tagid, 1234); + it('should request floor price in adserver currency', function () { + config.setConfig({ currency: { adServerCurrency: 'DKK' } }); + const validBidRequests = [ getBidWithFloor() ]; + const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'DKK'); }); + + it('should add correct floor values', function () { + const expectedFloors = [ 1, 1.3, 0.5 ]; + const validBidRequests = expectedFloors.map(getBidWithFloor); + const imps = spec + .buildRequests(validBidRequests, { refererInfo: { page: 'page' } }) + .map(r => JSON.parse(r.data)); + expectedFloors.forEach((floor, index) => { + assert.equal(imps[index].bidfloor, floor); + assert.equal(imps[index].bidfloorcur, 'EUR'); + }); + }); + + function getBidWithFloor(floor) { + return { + params: { ctok: 'ctok1' }, + mediaTypes: { video: {} }, + getFloor: ({ currency }) => { + return { + currency: currency, + floor + }; + } + }; + } }); describe('multiple media types', function () { it('should use all configured media types for bidding', function () { - let validBidRequests = [{ + const validBidRequests = [{ bidId: 'bidId', - params: { mid: 1000 }, + params: { ctok: 'ctok1' }, mediaTypes: { banner: { sizes: [[100, 100], [200, 300]] @@ -352,45 +350,28 @@ describe('Caroda adapter', function () { video: {} } }, { - bidId: 'bidId1', - params: { mid: 1000 }, + bidId: 'bidId2', + params: { ctok: 'ctok1' }, mediaTypes: { video: {}, native: {} } - }, { - bidId: 'bidId2', - params: { mid: 1000 }, - nativeParams: { - title: { required: true, len: 140 } - }, - mediaTypes: { - banner: { - sizes: [[100, 100], [200, 300]] - }, - native: {}, - video: {} - } }]; - let [ first, second, third ] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; + const [ first, second ] = spec + .buildRequests(validBidRequests, { refererInfo: { page: 'page' } }) + .map(r => JSON.parse(r.data)); assert.ok(first.banner); assert.ok(first.video); - assert.equal(first.native, undefined); assert.ok(second.video); assert.equal(second.banner, undefined); - assert.equal(second.native, undefined); - - assert.ok(third.native); - assert.ok(third.video); - assert.ok(third.banner); }); }); describe('banner', function () { it('should convert sizes to openrtb format', function () { - let validBidRequests = [{ + const validBidRequests = [{ bidId: 'bidId', params: { mid: 1000 }, mediaTypes: { @@ -399,7 +380,7 @@ describe('Caroda adapter', function () { } } }]; - let { banner } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0]; + const { banner } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); assert.deepEqual(banner, { format: [ { w: 100, h: 100 }, { w: 200, h: 300 } ] }); @@ -408,7 +389,7 @@ describe('Caroda adapter', function () { describe('video', function () { it('should pass video mediatype config', function () { - let validBidRequests = [{ + const validBidRequests = [{ bidId: 'bidId', params: { mid: 1000 }, mediaTypes: { @@ -419,7 +400,7 @@ describe('Caroda adapter', function () { } } }]; - let { video } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0]; + const { video } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); assert.deepEqual(video, { playerSize: [640, 480], context: 'outstream', @@ -427,166 +408,7 @@ describe('Caroda adapter', function () { }); }); }); - - describe('native', function () { - describe('assets', function () { - it('should set correct asset id', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { mid: 1000 }, - nativeParams: { - title: { required: true, len: 140 }, - image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, - body: { len: 140 } - } - }]; - let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; - - assert.equal(assets[0].id, 0); - assert.equal(assets[1].id, 3); - assert.equal(assets[2].id, 4); - }); - it('should add required key if it is necessary', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { mid: 1000 }, - nativeParams: { - title: { required: true, len: 140 }, - image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, - body: { len: 140 }, - sponsoredBy: { required: true, len: 140 } - } - }]; - - let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; - - assert.equal(assets[0].required, 1); - assert.ok(!assets[1].required); - assert.ok(!assets[2].required); - assert.equal(assets[3].required, 1); - }); - - it('should map img and data assets', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { mid: 1000 }, - nativeParams: { - title: { required: true, len: 140 }, - image: { required: true, sizes: [150, 50] }, - icon: { required: false, sizes: [50, 50] }, - body: { required: false, len: 140 }, - sponsoredBy: { required: true }, - cta: { required: false }, - clickUrl: { required: false } - } - }]; - - let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; - assert.ok(assets[0].title); - assert.equal(assets[0].title.len, 140); - assert.deepEqual(assets[1].img, { type: 3, w: 150, h: 50 }); - assert.deepEqual(assets[2].img, { type: 1, w: 50, h: 50 }); - assert.deepEqual(assets[3].data, { type: 2, len: 140 }); - assert.deepEqual(assets[4].data, { type: 1 }); - assert.deepEqual(assets[5].data, { type: 12 }); - assert.ok(!assets[6]); - }); - - describe('icon/image sizing', function () { - it('should flatten sizes and utilise first pair', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: { mid: 1000 }, - nativeParams: { - image: { - sizes: [[200, 300], [100, 200]] - }, - } - }]; - - let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; - assert.ok(assets[0].img); - assert.equal(assets[0].img.w, 200); - assert.equal(assets[0].img.h, 300); - }); - }); - - it('should utilise aspect_ratios', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: { mid: 1000 }, - nativeParams: { - image: { - aspect_ratios: [{ - min_width: 100, - ratio_height: 3, - ratio_width: 1 - }] - }, - icon: { - aspect_ratios: [{ - min_width: 10, - ratio_height: 5, - ratio_width: 2 - }] - } - } - }]; - - let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; - assert.ok(assets[0].img); - assert.equal(assets[0].img.wmin, 100); - assert.equal(assets[0].img.hmin, 300); - - assert.ok(assets[1].img); - assert.equal(assets[1].img.wmin, 10); - assert.equal(assets[1].img.hmin, 25); - }); - - it('should not throw error if aspect_ratios config is not defined', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: { mid: 1000 }, - nativeParams: { - image: { - aspect_ratios: [] - }, - icon: { - aspect_ratios: [] - } - } - }]; - - assert.doesNotThrow(() => spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })); - }); - }); - - it('should expect any dimensions if min_width not passed', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: { mid: 1000 }, - nativeParams: { - image: { - aspect_ratios: [{ - ratio_height: 3, - ratio_width: 1 - }] - } - } - }]; - - let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0].native.request.assets; - assert.ok(assets[0].img); - assert.equal(assets[0].img.wmin, 0); - assert.equal(assets[0].img.hmin, 0); - assert.ok(!assets[1]); - }); - }); }); - - function getRequestImps(validBidRequests) { - return JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; - } }); describe.skip('interpretResponse', function () { From a229d81d9cdf33016784e936cce2fb9bb12544a7 Mon Sep 17 00:00:00 2001 From: Ioan-Carol Plangu Date: Tue, 9 Aug 2022 13:12:25 +0200 Subject: [PATCH 11/22] final unit tests --- modules/carodaBidAdapter.js | 2 +- test/spec/modules/carodaBidAdapter_spec.js | 376 ++------------------- 2 files changed, 36 insertions(+), 342 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index de3701e0dec..5d0fcb57cea 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -96,7 +96,7 @@ export const spec = { return { requestId: bid.bidId, cpm: bid.cpm, - creativeId: bid.crid, + creativeId: bid.creativeId, ttl: 300, netRevenue: true, currency: bid.currency, diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index 21e143b576f..2bbeb0027c3 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -411,365 +411,59 @@ describe('Caroda adapter', function () { }); }); - describe.skip('interpretResponse', function () { + describe('interpretResponse', function () { it('should return if no body in response', function () { - let serverResponse = {}; - let bidRequest = {}; - + const serverResponse = {}; + const bidRequest = {}; assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); }); - it('should return more than one bids', function () { - let serverResponse = { + it('should parse a typical ok response', function () { + const serverResponse = { body: { - seatbid: [{ - bid: [{impid: '1', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}}] - }, { - bid: [{impid: '2', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 1, data: {value: 'Asset title text'}}]}}] - }] + ok: JSON.stringify([{ + bidId: 'bidId', + cpm: 10, + creativeId: '12345', + currency: 'CZK', + w: 100, + h: 100, + ad: ' { - const { requestId, native: { clickUrl } } = bid; - return [ requestId, clickUrl ]; - }); - - assert.equal(bids.length, 3); - assert.deepEqual(bids, [[ 'bidId1', 'link1' ], [ 'bidId2', 'link2' ], [ 'bidId4', 'link4' ]]); - }); - - it('should set correct values to bid', function () { - let serverResponse = { - body: { - id: null, - bidid: null, - seatbid: [{ - bid: [ - { - impid: '1', - price: 93.1231, - crid: '12312312', - native: { - assets: [], - link: { url: 'link' }, - imptrackers: ['imptrackers url1', 'imptrackers url2'] - }, - dealid: 'deal-id', - adomain: [ 'demo.com' ], - ext: { - prebid: { - type: 'native' - } - } - } - ] - }], - cur: 'NOK' - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: { mid: 1000 }, - nativeParams: { - title: { required: true, len: 140 }, - image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, - body: { len: 140 } - } - } - ] - }; - - const bids = spec.interpretResponse(serverResponse, bidRequest); - const bid = serverResponse.body.seatbid[0].bid[0]; - assert.deepEqual(bids[0].requestId, bidRequest.bids[0].bidId); - assert.deepEqual(bids[0].cpm, bid.price); - assert.deepEqual(bids[0].creativeId, bid.crid); - assert.deepEqual(bids[0].ttl, 360); - assert.deepEqual(bids[0].netRevenue, false); - assert.deepEqual(bids[0].currency, serverResponse.body.cur); - assert.deepEqual(bids[0].mediaType, 'native'); - assert.deepEqual(bids[0].meta.mediaType, 'native'); - assert.deepEqual(bids[0].meta.advertiserDomains, [ 'demo.com' ]); - assert.deepEqual(bids[0].dealId, 'deal-id'); - }); - it('should set correct native params', function () { - const bid = [ + assert.equal(spec.interpretResponse(serverResponse, bidRequest).length, 1); + assert.deepEqual( + spec.interpretResponse(serverResponse, bidRequest)[0], { - impid: '1', - price: 93.1231, - crid: '12312312', - native: { - assets: [ - { - data: null, - id: 0, - img: null, - required: 0, - title: {text: 'title', len: null}, - video: null - }, { - data: null, - id: 2, - img: {type: null, url: 'test.url.com/Files/58345/308185.jpg?bv=1', w: 30, h: 10}, - required: 0, - title: null, - video: null - }, { - data: null, - id: 3, - img: {type: null, url: 'test.url.com/Files/58345/308200.jpg?bv=1', w: 100, h: 100}, - required: 0, - title: null, - video: null - }, { - data: {type: null, len: null, value: 'body'}, - id: 4, - img: null, - required: 0, - title: null, - video: null - }, { - data: {type: null, len: null, value: 'cta'}, - id: 1, - img: null, - required: 0, - title: null, - video: null - }, { - data: {type: null, len: null, value: 'sponsoredBy'}, - id: 5, - img: null, - required: 0, - title: null, - video: null - } - ], - link: { url: 'clickUrl', clicktrackers: ['clickTracker1', 'clickTracker2'] }, - imptrackers: ['imptrackers url1', 'imptrackers url2'], - jstracker: 'jstracker' - } - } - ]; - const serverResponse = { - body: { - id: null, - bidid: null, - seatbid: [{ bid }], - cur: 'NOK' - } - }; - let bidRequest = { - data: {}, - bids: [{ bidId: 'bidId1' }] - }; - - const result = spec.interpretResponse(serverResponse, bidRequest)[0].native; - const native = bid[0].native; - const assets = native.assets; - assert.deepEqual({ - clickUrl: native.link.url, - clickTrackers: native.link.clicktrackers, - impressionTrackers: native.imptrackers, - javascriptTrackers: [ native.jstracker ], - title: assets[0].title.text, - icon: {url: assets[1].img.url, width: assets[1].img.w, height: assets[1].img.h}, - image: {url: assets[2].img.url, width: assets[2].img.w, height: assets[2].img.h}, - body: assets[3].data.value, - cta: assets[4].data.value, - sponsoredBy: assets[5].data.value - }, result); - }); - it('should return empty when there is no bids in response', function () { - const serverResponse = { - body: { - id: null, - bidid: null, - seatbid: [{ bid: [] }], - cur: 'NOK' - } - }; - let bidRequest = { - data: {}, - bids: [{ bidId: 'bidId1' }] - }; - const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.ok(!result); - }); - - describe('banner', function () { - it('should set ad content on response', function () { - let serverResponse = { - body: { - seatbid: [{ - bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'banner' } } }] - }] - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: { mid: 1000 } - } - ] - }; - - bids = spec.interpretResponse(serverResponse, bidRequest); - assert.equal(bids.length, 1); - assert.equal(bids[0].ad, ''); - assert.equal(bids[0].mediaType, 'banner'); - assert.equal(bids[0].meta.mediaType, 'banner'); - }); - }); - - describe('video', function () { - it('should set vastXml on response', function () { - let serverResponse = { - body: { - seatbid: [{ - bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'video' } } }] - }] - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: { mid: 1000 } - } - ] - }; - - bids = spec.interpretResponse(serverResponse, bidRequest); - assert.equal(bids.length, 1); - assert.equal(bids[0].vastXml, ''); - assert.equal(bids[0].mediaType, 'video'); - assert.equal(bids[0].meta.mediaType, 'video'); - }); - - it('should add renderer for outstream bids', function () { - let serverResponse = { - body: { - seatbid: [{ - bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'video' } } }, { impid: '2', adm: '', ext: { prebid: { type: 'video' } } }] - }] - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: { mid: 1000 }, - mediaTypes: { - video: { - context: 'outstream' - } - } - }, - { - bidId: 'bidId2', - params: { mid: 1000 }, - mediaTypes: { - video: { - constext: 'instream' - } - } - } - ] - }; - - bids = spec.interpretResponse(serverResponse, bidRequest); - assert.ok(bids[0].renderer); - assert.equal(bids[1].renderer, undefined); - }); + requestId: 'bidId', + cpm: 10, + creativeId: '12345', + ttl: 300, + netRevenue: true, + currency: 'CZK', + width: 100, + height: 100, + meta: { + advertiserDomains: [] + }, + ad: ' Date: Wed, 10 Aug 2022 12:44:24 +0200 Subject: [PATCH 12/22] renamed setOnAny to getFirstWithKey --- modules/carodaBidAdapter.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 5d0fcb57cea..4a9847a3bf4 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -36,12 +36,12 @@ export const spec = { const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) const ortbCommon = getORTBCommon(bidderRequest) const priceType = - setOnAny(validBidRequests, 'params.priceType') || + getFirstWithKey(validBidRequests, 'params.priceType') || 'net' - const test = setOnAny(validBidRequests, 'params.test') + const test = getFirstWithKey(validBidRequests, 'params.test') const currency = getConfig('currency.adServerCurrency') - const eids = setOnAny(validBidRequests, 'userIdAsEids') - const schain = setOnAny(validBidRequests, 'schain') + const eids = getFirstWithKey(validBidRequests, 'userIdAsEids') + const schain = getFirstWithKey(validBidRequests, 'schain') const request = { id: bidderRequest.auctionId, currency, @@ -118,7 +118,7 @@ export const spec = { registerBidder(spec) -function setOnAny (collection, key) { +function getFirstWithKey (collection, key) { for (let i = 0, result; i < collection.length; i++) { result = deepAccess(collection[i], key) if (result) { From 22d0c79e3d74afba851e994893a1143d0f3d01b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Wed, 10 Aug 2022 12:45:57 +0200 Subject: [PATCH 13/22] changed interpretResponse to arrow function --- modules/carodaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 4a9847a3bf4..8d11246be60 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -82,7 +82,7 @@ export const spec = { data: JSON.stringify(imp) })) }, - interpretResponse: function (serverResponse, { bids }) { + interpretResponse: (serverResponse) => { if (!serverResponse.body) { return } From 0efbf7c39376ef8d2236e1baea6a22c3d501c990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Wed, 10 Aug 2022 13:53:15 +0200 Subject: [PATCH 14/22] removed extra newline --- test/spec/modules/carodaBidAdapter_spec.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index 2bbeb0027c3..071526dfcef 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -88,7 +88,7 @@ describe('Caroda adapter', function () { const data = JSON.parse(request.data) assert.equal(data.placement_id, 'opzafe342f'); }); - + it('should send info about device', function () { config.setConfig({ device: { w: 100, h: 100 } @@ -251,23 +251,23 @@ describe('Caroda adapter', function () { let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - + assert.equal(request.privacy.coppa, undefined); - + config.setConfig({ coppa: false }); request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); - + assert.equal(request.privacy.coppa, undefined); - }); + }); it('should set coppa to 1 when coppa is provided with value true', function () { config.setConfig({ coppa: true }); let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); - + assert.equal(request.privacy.coppa, 1); }); }); @@ -288,7 +288,6 @@ describe('Caroda adapter', function () { assert.equal(data[1].ctok, 'ctok2') }); - describe('price floors', function () { it('should not add if floors module not configured', function () { const validBidRequests = [{ bidId: 'bidId', params: {ctok: 'ctok1'}, mediaTypes: {video: {}} }]; From 3b061d348da408280293d8e9465982d3bbd7f0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Mon, 15 Aug 2022 15:52:43 +0200 Subject: [PATCH 15/22] slight adjustment of request/response format --- modules/carodaBidAdapter.js | 17 +++--- test/spec/modules/carodaBidAdapter_spec.js | 68 +++++++++++----------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 8d11246be60..4a1b6b27c55 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -16,7 +16,6 @@ const { getConfig } = config const BIDDER_CODE = 'caroda' const GVLID = 954 -const carodaDomain = 'prebid.caroda.io' // some state info is required to synchronize with Caroda ad server const topUsableWindow = getTopUsableWindow() @@ -43,7 +42,7 @@ export const spec = { const eids = getFirstWithKey(validBidRequests, 'userIdAsEids') const schain = getFirstWithKey(validBidRequests, 'schain') const request = { - id: bidderRequest.auctionId, + auctionId: bidderRequest.auctionId, currency, hb_version: '$prebid.version$', ...ortbCommon, @@ -78,25 +77,26 @@ export const spec = { } return getImps(validBidRequests, request).map(imp => ({ method: 'POST', - url: 'https://' + carodaDomain + '/api/hb?entry_id=' + pageViewId, + url: 'https://prebid.caroda.io/api/hb?entry_id=' + pageViewId, data: JSON.stringify(imp) })) }, - interpretResponse: (serverResponse) => { + interpretResponse: (serverResponse, bidRequest) => { if (!serverResponse.body) { return } const { ok, error } = serverResponse.body if (error) { + logError(BIDDER_CODE, ': server caught', error.message) return } try { - return JSON.parse(ok) + return JSON.parse(ok.value) .map((bid) => { return { - requestId: bid.bidId, + requestId: bid.bid_id, cpm: bid.cpm, - creativeId: bid.creativeId, + creativeId: bid.creative_id, ttl: 300, netRevenue: true, currency: bid.currency, @@ -106,7 +106,7 @@ export const spec = { advertiserDomains: bid && bid.adomain ? bid.adomain : [] }, ad: bid.ad, - placementId: bid.placementId + placementId: bid.placement_id } }) .filter(Boolean) @@ -176,6 +176,7 @@ function getImps (validBidRequests, common) { const bidfloorcur = floorInfo.currency const { ctok, placementId } = bid.params const imp = { + bid_id: bid.bidId, ctok, bidfloor, bidfloorcur, diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index 071526dfcef..be4858fdc05 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -43,7 +43,7 @@ describe('Caroda adapter', function () { }); it('should send request with minimal structure', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { 'ctok': 'adf232eef344' } @@ -63,7 +63,7 @@ describe('Caroda adapter', function () { it('should add test to request, if test is set in parameters', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { 'ctok': 'adf232eef344', 'test': 1 @@ -77,7 +77,7 @@ describe('Caroda adapter', function () { it('should add placement_id to request when available', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { 'ctok': 'adf232eef344', 'placementId': 'opzafe342f' @@ -94,7 +94,7 @@ describe('Caroda adapter', function () { device: { w: 100, h: 100 } }); const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { 'ctok': 'adf232eef344' } }]; const data = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); @@ -105,7 +105,7 @@ describe('Caroda adapter', function () { it('should pass supply chain object', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: {}, schain: { validation: 'strict', @@ -130,7 +130,7 @@ describe('Caroda adapter', function () { }); const ortb2 = { app: { name: 'appname' } }; let validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { mid: '1000' }, ortb2 }]; @@ -157,7 +157,7 @@ describe('Caroda adapter', function () { } }; let validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { mid: '1000' }, ortb2 }]; @@ -176,7 +176,7 @@ describe('Caroda adapter', function () { it('should send correct priceType value', function () { let validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { priceType: 'gross' } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); @@ -195,7 +195,7 @@ describe('Caroda adapter', function () { it('should pass extended ids', function () { let validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: {}, userIdAsEids: createEidsArray({ tdid: 'TTD_ID_FROM_USER_ID_MODULE', @@ -212,7 +212,7 @@ describe('Caroda adapter', function () { describe('user privacy', function () { it('should send GDPR Consent data to adform if gdprApplies', function () { - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let validBidRequests = [{ bid_id: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); @@ -222,7 +222,7 @@ describe('Caroda adapter', function () { }); it('should send gdpr as number', function () { - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let validBidRequests = [{ bid_id: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); @@ -231,7 +231,7 @@ describe('Caroda adapter', function () { }); it('should send CCPA Consent data', function () { - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let validBidRequests = [{ bid_id: 'bidId', params: { test: 1 } }]; let bidderRequest = { uspConsent: '1YA-', refererInfo: { page: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); @@ -248,7 +248,7 @@ describe('Caroda adapter', function () { it('should not set coppa when coppa is not provided or is set to false', function () { config.setConfig({ }); - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let validBidRequests = [{ bid_id: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest)[0].data); @@ -265,7 +265,7 @@ describe('Caroda adapter', function () { config.setConfig({ coppa: true }); - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let validBidRequests = [{ bid_id: 'bidId', params: { test: 1 } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); assert.equal(request.privacy.coppa, 1); @@ -275,10 +275,10 @@ describe('Caroda adapter', function () { describe('bids', function () { it('should be able to handle multiple bids', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { ctok: 'ctok1' } }, { - bidId: 'bidId2', + bid_id: 'bidId2', params: { ctok: 'ctok2' } }]; const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }); @@ -290,7 +290,7 @@ describe('Caroda adapter', function () { describe('price floors', function () { it('should not add if floors module not configured', function () { - const validBidRequests = [{ bidId: 'bidId', params: {ctok: 'ctok1'}, mediaTypes: {video: {}} }]; + const validBidRequests = [{ bid_id: 'bidId', params: {ctok: 'ctok1'}, mediaTypes: {video: {}} }]; const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); assert.equal(imp.bidfloor, undefined); assert.equal(imp.bidfloorcur, undefined); @@ -340,7 +340,7 @@ describe('Caroda adapter', function () { describe('multiple media types', function () { it('should use all configured media types for bidding', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { ctok: 'ctok1' }, mediaTypes: { banner: { @@ -349,7 +349,7 @@ describe('Caroda adapter', function () { video: {} } }, { - bidId: 'bidId2', + bid_id: 'bidId2', params: { ctok: 'ctok1' }, mediaTypes: { video: {}, @@ -371,7 +371,7 @@ describe('Caroda adapter', function () { describe('banner', function () { it('should convert sizes to openrtb format', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { mid: 1000 }, mediaTypes: { banner: { @@ -389,7 +389,7 @@ describe('Caroda adapter', function () { describe('video', function () { it('should pass video mediatype config', function () { const validBidRequests = [{ - bidId: 'bidId', + bid_id: 'bidId', params: { mid: 1000 }, mediaTypes: { video: { @@ -419,27 +419,29 @@ describe('Caroda adapter', function () { it('should parse a typical ok response', function () { const serverResponse = { body: { - ok: JSON.stringify([{ - bidId: 'bidId', - cpm: 10, - creativeId: '12345', - currency: 'CZK', - w: 100, - h: 100, - ad: ' Date: Tue, 16 Aug 2022 10:17:57 +0200 Subject: [PATCH 16/22] added ad targeting and ad id to bid response --- modules/carodaBidAdapter.js | 14 +-- test/spec/modules/carodaBidAdapter_spec.js | 102 ++++++++++++++------- 2 files changed, 78 insertions(+), 38 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 4a1b6b27c55..df347e0df85 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -81,7 +81,7 @@ export const spec = { data: JSON.stringify(imp) })) }, - interpretResponse: (serverResponse, bidRequest) => { + interpretResponse: (serverResponse) => { if (!serverResponse.body) { return } @@ -92,8 +92,7 @@ export const spec = { } try { return JSON.parse(ok.value) - .map((bid) => { - return { + .map((bid) => ({ requestId: bid.bid_id, cpm: bid.cpm, creativeId: bid.creative_id, @@ -103,12 +102,13 @@ export const spec = { width: bid.w, height: bid.h, meta: { - advertiserDomains: bid && bid.adomain ? bid.adomain : [] + advertiserDomains: bid.adomain || [] }, ad: bid.ad, - placementId: bid.placement_id - } - }) + adId: bid.ad_id, + placementId: bid.placement_id, + adserverTargeting: bid.adserver_targeting + })) .filter(Boolean) } catch (e) { logError(BIDDER_CODE, ': caught', e) diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index be4858fdc05..c3f2b8b2eca 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -416,40 +416,36 @@ describe('Caroda adapter', function () { const bidRequest = {}; assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); }); + const basicBidResponse = () => ({ + bid_id: 'bidId', + cpm: 10, + creative_id: '12345', + currency: 'CZK', + w: 100, + h: 100, + ad: ' ({ + data: {}, + bids: [ + { + bid_id: 'bidId1', + params: { ctok: 'ctok1' } + }, + { + bid_id: 'bidId2', + params: { ctok: 'ctok2' } + } + ] + }); it('should parse a typical ok response', function () { const serverResponse = { - body: { - ok: { - value: JSON.stringify([{ - bid_id: 'bidId', - cpm: 10, - creative_id: '12345', - currency: 'CZK', - w: 100, - h: 100, - ad: ' Date: Tue, 16 Aug 2022 10:34:37 +0200 Subject: [PATCH 17/22] fixed undefined ad id error --- modules/carodaBidAdapter.js | 17 ++++++++++++----- test/spec/modules/carodaBidAdapter_spec.js | 2 -- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index df347e0df85..2c24ed65c0e 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -92,7 +92,8 @@ export const spec = { } try { return JSON.parse(ok.value) - .map((bid) => ({ + .map((bid) => { + const ret = { requestId: bid.bid_id, cpm: bid.cpm, creativeId: bid.creative_id, @@ -105,10 +106,16 @@ export const spec = { advertiserDomains: bid.adomain || [] }, ad: bid.ad, - adId: bid.ad_id, - placementId: bid.placement_id, - adserverTargeting: bid.adserver_targeting - })) + placementId: bid.placement_id + } + if (bid.ad_id) { + ret.adId = bid.ad_id + } + if (bid.adserver_targeting) { + ret.adserverTargeting = bid.adserver_targeting + } + return ret + }) .filter(Boolean) } catch (e) { logError(BIDDER_CODE, ': caught', e) diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index c3f2b8b2eca..db526e63d00 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -459,8 +459,6 @@ describe('Caroda adapter', function () { advertiserDomains: [] }, ad: ' Date: Tue, 16 Aug 2022 14:39:29 +0200 Subject: [PATCH 18/22] fixed linting --- modules/carodaBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 2c24ed65c0e..2569dbac442 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -42,7 +42,7 @@ export const spec = { const eids = getFirstWithKey(validBidRequests, 'userIdAsEids') const schain = getFirstWithKey(validBidRequests, 'schain') const request = { - auctionId: bidderRequest.auctionId, + auctionId: bidderRequest.auctionId, currency, hb_version: '$prebid.version$', ...ortbCommon, @@ -106,7 +106,7 @@ export const spec = { advertiserDomains: bid.adomain || [] }, ad: bid.ad, - placementId: bid.placement_id + placementId: bid.placement_id } if (bid.ad_id) { ret.adId = bid.ad_id From 6a6a57da5747ccb6f5e40831b11f843e3f86fe66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Thu, 1 Sep 2022 11:54:44 +0200 Subject: [PATCH 19/22] fix: removed setting ad id in interpret response --- modules/carodaBidAdapter.js | 3 --- test/spec/modules/carodaBidAdapter_spec.js | 14 -------------- 2 files changed, 17 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 2569dbac442..0a678c486e4 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -108,9 +108,6 @@ export const spec = { ad: bid.ad, placementId: bid.placement_id } - if (bid.ad_id) { - ret.adId = bid.ad_id - } if (bid.adserver_targeting) { ret.adserverTargeting = bid.adserver_targeting } diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index db526e63d00..b9cee14d5b7 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -476,20 +476,6 @@ describe('Caroda adapter', function () { bids = spec.interpretResponse(serverResponse, bidRequest()); assert.deepEqual(bids[0].adserverTargeting, { tag: 'value' }); }); - it('should add adId', function () { - const serverResponse = { - body: { - ok: { - value: JSON.stringify([{ - ...basicBidResponse(), - ad_id: '123' - }]) - } - } - }; - bids = spec.interpretResponse(serverResponse, bidRequest()); - assert.equal(bids[0].adId, '123'); - }); it('should add adomains', function () { const serverResponse = { body: { From 8a6535d439ee17ba38a24263d754d72534902f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Thu, 1 Sep 2022 12:09:28 +0200 Subject: [PATCH 20/22] fix: impoved pageview id handling when caroda is loaded later than prebid --- modules/carodaBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 0a678c486e4..66437eac10a 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -32,7 +32,8 @@ export const spec = { typeof placementId === 'undefined') }, buildRequests: (validBidRequests, bidderRequest) => { - const pageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) + topUsableWindow.carodaPageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) + const pageViewId = topUsableWindow.carodaPageViewId const ortbCommon = getORTBCommon(bidderRequest) const priceType = getFirstWithKey(validBidRequests, 'params.priceType') || From e849d66f31746e8326dfebf51b296eef265f2762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Thu, 1 Sep 2022 12:53:14 +0200 Subject: [PATCH 21/22] style: added semicolumns --- modules/carodaBidAdapter.js | 134 ++++++++++----------- test/spec/modules/carodaBidAdapter_spec.js | 16 +-- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 66437eac10a..6616d4f505b 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -1,95 +1,95 @@ // jshint esversion: 6, es3: false, node: true 'use strict' -import { registerBidder } from '../src/adapters/bidderFactory.js' -import { BANNER, VIDEO } from '../src/mediaTypes.js' +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { deepAccess, deepSetValue, logError, mergeDeep, parseSizesInput -} from '../src/utils.js' -import { config } from '../src/config.js' +} from '../src/utils.js'; +import { config } from '../src/config.js'; -const { getConfig } = config +const { getConfig } = config; -const BIDDER_CODE = 'caroda' -const GVLID = 954 +const BIDDER_CODE = 'caroda'; +const GVLID = 954; // some state info is required to synchronize with Caroda ad server -const topUsableWindow = getTopUsableWindow() +const topUsableWindow = getTopUsableWindow(); export const spec = { code: BIDDER_CODE, gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: bid => { - const params = bid.params || {} - const { ctok, placementId } = params + const params = bid.params || {}; + const { ctok, placementId } = params; return typeof ctok === 'string' && ( typeof placementId === 'string' || - typeof placementId === 'undefined') + typeof placementId === 'undefined'); }, buildRequests: (validBidRequests, bidderRequest) => { - topUsableWindow.carodaPageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9) - const pageViewId = topUsableWindow.carodaPageViewId - const ortbCommon = getORTBCommon(bidderRequest) + topUsableWindow.carodaPageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9); + const pageViewId = topUsableWindow.carodaPageViewId; + const ortbCommon = getORTBCommon(bidderRequest); const priceType = getFirstWithKey(validBidRequests, 'params.priceType') || - 'net' - const test = getFirstWithKey(validBidRequests, 'params.test') - const currency = getConfig('currency.adServerCurrency') - const eids = getFirstWithKey(validBidRequests, 'userIdAsEids') - const schain = getFirstWithKey(validBidRequests, 'schain') + 'net'; + const test = getFirstWithKey(validBidRequests, 'params.test'); + const currency = getConfig('currency.adServerCurrency'); + const eids = getFirstWithKey(validBidRequests, 'userIdAsEids'); + const schain = getFirstWithKey(validBidRequests, 'schain'); const request = { auctionId: bidderRequest.auctionId, currency, hb_version: '$prebid.version$', ...ortbCommon, price_type: priceType - } + }; if (test) { - request.test = 1 + request.test = 1; } if (schain) { - request.schain = schain + request.schain = schain; } if (config.getConfig('coppa')) { - deepSetValue(request, 'privacy.coppa', 1) + deepSetValue(request, 'privacy.coppa', 1); } if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { deepSetValue( request, 'privacy.gdpr_consent', bidderRequest.gdprConsent.consentString - ) + ); deepSetValue( request, 'privacy.gdpr', bidderRequest.gdprConsent.gdprApplies & 1 - ) + ); } if (bidderRequest.uspConsent) { - deepSetValue(request, 'privacy.us_privacy', bidderRequest.uspConsent) + deepSetValue(request, 'privacy.us_privacy', bidderRequest.uspConsent); } if (eids) { - deepSetValue(request, 'user.eids', eids) + deepSetValue(request, 'user.eids', eids); } return getImps(validBidRequests, request).map(imp => ({ method: 'POST', url: 'https://prebid.caroda.io/api/hb?entry_id=' + pageViewId, data: JSON.stringify(imp) - })) + })); }, interpretResponse: (serverResponse) => { if (!serverResponse.body) { - return + return; } const { ok, error } = serverResponse.body if (error) { - logError(BIDDER_CODE, ': server caught', error.message) - return + logError(BIDDER_CODE, ': server caught', error.message); + return; } try { return JSON.parse(ok.value) @@ -114,9 +114,9 @@ export const spec = { } return ret }) - .filter(Boolean) + .filter(Boolean); } catch (e) { - logError(BIDDER_CODE, ': caught', e) + logError(BIDDER_CODE, ': caught', e); } } } @@ -125,88 +125,88 @@ registerBidder(spec) function getFirstWithKey (collection, key) { for (let i = 0, result; i < collection.length; i++) { - result = deepAccess(collection[i], key) + result = deepAccess(collection[i], key); if (result) { - return result + return result; } } } function getTopUsableWindow () { - let res = window + let res = window; try { while (window.top !== res && res.parent.location.href.length) { - res = res.parent + res = res.parent; } } catch (e) {} - return res + return res; } function getORTBCommon (bidderRequest) { - let app, site - const commonFpd = bidderRequest.ortb2 || {} - let { user } = commonFpd + let app, site; + const commonFpd = bidderRequest.ortb2 || {}; + let { user } = commonFpd; if (typeof getConfig('app') === 'object') { app = getConfig('app') || {} if (commonFpd.app) { - mergeDeep(app, commonFpd.app) + mergeDeep(app, commonFpd.app); } } else { - site = getConfig('site') || {} + site = getConfig('site') || {}; if (commonFpd.site) { - mergeDeep(site, commonFpd.site) + mergeDeep(site, commonFpd.site); } if (!site.page) { - site.page = bidderRequest.refererInfo.page + site.page = bidderRequest.refererInfo.page; } } - const device = getConfig('device') || {} - device.w = device.w || window.innerWidth - device.h = device.h || window.innerHeight - device.ua = device.ua || navigator.userAgent + const device = getConfig('device') || {}; + device.w = device.w || window.innerWidth; + device.h = device.h || window.innerHeight; + device.ua = device.ua || navigator.userAgent; return { app, site, user, device - } + }; } function getImps (validBidRequests, common) { return validBidRequests.map((bid) => { const floorInfo = bid.getFloor ? bid.getFloor({ currency: common.currency || 'EUR' }) - : {} - const bidfloor = floorInfo.floor - const bidfloorcur = floorInfo.currency - const { ctok, placementId } = bid.params + : {}; + const bidfloor = floorInfo.floor; + const bidfloorcur = floorInfo.currency; + const { ctok, placementId } = bid.params; const imp = { bid_id: bid.bidId, ctok, bidfloor, bidfloorcur, ...common - } - const bannerParams = deepAccess(bid, 'mediaTypes.banner') + }; + const bannerParams = deepAccess(bid, 'mediaTypes.banner'); if (bannerParams && bannerParams.sizes) { - const sizes = parseSizesInput(bannerParams.sizes) + const sizes = parseSizesInput(bannerParams.sizes); const format = sizes.map(size => { - const [width, height] = size.split('x') - const w = parseInt(width, 10) - const h = parseInt(height, 10) - return { w, h } - }) + const [width, height] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return { w, h }; + }); imp.banner = { format - } + }; } if (placementId) { - imp.placement_id = placementId + imp.placement_id = placementId; } - const videoParams = deepAccess(bid, 'mediaTypes.video') + const videoParams = deepAccess(bid, 'mediaTypes.video'); if (videoParams) { - imp.video = videoParams + imp.video = videoParams; } - return imp + return imp; }) } diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index b9cee14d5b7..f575e31e85d 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -39,7 +39,7 @@ describe('Caroda adapter', function () { describe('buildRequests', function () { beforeEach(function () { config.resetConfig(); - delete window.carodaPageViewId + delete window.carodaPageViewId; }); it('should send request with minimal structure', function () { const validBidRequests = [{ @@ -48,7 +48,7 @@ describe('Caroda adapter', function () { 'ctok': 'adf232eef344' } }]; - window.top.carodaPageViewId = 12345 + window.top.carodaPageViewId = 12345; const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0]; assert.equal(request.method, 'POST'); assert.equal(request.url, 'https://prebid.caroda.io/api/hb?entry_id=12345'); @@ -69,7 +69,7 @@ describe('Caroda adapter', function () { 'test': 1 } }]; - window.top.carodaPageViewId = 12345 + window.top.carodaPageViewId = 12345; const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0]; const data = JSON.parse(request.data) assert.equal(data.test, 1); @@ -83,9 +83,9 @@ describe('Caroda adapter', function () { 'placementId': 'opzafe342f' } }]; - window.top.carodaPageViewId = 12345 + window.top.carodaPageViewId = 12345; const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0]; - const data = JSON.parse(request.data) + const data = JSON.parse(request.data); assert.equal(data.placement_id, 'opzafe342f'); }); @@ -283,9 +283,9 @@ describe('Caroda adapter', function () { }]; const request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }); assert.equal(request.length, 2); - const data = request.map(r => JSON.parse(r.data)) - assert.equal(data[0].ctok, 'ctok1') - assert.equal(data[1].ctok, 'ctok2') + const data = request.map(r => JSON.parse(r.data)); + assert.equal(data[0].ctok, 'ctok1'); + assert.equal(data[1].ctok, 'ctok2'); }); describe('price floors', function () { From 3afbae4fe2d1c600f2382d21cb862d0592ee5a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan-Carol=20Pl=C3=A2ngu?= Date: Wed, 14 Sep 2022 12:45:36 +0200 Subject: [PATCH 22/22] added price type bid validation --- modules/carodaBidAdapter.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 6616d4f505b..7267452dd4c 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -26,10 +26,15 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: bid => { const params = bid.params || {}; - const { ctok, placementId } = params; + const { ctok, placementId, priceType } = params; return typeof ctok === 'string' && ( typeof placementId === 'string' || - typeof placementId === 'undefined'); + typeof placementId === 'undefined' + ) && ( + typeof priceType === 'undefined' || + priceType === 'gross' || + priceType === 'net' + ); }, buildRequests: (validBidRequests, bidderRequest) => { topUsableWindow.carodaPageViewId = topUsableWindow.carodaPageViewId || Math.floor(Math.random() * 1e9);