diff --git a/modules/glimpseBidAdapter.js b/modules/glimpseBidAdapter.js index 678e35729da..fc8f8dd4849 100644 --- a/modules/glimpseBidAdapter.js +++ b/modules/glimpseBidAdapter.js @@ -1,18 +1,27 @@ -import { BANNER } from '../src/mediaTypes.js' -import { config } from '../src/config.js' -import { getStorageManager } from '../src/storageManager.js' -import { isArray } from '../src/utils.js' -import { registerBidder } from '../src/adapters/bidderFactory.js' - -const GVLID = 1012 -const BIDDER_CODE = 'glimpse' -const storageManager = getStorageManager({bidderCode: BIDDER_CODE}) -const ENDPOINT = 'https://api.glimpsevault.io/ads/serving/public/v1/prebid' +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { + isArray, + isEmpty, + isEmptyStr, + isStr, + isPlainObject, +} from '../src/utils.js'; + +const GVLID = 1012; +const BIDDER_CODE = 'glimpse'; +const storageManager = getStorageManager({ + gvlid: GVLID, + bidderCode: BIDDER_CODE, +}); +const ENDPOINT = 'https://market.glimpsevault.io/public/v1/prebid'; const LOCAL_STORAGE_KEY = { vault: { jwt: 'gp_vault_jwt', }, -} +}; export const spec = { gvlid: GVLID, @@ -20,126 +29,121 @@ export const spec = { supportedMediaTypes: [BANNER], /** - * Determines whether or not the given bid request is valid + * Determines if the bid request is valid * @param bid {BidRequest} The bid to validate * @return {boolean} */ isBidRequestValid: (bid) => { - return ( - hasValue(bid) && - hasValue(bid.params) && - hasStringValue(bid.params.placementId) - ) + const pid = bid?.params?.pid; + return isStr(pid) && !isEmptyStr(pid); }, /** - * Builds http request for Glimpse bids + * Builds the http request * @param validBidRequests {BidRequest[]} * @param bidderRequest {BidderRequest} * @returns {ServerRequest} */ buildRequests: (validBidRequests, bidderRequest) => { - const auth = getVaultJwt() - const referer = getReferer(bidderRequest) - const gdprConsent = getGdprConsentChoice(bidderRequest) - const bidRequests = validBidRequests.map(processBidRequest) - const firstPartyData = getFirstPartyData() + const url = buildQuery(bidderRequest); + const auth = getVaultJwt(); + const referer = getReferer(bidderRequest); + const imp = validBidRequests.map(processBidRequest); + const fpd = getFirstPartyData(); const data = { auth, data: { referer, - gdprConsent, - bidRequests, - site: firstPartyData.site, - user: firstPartyData.user, - bidderCode: spec.code, - } - } + imp, + fpd, + }, + }; return { method: 'POST', - url: ENDPOINT, + url, data: JSON.stringify(data), options: {}, - } + }; }, /** - * Parse response from Glimpse server - * @param bidResponse {ServerResponse} + * Parse http response + * @param response {ServerResponse} * @returns {Bid[]} */ - interpretResponse: (bidResponse) => { - const isValidResponse = isValidBidResponse(bidResponse) - - if (isValidResponse) { - const {auth, data} = bidResponse.body - setVaultJwt(auth) - return data.bids + interpretResponse: (response) => { + if (isValidResponse(response)) { + const { auth, data } = response.body; + setVaultJwt(auth); + const bids = data.bids.map(processBidResponse); + return bids; } - - return [] + return []; }, -} +}; function setVaultJwt(auth) { - storageManager.setDataInLocalStorage(LOCAL_STORAGE_KEY.vault.jwt, auth) + storageManager.setDataInLocalStorage(LOCAL_STORAGE_KEY.vault.jwt, auth); } function getVaultJwt() { - return storageManager.getDataFromLocalStorage(LOCAL_STORAGE_KEY.vault.jwt) || '' + return ( + storageManager.getDataFromLocalStorage(LOCAL_STORAGE_KEY.vault.jwt) || '' + ); } function getReferer(bidderRequest) { - const hasReferer = - hasValue(bidderRequest) && - hasValue(bidderRequest.refererInfo) && - hasStringValue(bidderRequest.refererInfo.referer) - - if (hasReferer) { - return bidderRequest.refererInfo.referer - } - - return '' + return bidderRequest?.refererInfo?.referer || ''; } -function getGdprConsentChoice(bidderRequest) { - const hasGdprConsent = - hasValue(bidderRequest) && - hasValue(bidderRequest.gdprConsent) +function buildQuery(bidderRequest) { + let url = appendQueryParam(ENDPOINT, 'ver', '$prebid.version$'); - if (hasGdprConsent) { - const gdprConsent = bidderRequest.gdprConsent - const hasGdprApplies = hasBooleanValue(gdprConsent.gdprApplies) + const timeout = config.getConfig('bidderTimeout'); + url = appendQueryParam(url, 'tmax', timeout); - return { - consentString: gdprConsent.consentString || '', - vendorData: gdprConsent.vendorData || {}, - gdprApplies: hasGdprApplies ? gdprConsent.gdprApplies : true, - } + if (gdprApplies(bidderRequest)) { + const consentString = bidderRequest.gdprConsent.consentString; + url = appendQueryParam(url, 'reg', 'gdpr'); + url = appendQueryParam(url, 'cs', consentString); + } else if (ccpaApplies(bidderRequest)) { + url = appendQueryParam(url, 'reg', 'ccpa'); + url = appendQueryParam(url, 'cs', bidderRequest.uspConsent); + } else { + url = appendQueryParam(url, 'reg', 'none'); } + return url; +} - return { - consentString: '', - vendorData: {}, - gdprApplies: false, +function appendQueryParam(url, key, value) { + if (!value) { + return url; } + const prefix = url.includes('?') ? '&' : '?'; + return `${url}${prefix}${key}=${encodeURIComponent(value)}`; +} + +function gdprApplies(bidderRequest) { + return Boolean(bidderRequest?.gdprConsent?.gdprApplies); } -function processBidRequest(bidRequest) { - const demand = bidRequest.params.demand || 'glimpse' - const sizes = normalizeSizes(bidRequest.sizes) - const keywords = bidRequest.params.keywords || {} +function ccpaApplies(bidderRequest) { + return ( + !isEmptyStr(bidderRequest.uspConsent) && + bidderRequest.uspConsent.substr(1, 3) !== '---' + ); +} + +function processBidRequest(bid) { + const sizes = normalizeSizes(bid.sizes); return { - demand, + bid: bid.bidId, + pid: bid.params.pid, sizes, - keywords, - bidId: bidRequest.bidId, - placementId: bidRequest.params.placementId, - unitCode: bidRequest.adUnitCode, - } + }; } function normalizeSizes(sizes) { @@ -147,84 +151,51 @@ function normalizeSizes(sizes) { isArray(sizes) && sizes.length === 2 && !isArray(sizes[0]) && - !isArray(sizes[1]) + !isArray(sizes[1]); if (isSingleSize) { - return [sizes] + return [sizes]; } - return sizes + return sizes; } function getFirstPartyData() { - const siteKeywords = parseGlobalKeywords('site') - const userKeywords = parseGlobalKeywords('user') - - const siteAttributes = getConfig('ortb2.site.ext.data', {}) - const userAttributes = getConfig('ortb2.user.ext.data', {}) - - return { - site: { - keywords: siteKeywords, - attributes: siteAttributes, - }, - user: { - keywords: userKeywords, - attributes: userAttributes, - }, - } + let fpd = config.getConfig('ortb2') || {}; + optimizeObject(fpd); + return fpd; } -function parseGlobalKeywords(scope) { - const keywords = getConfig(`ortb2.${scope}.keywords`, '') - - return keywords - .split(', ') - .filter((keyword) => keyword !== '') -} - -function getConfig(path, defaultValue) { - return config.getConfig(path) || defaultValue -} - -function isValidBidResponse(bidResponse) { - return ( - hasValue(bidResponse) && - hasValue(bidResponse.body) && - hasValue(bidResponse.body.data) && - hasArrayValue(bidResponse.body.data.bids) && - hasStringValue(bidResponse.body.auth) - ) -} - -function hasValue(value) { - return ( - value !== undefined && - value !== null - ) +function optimizeObject(obj) { + if (!isPlainObject(obj)) { + return; + } + for (const [key, value] of Object.entries(obj)) { + optimizeObject(value); + // only delete empty object, array, or string + if ( + (isPlainObject(value) || isArray(value) || isStr(value)) && + isEmpty(value) + ) { + delete obj[key]; + } + } } -function hasBooleanValue(value) { - return ( - hasValue(value) && - typeof value === 'boolean' - ) +function isValidResponse(bidResponse) { + const auth = bidResponse?.body?.auth; + const bids = bidResponse?.body?.data?.bids; + return isStr(auth) && isArray(bids) && !isEmpty(bids); } -function hasStringValue(value) { - return ( - hasValue(value) && - typeof value === 'string' && - value.length > 0 - ) -} +function processBidResponse(bid) { + const meta = bid.meta || {}; + meta.advertiserDomains = bid.meta?.advertiserDomains || []; -function hasArrayValue(value) { - return ( - hasValue(value) && - isArray(value) && - value.length > 0 - ) + return { + ...bid, + meta, + }; } -registerBidder(spec) +registerBidder(spec); diff --git a/modules/glimpseBidAdapter.md b/modules/glimpseBidAdapter.md index 767efcecf54..e82c5d8f32e 100644 --- a/modules/glimpseBidAdapter.md +++ b/modules/glimpseBidAdapter.md @@ -24,15 +24,14 @@ const adUnits = [ sizes: [[300, 250]], }, }, - bids: [{ - bidder: 'glimpse', - params: { - placementId: 'e53a7f564f8f44cc913b', - keywords: { - country: 'uk', + bids: [ + { + bidder: 'glimpse', + params: { + pid: 'e53a7f564f8f44cc913b', }, }, - }], + ], }, -] +]; ``` diff --git a/test/spec/modules/glimpseBidAdapter_spec.js b/test/spec/modules/glimpseBidAdapter_spec.js index 02f5b502a1b..e4ca36e4403 100644 --- a/test/spec/modules/glimpseBidAdapter_spec.js +++ b/test/spec/modules/glimpseBidAdapter_spec.js @@ -1,9 +1,13 @@ -import { BANNER } from '../../../src/mediaTypes' -import { expect } from 'chai' -import { newBidder } from 'src/adapters/bidderFactory.js' -import { spec } from 'modules/glimpseBidAdapter.js' +import { expect } from 'chai'; +import { spec } from 'modules/glimpseBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config'; +import { BANNER } from '../../../src/mediaTypes'; -const ENDPOINT = 'https://api.glimpsevault.io/ads/serving/public/v1/prebid' +const ENDPOINT = 'https://market.glimpsevault.io/public/v1/prebid'; + +const nonStringValues = [null, undefined, 123, true, {}, [], () => {}]; +const nonArrayValues = [null, undefined, 123, true, {}, 'str', () => {}]; const mock = { bidRequest: { @@ -14,7 +18,7 @@ const mock = { adUnitCode: 'banner-div-a', sizes: [[300, 250]], params: { - placementId: 'glimpse-demo-300x250', + pid: 'glimpse-demo-300x250', }, }, bidderRequest: { @@ -23,7 +27,8 @@ const mock = { auctionId: '96692a73-307b-44b8-8e4f-ddfb40341570', timeout: 3000, gdprConsent: { - consentString: 'COzP517OzP517AcABBENAlCsAP_AAAAAAAwIF8NX-T5eL2vju2Zdt7JEaYwfZxyigOgThgQIsW8NwIeFbBoGP2EgHBG4JCQAGBAkkgCBAQMsHGBcCQAAgIgRiRKMYE2MjzNKBJJAigkbc0FACDVunsHS2ZCY70-8O__bPAviADAvUC-AAAAA.YAAAAAAAAAAA', + consentString: + 'COzP517OzP517AcABBENAlCsAP_AAAAAAAwIF8NX-T5eL2vju2Zdt7JEaYwfZxyigOgThgQIsW8NwIeFbBoGP2EgHBG4JCQAGBAkkgCBAQMsHGBcCQAAgIgRiRKMYE2MjzNKBJJAigkbc0FACDVunsHS2ZCY70-8O__bPAviADAvUC-AAAAA.YAAAAAAAAAAA', vendorData: {}, gdprApplies: true, }, @@ -39,7 +44,6 @@ const mock = { data: { bids: [ { - bidder: 'glimpse', requestId: '133baeded6ac94', creativeId: 'glimpse-demo-300x250', adUnitCode: 'banner-div-a', @@ -48,273 +52,379 @@ const mock = { width: 300, height: 250, cpm: 1.04, - pbAg: '1.04', - pbDg: '1.04', - pbHg: '1.04', - pbLg: '1.00', - pbMg: '1.05', netRevenue: true, mediaType: 'banner', ttl: 300, - } + }, ], }, }, -} +}; -const getBidRequest = () => getDeepCopy(mock.bidRequest) +const getBidRequest = () => getDeepCopy(mock.bidRequest); const getBidderRequest = () => ({ bids: [getBidRequest()], ...getDeepCopy(mock.bidderRequest), -}) +}); -const getBidResponseHelper = () => getDeepCopy(mock.bidResponse) const getBidResponse = () => ({ - body: getBidResponseHelper(), -}) + body: getDeepCopy(mock.bidResponse), +}); function getDeepCopy(object) { - return JSON.parse(JSON.stringify(object)) + return JSON.parse(JSON.stringify(object)); } describe('GlimpseProtocolAdapter', () => { - const glimpseAdapter = newBidder(spec) + const glimpseAdapter = newBidder(spec); describe('spec', () => { it('Has defined the glimpse gvlid', () => { - expect(spec.gvlid).to.equal(1012) - }) + expect(spec.gvlid).to.equal(1012); + }); it('Has defined glimpse as the bidder', () => { - expect(spec.code).to.equal('glimpse') - }) + expect(spec.code).to.equal('glimpse'); + }); it('Has defined valid mediaTypes', () => { - expect(spec.supportedMediaTypes).to.deep.equal([BANNER]) - }) - }) + expect(spec.supportedMediaTypes).to.deep.equal([BANNER]); + }); + }); describe('Inherited functions', () => { it('Functions exist and are valid types', () => { - expect(glimpseAdapter.callBids).to.exist.and.to.be.a('function') - expect(glimpseAdapter.getSpec).to.exist.and.to.be.a('function') - }) - }) + expect(glimpseAdapter.callBids).to.exist.and.to.be.a('function'); + expect(glimpseAdapter.getSpec).to.exist.and.to.be.a('function'); + }); + }); describe('isBidRequestValid', () => { - it('Returns true when a bid request has a valid placement id', () => { - const bidRequest = getBidRequest() - - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.true - }) - - it('Returns false when params are empty', () => { - const bidRequest = getBidRequest() - bidRequest.params = {} - - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) - - it('Returns false when params are null', () => { - const bidRequest = getBidRequest() - bidRequest.params = null - - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) - - it('Returns false when params are undefined', () => { - const bidRequest = getBidRequest() - delete bidRequest.params - - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) - - it('Returns false when params are invalid type', () => { - const bidRequest = getBidRequest() - bidRequest.params = 123 - - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) + it('Returns true if placement id is non-empty string', () => { + const bidRequest = getBidRequest(); - it('Returns false when placement id is empty', () => { - const bidRequest = getBidRequest() - bidRequest.params.placementId = '' + const isValidBidRequest = spec.isBidRequestValid(bidRequest); + expect(isValidBidRequest).to.be.true; + }); - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) + it('Returns false if no pid is provided', () => { + const bidRequest = getBidRequest(); + delete bidRequest.params.pid; - it('Returns false when placement id is null', () => { - const bidRequest = getBidRequest() - bidRequest.params.placementId = null + const isValidBidRequest = spec.isBidRequestValid(bidRequest); + expect(isValidBidRequest).to.be.false; + }); - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) + it('Returns false if pid is empty string', () => { + const bidRequest = getBidRequest(); + bidRequest.params.pid = ''; - it('Returns false when placement id is undefined', () => { - const bidRequest = getBidRequest() - delete bidRequest.params.placementId + const isValidBidRequest = spec.isBidRequestValid(bidRequest); + expect(isValidBidRequest).to.be.false; + }); - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) + it('Returns false if pid is not string', () => { + const bidRequest = getBidRequest(); + const invalidPids = nonStringValues; - it('Returns false when placement id has an invalid type', () => { - const bidRequest = getBidRequest() - bidRequest.params.placementId = 123 - - const isValidBidRequest = spec.isBidRequestValid(bidRequest) - expect(isValidBidRequest).to.be.false - }) - }) + invalidPids.forEach((invalidPid) => { + bidRequest.params.pid = invalidPid; + const isValidBidRequest = spec.isBidRequestValid(bidRequest); + expect(isValidBidRequest).to.be.false; + }); + }); + }); describe('buildRequests', () => { - const bidRequests = [getBidRequest()] - const bidderRequest = getBidderRequest() - - it('Adds GDPR consent', () => { - const request = spec.buildRequests(bidRequests, bidderRequest) - const payload = JSON.parse(request.data) - const expected = bidderRequest.gdprConsent.consentString - - expect(payload.data.gdprConsent).to.exist - expect(payload.data.gdprConsent.gdprApplies).to.be.true - expect(payload.data.gdprConsent.consentString).to.equal(expected) - }) - - it('Adds referer information', () => { - const request = spec.buildRequests(bidRequests, bidderRequest) - const payload = JSON.parse(request.data) - const expected = mock.bidderRequest.refererInfo.referer - - expect(payload.data.referer).to.equal(expected) - }) - - it('Sends a POST request to the Glimpse server', () => { - const request = spec.buildRequests(bidRequests) - - expect(request.url).to.equal(ENDPOINT) - expect(request.method).to.equal('POST') - }) - }) + const bidRequests = [getBidRequest()]; + const bidderRequest = getBidderRequest(); + + it('Adds additional info to api request query', () => { + const request = spec.buildRequests(bidRequests, bidderRequest); + const url = new URL(request.url); + const queries = new URLSearchParams(url.search); + + expect(queries.get('ver')).to.exist; + expect(queries.get('tmax')).to.exist; + expect(queries.get('reg')).to.exist; + expect(queries.get('cs')).to.equal( + bidderRequest.gdprConsent.consentString + ); + }); + + it('Has correct payload shape', () => { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.auth).to.be.a('string'); + expect(payload.data).to.be.an('object'); + expect(payload.data.referer).to.be.a('string'); + expect(payload.data.imp).to.be.an('array'); + expect(payload.data.fpd).to.be.an('object'); + }); + + it('Has referer information', () => { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + const expected = mock.bidderRequest.refererInfo.referer; + + expect(payload.data.referer).to.equal(expected); + }); + + it('Has correct bids (imp) shape', () => { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + const imp = payload.data.imp; + + imp.forEach((i) => { + expect(i.bid).to.be.a('string'); + expect(i.pid).to.be.a('string'); + expect(i.sizes).to.be.an('array').that.deep.include([300, 250]); + }); + }); + }); describe('interpretResponse', () => { - it('Handles valid bid responses', () => { - const bidResponse = getBidResponse() - const bids = spec.interpretResponse(bidResponse) - - expect(bids).to.have.lengthOf(1) - expect(bids[0].adUnitCode).to.equal(mock.bidRequest.adUnitCode) - }) - - it('Handles no bid responses', () => { - const bidResponse = getBidResponse() - bidResponse.body.data.bids = [] - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if body is empty', () => { - const bidResponse = getBidResponse() - bidResponse.body = {} - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if body is null', () => { - const bidResponse = getBidResponse() - bidResponse.body = null - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if body is undefined', () => { - const bidResponse = getBidResponse() - delete bidResponse.body - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if body is invalid type', () => { - const bidResponse = getBidResponse() - bidResponse.body = 123 - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if auth is empty', () => { - const bidResponse = getBidResponse() - bidResponse.body.auth = '' - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if auth is null', () => { - const bidResponse = getBidResponse() - bidResponse.body.auth = null - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if auth is undefined', () => { - const bidResponse = getBidResponse() - delete bidResponse.body.auth - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bids if auth is invalid type', () => { - const bidResponse = getBidResponse() - bidResponse.body.auth = 123 - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bid if data is empty', () => { - const bidResponse = getBidResponse() - bidResponse.body.data = {} - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bid if data is null', () => { - const bidResponse = getBidResponse() - bidResponse.body.data = null - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bid if data is undefined', () => { - const bidResponse = getBidResponse() - delete bidResponse.body.data - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - - it('Returns no bid if data is invalid type', () => { - const bidResponse = getBidResponse() - bidResponse.body.data = "This shouldn't be a string" - - const bids = spec.interpretResponse(bidResponse) - expect(bids).to.have.lengthOf(0) - }) - }) -}) + it('Returns valid bids', () => { + const bidResponse = getBidResponse(); + const bids = spec.interpretResponse(bidResponse); + + expect(bids).to.have.lengthOf(1); + expect(bids[0].adUnitCode).to.equal(mock.bidRequest.adUnitCode); + }); + + it('Returns no bids if auth is not string', () => { + const bidResponse = getBidResponse(); + const invalidAuths = nonStringValues; + + invalidAuths.forEach((invalidAuth) => { + bidResponse.body.auth = invalidAuth; + + const bids = spec.interpretResponse(bidResponse); + expect(bids).to.have.lengthOf(0); + }); + }); + + it('Returns no bids if bids is empty', () => { + const bidResponse = getBidResponse(); + bidResponse.body.data.bids = []; + + const bids = spec.interpretResponse(bidResponse); + expect(bids).to.have.lengthOf(0); + }); + + it('Returns no bids if bids is not array', () => { + const bidResponse = getBidResponse(); + const invalidBids = nonArrayValues; + + invalidBids.forEach((invalidBid) => { + bidResponse.body.data.bids = invalidBid; + + const bids = spec.interpretResponse(bidResponse); + expect(bids).to.have.lengthOf(0); + }); + }); + + it('Contains advertiserDomains', () => { + const bidResponse = getBidResponse(); + + const bids = spec.interpretResponse(bidResponse); + bids.forEach((bid) => { + expect(bid.meta.advertiserDomains).to.be.an('array'); + }); + }); + }); + + describe('optimize request fpd data', () => { + const bidRequests = [getBidRequest()]; + const bidderRequest = getBidderRequest(); + + const fpdMockBase = { + site: { + keywords: 'site,keywords', + ext: { + data: { + fpdProvider: { + dataArray: ['data1', 'data2'], + dataObject: { + data1: 'data1', + data2: 'data2', + }, + dataString: 'data1,data2', + }, + }, + }, + }, + user: { + keywords: 'user,keywords', + ext: { + data: { + fpdProvider: { + dataArray: ['data1', 'data2'], + dataObject: { + data1: 'data1', + data2: 'data2', + }, + dataString: 'data1,data2', + }, + }, + }, + }, + }; + + afterEach(() => { + config.getConfig.restore(); + }); + + it('should keep all non-empty fields', () => { + const fpdMock = fpdMockBase; + sinon.stub(config, 'getConfig').withArgs('ortb2').returns(fpdMock); + const expected = fpdMockBase; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + const fpd = payload.data.fpd; + + expect(fpd).to.deep.equal(expected); + }); + + it('should remove all empty objects', () => { + const fpdMock = getDeepCopy(fpdMockBase); + fpdMock.site.ext.data.fpdProvider.dataObject = {}; + fpdMock.user.ext.data.fpdProvider = {}; + sinon.stub(config, 'getConfig').withArgs('ortb2').returns(fpdMock); + + const expected = { + site: { + keywords: 'site,keywords', + ext: { + data: { + fpdProvider: { + dataArray: ['data1', 'data2'], + dataString: 'data1,data2', + }, + }, + }, + }, + user: { + keywords: 'user,keywords', + }, + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + const fpd = payload.data.fpd; + + expect(fpd).to.deep.equal(expected); + }); + + it('should remove all empty arrays', () => { + const fpdMock = getDeepCopy(fpdMockBase); + fpdMock.site.ext.data.fpdProvider.dataArray = []; + fpdMock.user.ext.data.fpdProvider.dataArray = []; + sinon.stub(config, 'getConfig').withArgs('ortb2').returns(fpdMock); + + const expected = { + site: { + keywords: 'site,keywords', + ext: { + data: { + fpdProvider: { + dataObject: { + data1: 'data1', + data2: 'data2', + }, + dataString: 'data1,data2', + }, + }, + }, + }, + user: { + keywords: 'user,keywords', + ext: { + data: { + fpdProvider: { + dataObject: { + data1: 'data1', + data2: 'data2', + }, + dataString: 'data1,data2', + }, + }, + }, + }, + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + const fpd = payload.data.fpd; + + expect(fpd).to.deep.equal(expected); + }); + + it('should remove all empty strings', () => { + const fpdMock = getDeepCopy(fpdMockBase); + fpdMock.site.keywords = ''; + fpdMock.site.ext.data.fpdProvider.dataString = ''; + fpdMock.user.keywords = ''; + fpdMock.user.ext.data.fpdProvider.dataString = ''; + sinon.stub(config, 'getConfig').withArgs('ortb2').returns(fpdMock); + + const expected = { + site: { + ext: { + data: { + fpdProvider: { + dataArray: ['data1', 'data2'], + dataObject: { + data1: 'data1', + data2: 'data2', + }, + }, + }, + }, + }, + user: { + ext: { + data: { + fpdProvider: { + dataArray: ['data1', 'data2'], + dataObject: { + data1: 'data1', + data2: 'data2', + }, + }, + }, + }, + }, + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + const fpd = payload.data.fpd; + + expect(fpd).to.deep.equal(expected); + }); + + it('should remove all empty fields', () => { + const fpdMock = getDeepCopy(fpdMockBase); + fpdMock.site.keywords = ''; + fpdMock.site.ext.data.fpdProvider.dataArray = []; + fpdMock.site.ext.data.fpdProvider.dataObject = {}; + fpdMock.site.ext.data.fpdProvider.dataString = ''; + fpdMock.user.keywords = ''; + fpdMock.user.ext.data.fpdProvider.dataArray = []; + fpdMock.user.ext.data.fpdProvider.dataObject = {}; + fpdMock.user.ext.data.fpdProvider.dataString = ''; + sinon.stub(config, 'getConfig').withArgs('ortb2').returns(fpdMock); + + const expected = {}; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + const fpd = payload.data.fpd; + + expect(fpd).to.deep.equal(expected); + }); + }); +});