From 1e1381aa7c150991078034a9497327fe558baa0d Mon Sep 17 00:00:00 2001 From: Antoine Zazzera Date: Fri, 24 Jan 2025 16:26:09 +0100 Subject: [PATCH] Ogury Bid Adapter : integrate ORTB Converter Library (#12661) * use ortb converter lib on Ogury adapter * override site.page with current location when publisher overrides ortb2 object with wrong values --- modules/oguryBidAdapter.js | 205 ++---- test/spec/modules/oguryBidAdapter_spec.js | 768 ++++++---------------- 2 files changed, 276 insertions(+), 697 deletions(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index ecf512f11d3..d4e5fea1cf0 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -1,10 +1,11 @@ 'use strict'; -import {BANNER} from '../src/mediaTypes.js'; -import {getWindowSelf, getWindowTop, isFn, logWarn, deepAccess, isPlainObject} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ajax} from '../src/ajax.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getWindowSelf, getWindowTop, isFn, deepAccess, isPlainObject, deepSetValue, mergeDeep } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ajax } from '../src/ajax.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; const BIDDER_CODE = 'ogury'; const GVLID = 31; @@ -12,38 +13,68 @@ const DEFAULT_TIMEOUT = 1000; const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_MONITORING_HOST = 'https://ms-ads-monitoring-events.presage.io'; const MS_COOKIE_SYNC_DOMAIN = 'https://ms-cookie-sync.presage.io'; -const ADAPTER_VERSION = '1.7.0'; +const ADAPTER_VERSION = '2.0.0'; + +export const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 60, + mediaType: 'banner' + }, + + request(buildRequest, imps, bidderRequest, context) { + const req = buildRequest(imps, bidderRequest, context); + req.tmax = DEFAULT_TIMEOUT; + deepSetValue(req, 'device.pxratio', window.devicePixelRatio); + deepSetValue(req, 'site.page', getWindowContext().location.href); + + req.ext = mergeDeep({}, req.ext, { + adapterversion: ADAPTER_VERSION, + prebidversion: '$prebid.version$' + }); -function getClientWidth() { - const documentElementClientWidth = window.top.document.documentElement.clientWidth - ? window.top.document.documentElement.clientWidth - : 0 - const innerWidth = window.top.innerWidth ? window.top.innerWidth : 0 - const outerWidth = window.top.outerWidth ? window.top.outerWidth : 0 - const screenWidth = window.top.screen.width ? window.top.screen.width : 0 + const bidWithAssetKey = bidderRequest.bids.find(bid => Boolean(deepAccess(bid, 'params.assetKey', false))); + if (bidWithAssetKey) deepSetValue(req, 'site.id', bidWithAssetKey.params.assetKey); - return documentElementClientWidth || innerWidth || outerWidth || screenWidth -} + const bidWithUserIds = bidderRequest.bids.find(bid => Boolean(bid.userId)); + if (bidWithUserIds) deepSetValue(req, 'user.ext.uids', bidWithUserIds.userId); -function getClientHeight() { - const documentElementClientHeight = window.top.document.documentElement.clientHeight - ? window.top.document.documentElement.clientHeight - : 0 - const innerHeight = window.top.innerHeight ? window.top.innerHeight : 0 - const outerHeight = window.top.outerHeight ? window.top.outerHeight : 0 - const screenHeight = window.top.screen.height ? window.top.screen.height : 0 + return req; + }, - return documentElementClientHeight || innerHeight || outerHeight || screenHeight -} + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + const timeSpentOnPage = document.timeline && document.timeline.currentTime ? document.timeline.currentTime : 0 + const gpid = bidRequest.adUnitCode; + imp.tagid = bidRequest.adUnitCode; + imp.ext = mergeDeep({}, bidRequest.params, { timeSpentOnPage, gpid }, imp.ext); + + const bidfloor = getFloor(bidRequest); + + if (!bidfloor) { + delete imp.bidfloor; + } else { + imp.bidfloor = bidfloor; + } + + return imp; + }, + + bidResponse(buildBidResponse, bid, context) { + const bidResponse = buildBidResponse(bid, context); + bidResponse.currency = 'USD'; + return bidResponse; + } +}); function isBidRequestValid(bid) { const adUnitSizes = getAdUnitSizes(bid); - const isValidSizes = Boolean(adUnitSizes) && adUnitSizes.length > 0; - const isValidAdUnitId = !!bid.params.adUnitId; - const isValidAssetKey = !!bid.params.assetKey; + const isValidSize = (Boolean(adUnitSizes) && adUnitSizes.length > 0); + const hasAssetKeyAndAdUnitId = !!deepAccess(bid, 'params.adUnitId') && !!deepAccess(bid, 'params.assetKey'); + const hasPublisherIdAndAdUnitCode = !!deepAccess(bid, 'ortb2.site.publisher.id') && !!bid.adUnitCode; - return (isValidSizes && isValidAdUnitId && isValidAssetKey); + return isValidSize && (hasAssetKeyAndAdUnitId || hasPublisherIdAndAdUnitCode); } function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { @@ -80,126 +111,19 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gpp return []; } -function buildRequests(validBidRequests, bidderRequest) { - const openRtbBidRequestBanner = { - id: bidderRequest.bidderRequestId, - tmax: Math.min(DEFAULT_TIMEOUT, bidderRequest.timeout), - at: 1, - regs: { - ext: { - gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0, - }, - }, - site: { - domain: location.hostname, - page: location.href - }, - user: { - ext: { - consent: '' - } - }, - imp: [], - ext: { - adapterversion: ADAPTER_VERSION, - prebidversion: '$prebid.version$' - }, - device: { - w: getClientWidth(), - h: getClientHeight(), - pxratio: window.devicePixelRatio - } - }; - - if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) { - openRtbBidRequestBanner.user.ext.consent = bidderRequest.gdprConsent.consentString - } - if (bidderRequest.gppConsent && bidderRequest.gppConsent.gppString) { - openRtbBidRequestBanner.regs.ext.gpp = bidderRequest.gppConsent.gppString - } - if (bidderRequest.gppConsent && bidderRequest.gppConsent.applicableSections) { - openRtbBidRequestBanner.regs.ext.gpp_sid = bidderRequest.gppConsent.applicableSections - } - - validBidRequests.forEach((bidRequest) => { - const sizes = getAdUnitSizes(bidRequest) - .map(size => ({ w: size[0], h: size[1] })); - - if (bidRequest.mediaTypes && - bidRequest.mediaTypes.hasOwnProperty('banner')) { - openRtbBidRequestBanner.site.id = bidRequest.params.assetKey; - const floor = getFloor(bidRequest); - - if (bidRequest.userId) { - openRtbBidRequestBanner.user.ext.uids = bidRequest.userId - } - if (bidRequest.userIdAsEids) { - openRtbBidRequestBanner.user.ext.eids = bidRequest.userIdAsEids - } - - const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); - - openRtbBidRequestBanner.imp.push({ - id: bidRequest.bidId, - tagid: bidRequest.params.adUnitId, - ...(floor && {bidfloor: floor}), - banner: { - format: sizes - }, - ext: { - ...bidRequest.params, - ...(gpid && {gpid}), - timeSpentOnPage: document.timeline && document.timeline.currentTime ? document.timeline.currentTime : 0 - } - }); - } - }); +function buildRequests(bidRequests, bidderRequest) { + const data = converter.toORTB({bidRequests, bidderRequest}); return { method: 'POST', url: BID_HOST, - data: openRtbBidRequestBanner, + data, options: {contentType: 'application/json'}, }; } -function interpretResponse(openRtbBidResponse) { - if (!openRtbBidResponse || - !openRtbBidResponse.body || - typeof openRtbBidResponse.body != 'object' || - Object.keys(openRtbBidResponse.body).length === 0) { - logWarn('no response or body is malformed'); - return []; - } - - const bidResponses = []; - - openRtbBidResponse.body.seatbid.forEach((seatbid) => { - seatbid.bid.forEach((bid) => { - let bidResponse = { - requestId: bid.impid, - cpm: bid.price, - currency: 'USD', - width: bid.w, - height: bid.h, - creativeId: bid.id, - netRevenue: true, - ttl: 60, - ext: bid.ext, - meta: { - advertiserDomains: bid.adomain - }, - nurl: bid.nurl, - adapterVersion: ADAPTER_VERSION, - prebidVersion: '$prebid.version$' - }; - - bidResponse.ad = bid.adm; - - bidResponses.push(bidResponse); - }); - }); - return bidResponses; +function interpretResponse(response, request) { + return converter.fromORTB({response: response.body, request: request.data}).bids; } function getFloor(bid) { @@ -211,6 +135,7 @@ function getFloor(bid) { mediaType: 'banner', size: '*' }); + return (isPlainObject(floorResult) && floorResult.currency === 'USD') ? floorResult.floor : 0; } diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index 99c16d0b1af..369540de668 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -1,14 +1,16 @@ import { expect } from 'chai'; +import sinon from 'sinon'; import { spec } from 'modules/oguryBidAdapter'; import * as utils from 'src/utils.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; const BID_URL = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_URL = 'https://ms-ads-monitoring-events.presage.io/bid_timeout' -describe('OguryBidAdapter', function () { - let bidRequests; - let bidderRequest; +describe('OguryBidAdapter', () => { + let bidRequests, bidderRequestBase, ortb2; + + const currentLocation = 'https://mwtt.ogury.tech/advanced'; bidRequests = [ { @@ -48,20 +50,7 @@ describe('OguryBidAdapter', function () { return floorResult; }, transactionId: 'transactionId', - userId: { - pubcid: '2abb10e5-c4f6-4f70-9f45-2200e4487714' - }, - userIdAsEids: [ - { - source: 'pubcid.org', - uids: [ - { - id: '2abb10e5-c4f6-4f70-9f45-2200e4487714', - atype: 1 - } - ] - } - ] + userId: { pubcid: 'f5debac9-9a8e-4c08-9820-51e96b69f858' } }, { adUnitCode: 'adUnitCode2', @@ -81,57 +70,119 @@ describe('OguryBidAdapter', function () { }, ]; - bidderRequest = { + ortb2 = { + regs: { + gpp_sid: [7], + gpp: 'DBABLA~BAAAAAAAAQA.QA', + ext: { gdpr: 1 } + }, + site: { + domain: 'mwtt.ogury.tech', + publisher: { domain: 'ogury.tech', id: 'ca06d4199b92bf6808e5ce15b28c6d30' }, + page: currentLocation, + ref: 'https://google.com' + }, + user: { + ext: { + consent: 'CQJI3tqQJI3tqFzABBENBJFsAP_gAEPgAAqIg1NX_H__bW9r8Xr3aft0eY1P99j77sQxBhfJE-4FyLvW_JwXx2EwNA26tqIKmRIEu3ZBIQFlHJHURVigaogVryHsYkGcgTNKJ6BkgFMRI2dYCF5vmYtj-QKY5_p_d3fx2D-t_dv83dzzz8VHn3e5fmckcKCdQ58tDfn9bRKb-5IO9-78v4v09l_rk2_eTVn_pcvr7B-uft87_XU-9_fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQagCzDQqIA-yJCQi0DCKBACIKwgIoEAAAAJA0QEAJAwKdgYBLrCRACBFAAMEAIAAUZAAgAAEgAQiACQAoEAAEAgEAAAAAAgEADAwADgAtBAIAAQHQMUwoAFAsIEiMiIUwIQoEggJbKBBICgQVwgCLDAigERMFAAgCQAVgAAAsVgMASAlYkECWUG0AABAAgFFKFQik6MAQwJmy1U4om0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACAA.YAAAAAAAAAAA', + eids: [ + { + source: 'pubcid.org', + uids: [{ 'id': 'f5debac9-9a8e-4c08-9820-51e96b69f858', 'atype': 1 }] + } + ] + } + }, + device: { + w: 412, + h: 915, + dnt: 0, + ua: 'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36', + language: 'en', + ext: { vpw: 412, vph: 915 }, + sua: { + source: 1, + platform: { brand: 'Android' }, + browsers: [{ brand: 'Google Chrome', version: ['131'] }], + mobile: 1 + } + } + }; + + bidderRequestBase = { + bids: bidRequests, bidderRequestId: 'mock-uuid', auctionId: bidRequests[0].auctionId, gdprConsent: {consentString: 'myConsentString', vendorData: {}, gdprApplies: true}, gppConsent: {gppString: 'myGppString', gppData: {}, applicableSections: [7], parsedSections: {}}, - timeout: 1000 + timeout: 1000, + ortb2 }; - describe('isBidRequestValid', function () { + describe('isBidRequestValid', () => { it('should validate correct bid', () => { let validBid = utils.deepClone(bidRequests[0]); let isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); + expect(isValid).to.true; }); - it('should not validate incorrect bid', () => { + it('should not validate when sizes is not defined', () => { let invalidBid = utils.deepClone(bidRequests[0]); delete invalidBid.sizes; delete invalidBid.mediaTypes; let isValid = spec.isBidRequestValid(invalidBid); - expect(isValid).to.equal(false); + expect(isValid).to.be.false; }); - it('should not validate bid if adunit is not present', () => { + it('should not validate bid when adunit is not defined', () => { let invalidBid = utils.deepClone(bidRequests[0]); delete invalidBid.params.adUnitId; let isValid = spec.isBidRequestValid(invalidBid); - expect(isValid).to.equal(false); + expect(isValid).to.to.be.false; }); - it('should not validate bid if assetKet is not present', () => { + it('should not validate bid when assetKey is not defined', () => { let invalidBid = utils.deepClone(bidRequests[0]); delete invalidBid.params.assetKey; let isValid = spec.isBidRequestValid(invalidBid); - expect(isValid).to.equal(false); + expect(isValid).to.be.false; }); - it('should validate bid if getFloor is not present', () => { - let invalidBid = utils.deepClone(bidRequests[1]); - delete invalidBid.getFloor; + it('should validate the request when only publisherId and adUnitCode is defined', () => { + const validBid = utils.deepClone(bidRequests[0]) + delete validBid.params.adUnitId + delete validBid.params.assetKey - let isValid = spec.isBidRequestValid(invalidBid); - expect(isValid).to.equal(true); + validBid.ortb2 = { site: { publisher: { id: 'publisherId' } } } + + expect(spec.isBidRequestValid(validBid)).to.be.true + }); + + it('should not validate the request when only publisherId is defined', () => { + const invalidBid = utils.deepClone(bidRequests[0]) + delete invalidBid.params.adUnitId + delete invalidBid.params.assetKey + delete invalidBid.adUnitCode + + invalidBid.ortb2 = { site: { publisher: { id: 'publisherId' } } } + + expect(spec.isBidRequestValid(invalidBid)).to.be.false + }); + + it('should not validate the request when only adUnitCode is defined', () => { + const invalidBid = utils.deepClone(bidRequests[0]) + delete invalidBid.params.adUnitId + delete invalidBid.params.assetKey + + expect(spec.isBidRequestValid(invalidBid)).to.be.false }); }); - describe('getUserSyncs', function() { + describe('getUserSyncs', () => { let syncOptions, gdprConsent, gppConsent; beforeEach(() => { @@ -612,17 +663,10 @@ describe('OguryBidAdapter', function () { }); }); - describe('buildRequests', function () { - const stubbedWidth = 200 - const stubbedHeight = 600 + describe('buildRequests', () => { + let windowTopStub; const stubbedCurrentTime = 1234567890 const stubbedDevicePixelRatio = 1 - const stubbedWidthMethod = sinon.stub(window.top.document.documentElement, 'clientWidth').get(function() { - return stubbedWidth; - }); - const stubbedHeightMethod = sinon.stub(window.top.document.documentElement, 'clientHeight').get(function() { - return stubbedHeight; - }); const stubbedCurrentTimeMethod = sinon.stub(document.timeline, 'currentTime').get(function() { return stubbedCurrentTime; }); @@ -632,496 +676,144 @@ describe('OguryBidAdapter', function () { }); const defaultTimeout = 1000; - const expectedRequestObject = { - id: 'mock-uuid', - at: 1, - tmax: defaultTimeout, - imp: [{ - id: bidRequests[0].bidId, - tagid: bidRequests[0].params.adUnitId, - bidfloor: 4, - banner: { - format: [{ - w: 300, - h: 250 - }] - }, - ext: { - ...bidRequests[0].params, - gpid: bidRequests[0].ortb2Imp.ext.gpid, - timeSpentOnPage: stubbedCurrentTime - } - }, { - id: bidRequests[1].bidId, - tagid: bidRequests[1].params.adUnitId, - banner: { - format: [{ - w: 600, - h: 500 - }] - }, - ext: { - ...bidRequests[1].params, - timeSpentOnPage: stubbedCurrentTime - } - }], - regs: { - ext: { - gdpr: 1, - gpp: 'myGppString', - gpp_sid: [7] - }, - }, - site: { - id: bidRequests[0].params.assetKey, - domain: window.location.hostname, - page: window.location.href - }, - user: { - ext: { - consent: bidderRequest.gdprConsent.consentString, - uids: { - pubcid: '2abb10e5-c4f6-4f70-9f45-2200e4487714' - }, - eids: [ - { - source: 'pubcid.org', - uids: [ - { - id: '2abb10e5-c4f6-4f70-9f45-2200e4487714', - atype: 1 - } - ] - } - ], - }, - }, - ext: { - prebidversion: '$prebid.version$', - adapterversion: '1.7.0' - }, - device: { - w: stubbedWidth, - h: stubbedHeight, - pxratio: stubbedDevicePixelRatio, - } - }; - - after(function() { - stubbedWidthMethod.restore(); - stubbedHeightMethod.restore(); - stubbedCurrentTimeMethod.restore(); - stubbedDevicePixelMethod.restore(); - }); - - it('sends bid request to ENDPOINT via POST', function () { - const validBidRequests = utils.deepClone(bidRequests) - - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.url).to.equal(BID_URL); - expect(request.method).to.equal('POST'); - }); - - it('timeSpentOnpage should be 0 if timeline is undefined', function () { - const stubbedTimelineMethod = sinon.stub(document, 'timeline').get(function() { - return undefined; - }); - const validBidRequests = utils.deepClone(bidRequests) - - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data.imp[0].ext.timeSpentOnPage).to.equal(0); - stubbedTimelineMethod.restore(); - }); - - it('send device pixel ratio in bid request', function() { - const validBidRequests = utils.deepClone(bidRequests) - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestObject); - expect(request.data.device.pxratio).to.be.a('number'); - }) - - it('bid request object should be conform', function () { - const validBidRequests = utils.deepClone(bidRequests) - - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestObject); - expect(request.data.regs.ext.gdpr).to.be.a('number'); - }); - - describe('getClientWidth', () => { - function testGetClientWidth(testGetClientSizeParams) { - const stubbedClientWidth = sinon.stub(window.top.document.documentElement, 'clientWidth').get(function() { - return testGetClientSizeParams.docClientSize - }) - - const stubbedInnerWidth = sinon.stub(window.top, 'innerWidth').get(function() { - return testGetClientSizeParams.innerSize - }) - - const stubbedOuterWidth = sinon.stub(window.top, 'outerWidth').get(function() { - return testGetClientSizeParams.outerSize - }) - - const stubbedWidth = sinon.stub(window.top.screen, 'width').get(function() { - return testGetClientSizeParams.screenSize - }) - - const validBidRequests = utils.deepClone(bidRequests) - - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data.device.w).to.equal(testGetClientSizeParams.expectedSize); - - stubbedClientWidth.restore(); - stubbedInnerWidth.restore(); - stubbedOuterWidth.restore(); - stubbedWidth.restore(); - } - - it('should get documentElementClientWidth by default', () => { - testGetClientWidth({ - docClientSize: 22, - innerSize: 50, - outerSize: 45, - screenSize: 10, - expectedSize: 22, - }) - }) - - it('should get innerWidth as first fallback', () => { - testGetClientWidth({ - docClientSize: undefined, - innerSize: 700, - outerSize: 650, - screenSize: 10, - expectedSize: 700, - }) - }) - - it('should get outerWidth as second fallback', () => { - testGetClientWidth({ - docClientSize: undefined, - innerSize: undefined, - outerSize: 650, - screenSize: 10, - expectedSize: 650, - }) - }) - - it('should get screenWidth as last fallback', () => { - testGetClientWidth({ - docClientSize: undefined, - innerSize: undefined, - outerSize: undefined, - screenSize: 10, - expectedSize: 10, - }); + function assertImpObject(ortbBidRequest, bidRequest) { + expect(ortbBidRequest.secure).to.equal(1); + expect(ortbBidRequest.id).to.equal(bidRequest.bidId); + expect(ortbBidRequest.tagid).to.equal(bidRequest.adUnitCode); + expect(ortbBidRequest.banner).to.deep.equal({ + topframe: 0, + format: [{ + w: bidRequest.mediaTypes.banner.sizes[0][0], + h: bidRequest.mediaTypes.banner.sizes[0][1], + }] }); - it('should return 0 if all window width values are undefined', () => { - testGetClientWidth({ - docClientSize: undefined, - innerSize: undefined, - outerSize: undefined, - screenSize: undefined, - expectedSize: 0, - }); + expect(ortbBidRequest.ext).to.deep.equal({ + ...bidRequest.params, + gpid: bidRequest.ortb2Imp?.ext.gpid || bidRequest.adUnitCode, + timeSpentOnPage: stubbedCurrentTime }); - }); - - describe('getClientHeight', () => { - function testGetClientHeight(testGetClientSizeParams) { - const stubbedClientHeight = sinon.stub(window.top.document.documentElement, 'clientHeight').get(function() { - return testGetClientSizeParams.docClientSize - }) - - const stubbedInnerHeight = sinon.stub(window.top, 'innerHeight').get(function() { - return testGetClientSizeParams.innerSize - }) - - const stubbedOuterHeight = sinon.stub(window.top, 'outerHeight').get(function() { - return testGetClientSizeParams.outerSize - }) - - const stubbedHeight = sinon.stub(window.top.screen, 'height').get(function() { - return testGetClientSizeParams.screenSize - }) + } - const validBidRequests = utils.deepClone(bidRequests) + function assertRequestObject(dataRequest) { + expect(dataRequest.id).to.be.a('string'); + expect(dataRequest.tmax).to.equal(defaultTimeout); - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data.device.h).to.equal(testGetClientSizeParams.expectedSize); + assertImpObject(dataRequest.imp[0], bidRequests[0]); + assertImpObject(dataRequest.imp[1], bidRequests[1]); - stubbedClientHeight.restore(); - stubbedInnerHeight.restore(); - stubbedOuterHeight.restore(); - stubbedHeight.restore(); - } - - it('should get documentElementClientHeight by default', () => { - testGetClientHeight({ - docClientSize: 420, - innerSize: 500, - outerSize: 480, - screenSize: 230, - expectedSize: 420, - }); + expect(dataRequest.imp[0].bidfloor).to.equal(4); + expect(dataRequest.regs).to.deep.equal(ortb2.regs); + expect(dataRequest.site).to.deep.equal({ + ...ortb2.site, + page: currentLocation, + id: bidRequests[0].params.assetKey }); - it('should get innerHeight as first fallback', () => { - testGetClientHeight({ - docClientSize: undefined, - innerSize: 500, - outerSize: 480, - screenSize: 230, - expectedSize: 500, - }); + expect(dataRequest.user).to.deep.equal({ + ext: { + ...ortb2.user.ext, + uids: bidRequests[0].userId + } }); - it('should get outerHeight as second fallback', () => { - testGetClientHeight({ - docClientSize: undefined, - innerSize: undefined, - outerSize: 480, - screenSize: 230, - expectedSize: 480, - }); + expect(dataRequest.ext).to.deep.equal({ + prebidversion: '$prebid.version$', + adapterversion: '2.0.0' }); - it('should get screenHeight as last fallback', () => { - testGetClientHeight({ - docClientSize: undefined, - innerSize: undefined, - outerSize: undefined, - screenSize: 230, - expectedSize: 230, - }); + expect(dataRequest.device).to.deep.equal({ + ...ortb2.device, + pxratio: stubbedDevicePixelRatio, }); - it('should return 0 if all window height values are undefined', () => { - testGetClientHeight({ - docClientSize: undefined, - innerSize: undefined, - outerSize: undefined, - screenSize: undefined, - expectedSize: 0, - }); - }); - }); + expect(dataRequest.regs.ext.gdpr).to.be.a('number'); + expect(dataRequest.device.pxratio).to.be.a('number'); + } - it('should not add gdpr infos if not present', () => { - const bidderRequestWithoutGdpr = { - ...bidderRequest, - gdprConsent: {}, - } - const expectedRequestObjectWithoutGdpr = { - ...expectedRequestObject, - regs: { - ext: { - gdpr: 0, - gpp: 'myGppString', - gpp_sid: [7] - }, - }, - user: { - ext: { - consent: '', - uids: expectedRequestObject.user.ext.uids, - eids: expectedRequestObject.user.ext.eids - }, - } - }; - - const validBidRequests = bidRequests - - const request = spec.buildRequests(validBidRequests, bidderRequestWithoutGdpr); - expect(request.data).to.deep.equal(expectedRequestObjectWithoutGdpr); - expect(request.data.regs.ext.gdpr).to.be.a('number'); + beforeEach(() => { + windowTopStub = sinon.stub(utils, 'getWindowTop'); + windowTopStub.returns({ location: { href: currentLocation } }); }); - it('should not add gpp infos if not present', () => { - const bidderRequestWithoutGpp = { - ...bidderRequest, - gppConsent: {}, - } - const expectedRequestObjectWithoutGpp = { - ...expectedRequestObject, - regs: { - ext: { - gdpr: 1 - }, - }, - user: { - ext: { - consent: 'myConsentString', - uids: expectedRequestObject.user.ext.uids, - eids: expectedRequestObject.user.ext.eids - }, - } - }; - - const validBidRequests = bidRequests - - const request = spec.buildRequests(validBidRequests, bidderRequestWithoutGpp); - expect(request.data).to.deep.equal(expectedRequestObjectWithoutGpp); + afterEach(() => { + windowTopStub.restore(); }); - it('should not add gdpr infos if gdprConsent is undefined', () => { - const bidderRequestWithoutGdpr = { - ...bidderRequest, - gdprConsent: undefined, - } - const expectedRequestObjectWithoutGdpr = { - ...expectedRequestObject, - regs: { - ext: { - gdpr: 0, - gpp: 'myGppString', - gpp_sid: [7] - }, - }, - user: { - ext: { - consent: '', - uids: expectedRequestObject.user.ext.uids, - eids: expectedRequestObject.user.ext.eids - }, - } - }; - - const validBidRequests = bidRequests - - const request = spec.buildRequests(validBidRequests, bidderRequestWithoutGdpr); - expect(request.data).to.deep.equal(expectedRequestObjectWithoutGdpr); - expect(request.data.regs.ext.gdpr).to.be.a('number'); + after(() => { + stubbedCurrentTimeMethod.restore(); + stubbedDevicePixelMethod.restore(); }); - it('should not add gpp infos if gppConsent is undefined', () => { - const bidderRequestWithoutGdpr = { - ...bidderRequest, - gppConsent: undefined, - } - const expectedRequestObjectWithoutGdpr = { - ...expectedRequestObject, - regs: { - ext: { - gdpr: 1, - }, - }, - }; - - const validBidRequests = bidRequests - - const request = spec.buildRequests(validBidRequests, bidderRequestWithoutGdpr); - expect(request.data).to.deep.equal(expectedRequestObjectWithoutGdpr); + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests, bidderRequestBase); + expect(request.url).to.equal(BID_URL); + expect(request.method).to.equal('POST'); }); - it('should not add tcString and turn off gdpr-applies if consentString and gdprApplies are undefined', () => { - const bidderRequestWithoutGdpr = { - ...bidderRequest, - gdprConsent: { consentString: undefined, gdprApplies: undefined }, - } - const expectedRequestObjectWithoutGdpr = { - ...expectedRequestObject, - regs: { - ext: { - gdpr: 0, - gpp: 'myGppString', - gpp_sid: [7] - }, - }, - user: { - ext: { - consent: '', - uids: expectedRequestObject.user.ext.uids, - eids: expectedRequestObject.user.ext.eids - }, - } - }; - - const validBidRequests = bidRequests + it('timeSpentOnpage should be 0 if timeline is undefined', function () { + const stubbedTimelineMethod = sinon.stub(document, 'timeline').get(function() { + return undefined; + }); - const request = spec.buildRequests(validBidRequests, bidderRequestWithoutGdpr); - expect(request.data).to.deep.equal(expectedRequestObjectWithoutGdpr); - expect(request.data.regs.ext.gdpr).to.be.a('number'); + const request = spec.buildRequests(bidRequests, bidderRequestBase); + expect(request.data.imp[0].ext.timeSpentOnPage).to.equal(0); + stubbedTimelineMethod.restore(); }); - it('should should not add uids infos if userId is undefined', () => { - const expectedRequestWithUndefinedUserId = { - ...expectedRequestObject, - user: { - ext: { - consent: expectedRequestObject.user.ext.consent, - eids: expectedRequestObject.user.ext.eids - } - } - }; + it('bid request object should be conform', function () { + const request = spec.buildRequests(bidRequests, bidderRequestBase); + assertRequestObject(request.data); + }); - const validBidRequests = utils.deepClone(bidRequests); - validBidRequests[0] = { - ...validBidRequests[0], - userId: undefined - }; + it('should not set site.id when assetKey is not present', () => { + const bidderRequest = utils.deepClone(bidderRequestBase); + const validBidRequests = bidderRequest.bids; + delete validBidRequests[0].params.assetKey; + delete validBidRequests[1].params.assetKey; const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUndefinedUserId); + expect(request.data.site.id).to.be.an('undefined'); }); - it('should should not add uids infos if userIdAsEids is undefined', () => { - const expectedRequestWithUndefinedUserIdAsEids = { - ...expectedRequestObject, - user: { - ext: { - consent: expectedRequestObject.user.ext.consent, - uids: expectedRequestObject.user.ext.uids - } - } - }; - - const validBidRequests = utils.deepClone(bidRequests); - validBidRequests[0] = { - ...validBidRequests[0], - userIdAsEids: undefined - }; + it('should not set user.ext.uids when userId is not present', () => { + const bidderRequest = utils.deepClone(bidderRequestBase); + const validBidRequests = bidderRequest.bids; + delete validBidRequests[0].userId; const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUndefinedUserIdAsEids); + expect(request.data.user.ext.uids).to.be.an('undefined'); }); it('should handle bidFloor undefined', () => { - const expectedRequestWithUndefinedFloor = { - ...expectedRequestObject - }; - - const validBidRequests = utils.deepClone(bidRequests); - validBidRequests[1] = { - ...validBidRequests[1], + const bidderRequest = utils.deepClone(bidderRequestBase); + const validBidRequests = bidderRequest.bids; + validBidRequests[0] = { + ...validBidRequests[0], getFloor: undefined }; const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUndefinedFloor); + expect(request.data.imp[0].bidfloor).to.be.an('undefined'); }); it('should handle bidFloor when is not function', () => { - const expectedRequestWithNotAFunctionFloor = { - ...expectedRequestObject - }; - - let validBidRequests = utils.deepClone(bidRequests); - validBidRequests[1] = { - ...validBidRequests[1], + const bidderRequest = utils.deepClone(bidderRequestBase); + const validBidRequests = bidderRequest.bids; + validBidRequests[0] = { + ...validBidRequests[0], getFloor: 'getFloor' }; const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithNotAFunctionFloor); + expect(request.data.imp[0].bidfloor).to.be.an('undefined'); }); it('should handle bidFloor when currency is not USD', () => { - const expectedRequestWithUnsupportedFloorCurrency = utils.deepClone(expectedRequestObject) - delete expectedRequestWithUnsupportedFloorCurrency.imp[0].bidfloor; - let validBidRequests = utils.deepClone(bidRequests); + const bidderRequest = utils.deepClone(bidderRequestBase); + const validBidRequests = bidderRequest.bids; + validBidRequests[0] = { ...validBidRequests[0], getFloor: ({ size, currency, mediaType }) => { @@ -1131,30 +823,23 @@ describe('OguryBidAdapter', function () { } } }; + const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUnsupportedFloorCurrency); + expect(request.data.imp[0].bidfloor).to.be.an('undefined'); }); - it('should not add gpid if ortb2 undefined', () => { - const expectedRequestWithUndefinedGpid = utils.deepClone(expectedRequestObject) - - delete expectedRequestWithUndefinedGpid.imp[0].ext.gpid; - delete expectedRequestWithUndefinedGpid.imp[1].ext.gpid; - - const validBidRequests = utils.deepClone(bidRequests); + it('should use adUnitCode when gpid from ortb2 is undefined', () => { + const bidderRequest = utils.deepClone(bidderRequestBase); + const validBidRequests = bidderRequest.bids; delete validBidRequests[0].ortb2Imp.ext.gpid; const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUndefinedGpid); + expect(request.data.imp[0].ext.gpid).to.equal(bidRequests[0].adUnitCode); }); - it('should not add gpid if gpid undefined', () => { - const expectedRequestWithUndefinedGpid = utils.deepClone(expectedRequestObject) - - delete expectedRequestWithUndefinedGpid.imp[0].ext.gpid; - delete expectedRequestWithUndefinedGpid.imp[1].ext.gpid; - - const validBidRequests = utils.deepClone(bidRequests); + it('should use adUnitCode when gpid is not present in ortb2Imp object', () => { + const bidderRequest = utils.deepClone(bidderRequestBase); + const validBidRequests = bidderRequest.bids; validBidRequests[0] = { ...validBidRequests[0], ortb2Imp: { @@ -1163,18 +848,17 @@ describe('OguryBidAdapter', function () { }; const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUndefinedGpid); + expect(request.data.imp[0].ext.gpid).to.equal(bidRequests[0].adUnitCode); }); - it('should send gpid in bid request', function() { - const validBidRequests = utils.deepClone(bidRequests) + it('should set the actual site location in site.page when the ORTB object contains the referrer instead of the current location', () => { + const bidderRequest = utils.deepClone(bidderRequestBase); + bidderRequest.ortb2.site.page = 'https://google.com'; - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestObject); - expect(request.data.imp[0].ext.gpid).to.be.a('string'); - expect(request.data.imp[1].ext.gpid).to.be.undefined - }) - }) + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.site.page).to.equal(currentLocation); + }); + }); describe('interpretResponse', function () { let openRtbBidResponse = { @@ -1186,7 +870,7 @@ describe('OguryBidAdapter', function () { impid: 'bidId', price: 100, nurl: 'url', - adm: `test creative
cookies
`, + adm: `
cookies
`, adomain: ['renault.fr'], ext: { adcontent: 'sample_creative', @@ -1205,7 +889,7 @@ describe('OguryBidAdapter', function () { impid: 'bidId2', price: 150, nurl: 'url2', - adm: `test creative
cookies
`, + adm: `
cookies
`, adomain: ['peugeot.fr'], ext: { adcontent: 'sample_creative', @@ -1225,57 +909,27 @@ describe('OguryBidAdapter', function () { } }; - it('should correctly interpret bidResponse', () => { - let expectedInterpretedBidResponse = [{ - requestId: openRtbBidResponse.body.seatbid[0].bid[0].impid, - cpm: openRtbBidResponse.body.seatbid[0].bid[0].price, - currency: 'USD', - width: openRtbBidResponse.body.seatbid[0].bid[0].w, - height: openRtbBidResponse.body.seatbid[0].bid[0].h, - ad: openRtbBidResponse.body.seatbid[0].bid[0].adm, - ttl: 60, - ext: openRtbBidResponse.body.seatbid[0].bid[0].ext, - creativeId: openRtbBidResponse.body.seatbid[0].bid[0].id, - netRevenue: true, - meta: { - advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain - }, - nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl, - adapterVersion: '1.7.0', - prebidVersion: '$prebid.version$' - }, { - requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, - cpm: openRtbBidResponse.body.seatbid[0].bid[1].price, - currency: 'USD', - width: openRtbBidResponse.body.seatbid[0].bid[1].w, - height: openRtbBidResponse.body.seatbid[0].bid[1].h, - ad: openRtbBidResponse.body.seatbid[0].bid[1].adm, - ttl: 60, - ext: openRtbBidResponse.body.seatbid[0].bid[1].ext, - creativeId: openRtbBidResponse.body.seatbid[0].bid[1].id, - netRevenue: true, - meta: { - advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain - }, - nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl, - adapterVersion: '1.7.0', - prebidVersion: '$prebid.version$' - }] + function assertPrebidBidResponse(prebidBidResponse, ortbResponse) { + expect(prebidBidResponse.ttl).to.equal(60); + expect(prebidBidResponse.currency).to.equal('USD'); + expect(prebidBidResponse.netRevenue).to.be.true; + expect(prebidBidResponse.mediaType).to.equal('banner'); + expect(prebidBidResponse.requestId).to.equal(ortbResponse.impid); + expect(prebidBidResponse.cpm).to.equal(ortbResponse.price); + expect(prebidBidResponse.width).to.equal(ortbResponse.w); + expect(prebidBidResponse.height).to.equal(ortbResponse.h); + expect(prebidBidResponse.ad).to.contain(ortbResponse.adm); + expect(prebidBidResponse.meta.advertiserDomains).to.equal(ortbResponse.adomain); + expect(prebidBidResponse.seatBidId).to.equal(ortbResponse.id); + } - let request = spec.buildRequests(bidRequests, bidderRequest); - let result = spec.interpretResponse(openRtbBidResponse, request); + it('should correctly interpret bidResponse', () => { + const request = spec.buildRequests(bidRequests, bidderRequestBase); + const result = spec.interpretResponse(openRtbBidResponse, request); - expect(result).to.deep.equal(expectedInterpretedBidResponse) + assertPrebidBidResponse(result[0], openRtbBidResponse.body.seatbid[0].bid[0]) + assertPrebidBidResponse(result[1], openRtbBidResponse.body.seatbid[0].bid[1]) }); - - it('should return empty array if error during parsing', () => { - const wrongOpenRtbBidReponse = 'wrong data' - let request = spec.buildRequests(bidRequests, bidderRequest); - let result = spec.interpretResponse(wrongOpenRtbBidReponse, request); - - expect(result).to.be.instanceof(Array); - expect(result.length).to.equal(0) - }) }); describe('onBidWon', function() {