UNPKG

mk9-prebid

Version:

Header Bidding Management Library

1,477 lines (1,327 loc) 76.4 kB
import {expect} from 'chai'; import {spec, USER_ID_CODE_TO_QUERY_ARG} from 'modules/openxBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from 'src/mediaTypes.js'; import {userSync} from 'src/userSync.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; const URLBASE = '/w/1.0/arj'; const URLBASEVIDEO = '/v/1.0/avjp'; describe('OpenxAdapter', function () { const adapter = newBidder(spec); /** * Type Definitions */ /** * @typedef {{ * impression: string, * inview: string, * click: string * }} */ let OxArjTracking; /** * @typedef {{ * ads: { * version: number, * count: number, * pixels: string, * ad: Array<OxArjAdUnit> * } * }} */ let OxArjResponse; /** * @typedef {{ * adunitid: number, * adid:number, * type: string, * htmlz: string, * framed: number, * is_fallback: number, * ts: string, * cpipc: number, * pub_rev: string, * tbd: ?string, * adv_id: string, * deal_id: string, * auct_win_is_deal: number, * brand_id: string, * currency: string, * idx: string, * creative: Array<OxArjCreative> * }} */ let OxArjAdUnit; /** * @typedef {{ * id: string, * width: string, * height: string, * target: string, * mime: string, * media: string, * tracking: OxArjTracking * }} */ let OxArjCreative; // HELPER METHODS /** * @type {OxArjCreative} */ const DEFAULT_TEST_ARJ_CREATIVE = { id: '0', width: 'test-width', height: 'test-height', target: 'test-target', mime: 'test-mime', media: 'test-media', tracking: { impression: 'test-impression', inview: 'test-inview', click: 'test-click' } }; /** * @type {OxArjAdUnit} */ const DEFAULT_TEST_ARJ_AD_UNIT = { adunitid: 0, type: 'test-type', html: 'test-html', framed: 0, is_fallback: 0, ts: 'test-ts', tbd: 'NaN', deal_id: undefined, auct_win_is_deal: undefined, cpipc: 0, pub_rev: 'test-pub_rev', adv_id: 'test-adv_id', brand_id: 'test-brand_id', currency: 'test-currency', idx: '0', creative: [DEFAULT_TEST_ARJ_CREATIVE] }; /** * @type {OxArjResponse} */ const DEFAULT_ARJ_RESPONSE = { ads: { version: 0, count: 1, pixels: 'https://testpixels.net', ad: [DEFAULT_TEST_ARJ_AD_UNIT] } }; // Sample bid requests const BANNER_BID_REQUESTS_WITH_MEDIA_TYPES = [{ bidder: 'openx', params: { unit: '11', delDomain: 'test-del-domain' }, adUnitCode: '/adunit-code/test-path', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1', ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } }, }, { bidder: 'openx', params: { unit: '22', delDomain: 'test-del-domain' }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[728, 90]] } }, bidId: 'test-bid-id-2', bidderRequestId: 'test-bid-request-2', auctionId: 'test-auction-2', ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-1' } } }, }]; const VIDEO_BID_REQUESTS_WITH_MEDIA_TYPES = [{ bidder: 'openx', mediaTypes: { video: { playerSize: [640, 480] } }, params: { unit: '12345678', delDomain: 'test-del-domain' }, adUnitCode: 'adunit-code', bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e', ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } }, }]; const MULTI_FORMAT_BID_REQUESTS = [{ bidder: 'openx', params: { unit: '12345678', delDomain: 'test-del-domain' }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250]] }, video: { playerSize: [300, 250] } }, bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e', ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } }, }]; describe('inherited functions', function () { it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); describe('isBidRequestValid', function () { describe('when request is for a banner ad', function () { let bannerBid; beforeEach(function () { bannerBid = { bidder: 'openx', params: {}, adUnitCode: 'adunit-code', mediaTypes: {banner: {}}, sizes: [[300, 250], [300, 600]], bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475' }; }); it('should return false when there is no delivery domain', function () { bannerBid.params = {'unit': '12345678'}; expect(spec.isBidRequestValid(bannerBid)).to.equal(false); }); describe('when there is a delivery domain', function () { beforeEach(function () { bannerBid.params = {delDomain: 'test-delivery-domain'} }); it('should return false when there is no ad unit id and size', function () { expect(spec.isBidRequestValid(bannerBid)).to.equal(false); }); it('should return true if there is an adunit id ', function () { bannerBid.params.unit = '12345678'; expect(spec.isBidRequestValid(bannerBid)).to.equal(true); }); it('should return true if there is no adunit id and sizes are defined', function () { bannerBid.mediaTypes.banner.sizes = [720, 90]; expect(spec.isBidRequestValid(bannerBid)).to.equal(true); }); it('should return false if no sizes are defined ', function () { expect(spec.isBidRequestValid(bannerBid)).to.equal(false); }); it('should return false if sizes empty ', function () { bannerBid.mediaTypes.banner.sizes = []; expect(spec.isBidRequestValid(bannerBid)).to.equal(false); }); }); }); describe('when request is for a multiformat ad', function () { describe('and request config uses mediaTypes video and banner', () => { it('should return true multisize when required params found', function () { expect(spec.isBidRequestValid(MULTI_FORMAT_BID_REQUESTS[0])).to.equal(true); }); }); }); describe('when request is for a video ad', function () { describe('and request config uses mediaTypes', () => { const videoBidWithMediaTypes = { bidder: 'openx', params: { unit: '12345678', delDomain: 'test-del-domain' }, adUnitCode: 'adunit-code', mediaTypes: { video: { playerSize: [640, 480] } }, bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' }; it('should return true when required params found', function () { expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(true); }); it('should return false when required params are not passed', function () { let videoBidWithMediaTypes = Object.assign({}, videoBidWithMediaTypes); videoBidWithMediaTypes.params = {}; expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); }); }); describe('and request config uses both delDomain and platform', () => { const videoBidWithDelDomainAndPlatform = { bidder: 'openx', params: { unit: '12345678', delDomain: 'test-del-domain', platform: '1cabba9e-cafe-3665-beef-f00f00f00f00' }, adUnitCode: 'adunit-code', mediaTypes: { video: { playerSize: [640, 480] } }, bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' }; it('should return true when required params found', function () { expect(spec.isBidRequestValid(videoBidWithDelDomainAndPlatform)).to.equal(true); }); it('should return false when required params are not passed', function () { let videoBidWithMediaTypes = Object.assign({}, videoBidWithDelDomainAndPlatform); videoBidWithMediaTypes.params = {}; expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); }); }); describe('and request config uses mediaType', () => { const videoBidWithMediaType = { 'bidder': 'openx', 'params': { 'unit': '12345678', 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', 'mediaType': 'video', 'sizes': [640, 480], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' }; it('should return true when required params found', function () { expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(true); }); it('should return false when required params are not passed', function () { let videoBidWithMediaType = Object.assign({}, videoBidWithMediaType); delete videoBidWithMediaType.params; videoBidWithMediaType.params = {}; expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(false); }); }); describe('and request config uses test', () => { const videoBidWithTest = { bidder: 'openx', params: { unit: '12345678', delDomain: 'test-del-domain', test: true }, adUnitCode: 'adunit-code', mediaTypes: { video: { playerSize: [640, 480] } }, bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' }; let mockBidderRequest = {refererInfo: {}}; it('should return true when required params found', function () { expect(spec.isBidRequestValid(videoBidWithTest)).to.equal(true); }); it('should send video bid request to openx url via GET, with vtest=1 video parameter', function () { const request = spec.buildRequests([videoBidWithTest], mockBidderRequest); expect(request[0].data.vtest).to.equal(1); }); }); }); }); describe('buildRequests for banner ads', function () { const bidRequestsWithMediaTypes = BANNER_BID_REQUESTS_WITH_MEDIA_TYPES; const bidRequestsWithPlatform = [{ 'bidder': 'openx', 'params': { 'unit': '11', 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' }, 'adUnitCode': '/adunit-code/test-path', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', 'auctionId': 'test-auction-1' }, { 'bidder': 'openx', 'params': { 'unit': '11', 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' }, 'adUnitCode': '/adunit-code/test-path', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', 'auctionId': 'test-auction-1' }]; const mockBidderRequest = {refererInfo: {}}; it('should send bid request to openx url via GET, with mediaTypes specified with banner type', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].url).to.equal('https://' + bidRequestsWithMediaTypes[0].params.delDomain + URLBASE); expect(request[0].data.ph).to.be.undefined; expect(request[0].method).to.equal('GET'); }); it('should send bid request to openx platform url via GET, if platform is present', function () { const request = spec.buildRequests(bidRequestsWithPlatform, mockBidderRequest); expect(request[0].url).to.equal(`https://u.openx.net${URLBASE}`); expect(request[0].data.ph).to.equal(bidRequestsWithPlatform[0].params.platform); expect(request[0].method).to.equal('GET'); }); it('should send bid request to openx platform url via GET, if both params present', function () { const bidRequestsWithPlatformAndDelDomain = [{ 'bidder': 'openx', 'params': { 'unit': '11', 'delDomain': 'test-del-domain', 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' }, 'adUnitCode': '/adunit-code/test-path', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', 'auctionId': 'test-auction-1' }, { 'bidder': 'openx', 'params': { 'unit': '11', 'delDomain': 'test-del-domain', 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' }, 'adUnitCode': '/adunit-code/test-path', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', 'auctionId': 'test-auction-1' }]; const request = spec.buildRequests(bidRequestsWithPlatformAndDelDomain, mockBidderRequest); expect(request[0].url).to.equal(`https://u.openx.net${URLBASE}`); expect(request[0].data.ph).to.equal(bidRequestsWithPlatform[0].params.platform); expect(request[0].method).to.equal('GET'); }); it('should send the adunit codes', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].data.divids).to.equal(`${encodeURIComponent(bidRequestsWithMediaTypes[0].adUnitCode)},${encodeURIComponent(bidRequestsWithMediaTypes[1].adUnitCode)}`); }); it('should send the gpids', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].data.aucs).to.equal(`${encodeURIComponent('/12345/my-gpt-tag-0')},${encodeURIComponent('/12345/my-gpt-tag-1')}`); }); it('should send ad unit ids when any are defined', function () { const bidRequestsWithUnitIds = [{ 'bidder': 'openx', 'params': { 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', 'auctionId': 'test-auction-1' }, { 'bidder': 'openx', 'params': { 'unit': '22', 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', mediaTypes: { banner: { sizes: [[728, 90]] } }, 'bidId': 'test-bid-id-2', 'bidderRequestId': 'test-bid-request-2', 'auctionId': 'test-auction-2' }]; const request = spec.buildRequests(bidRequestsWithUnitIds, mockBidderRequest); expect(request[0].data.auid).to.equal(`,${bidRequestsWithUnitIds[1].params.unit}`); }); it('should not send any ad unit ids when none are defined', function () { const bidRequestsWithoutUnitIds = [{ 'bidder': 'openx', 'params': { 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', 'auctionId': 'test-auction-1' }, { 'bidder': 'openx', 'params': { 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', mediaTypes: { banner: { sizes: [[728, 90]] } }, 'bidId': 'test-bid-id-2', 'bidderRequestId': 'test-bid-request-2', 'auctionId': 'test-auction-2' }]; const request = spec.buildRequests(bidRequestsWithoutUnitIds, mockBidderRequest); expect(request[0].data).to.not.have.any.keys('auid'); }); it('should send out custom params on bids that have customParams specified', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], { params: { 'unit': '12345678', 'delDomain': 'test-del-domain', 'customParams': {'Test1': 'testval1+', 'test2': ['testval2/', 'testval3']} } } ); const request = spec.buildRequests([bidRequest], mockBidderRequest); const dataParams = request[0].data; expect(dataParams.tps).to.exist; expect(dataParams.tps).to.equal(btoa('test1=testval1.&test2=testval2_,testval3')); }); it('should send out custom bc parameter, if override is present', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], { params: { 'unit': '12345678', 'delDomain': 'test-del-domain', 'bc': 'hb_override' } } ); const request = spec.buildRequests([bidRequest], mockBidderRequest); const dataParams = request[0].data; expect(dataParams.bc).to.exist; expect(dataParams.bc).to.equal('hb_override'); }); it('should not send any consent management properties', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].data.gdpr).to.equal(undefined); expect(request[0].data.gdpr_consent).to.equal(undefined); expect(request[0].data.x_gdpr_f).to.equal(undefined); }); describe('when there is a consent management framework', function () { let bidRequests; let mockConfig; let bidderRequest; const IAB_CONSENT_FRAMEWORK_CODE = 1; beforeEach(function () { bidRequests = [{ bidder: 'openx', params: { unit: '12345678-banner', delDomain: 'test-del-domain' }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, bidId: 'test-bid-id', bidderRequestId: 'test-bidder-request-id', auctionId: 'test-auction-id' }, { 'bidder': 'openx', 'mediaTypes': { video: { playerSize: [640, 480] } }, 'params': { 'unit': '12345678-video', 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', bidId: 'test-bid-id', bidderRequestId: 'test-bidder-request-id', auctionId: 'test-auction-id', transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' }]; }); afterEach(function () { config.getConfig.restore(); }); describe('when us_privacy applies', function () { beforeEach(function () { bidderRequest = { uspConsent: '1YYN', refererInfo: {} }; sinon.stub(config, 'getConfig').callsFake((key) => { return utils.deepAccess(mockConfig, key); }); }); it('should send a signal to specify that GDPR applies to this request', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.us_privacy).to.equal('1YYN'); expect(request[1].data.us_privacy).to.equal('1YYN'); }); }); describe('when us_privacy does not applies', function () { beforeEach(function () { bidderRequest = { refererInfo: {} }; sinon.stub(config, 'getConfig').callsFake((key) => { return utils.deepAccess(mockConfig, key); }); }); it('should not send the consent string, when consent string is undefined', function () { delete bidderRequest.uspConsent; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data).to.not.have.property('us_privacy'); expect(request[1].data).to.not.have.property('us_privacy'); }); }); describe('when GDPR applies', function () { beforeEach(function () { bidderRequest = { gdprConsent: { consentString: 'test-gdpr-consent-string', gdprApplies: true }, refererInfo: {} }; mockConfig = { consentManagement: { cmpApi: 'iab', timeout: 1111, allowAuctionWithoutConsent: 'cancel' } }; sinon.stub(config, 'getConfig').callsFake((key) => { return utils.deepAccess(mockConfig, key); }); }); it('should send a signal to specify that GDPR applies to this request', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.gdpr).to.equal(1); expect(request[1].data.gdpr).to.equal(1); }); it('should send the consent string', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); expect(request[1].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); }); it('should send the consent management framework code', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); expect(request[1].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); }); }); describe('when GDPR does not apply', function () { beforeEach(function () { bidderRequest = { gdprConsent: { consentString: 'test-gdpr-consent-string', gdprApplies: false }, refererInfo: {} }; mockConfig = { consentManagement: { cmpApi: 'iab', timeout: 1111, allowAuctionWithoutConsent: 'cancel' } }; sinon.stub(config, 'getConfig').callsFake((key) => { return utils.deepAccess(mockConfig, key); }); }); it('should not send a signal to specify that GDPR does not apply to this request', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.gdpr).to.equal(0); expect(request[1].data.gdpr).to.equal(0); }); it('should send the consent string', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); expect(request[1].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); }); it('should send the consent management framework code', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); expect(request[1].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); }); }); describe('when GDPR consent has undefined data', function () { beforeEach(function () { bidderRequest = { gdprConsent: { consentString: 'test-gdpr-consent-string', gdprApplies: true }, refererInfo: {} }; mockConfig = { consentManagement: { cmpApi: 'iab', timeout: 1111, allowAuctionWithoutConsent: 'cancel' } }; sinon.stub(config, 'getConfig').callsFake((key) => { return utils.deepAccess(mockConfig, key); }); }); it('should not send a signal to specify whether GDPR applies to this request, when GDPR application is undefined', function () { delete bidderRequest.gdprConsent.gdprApplies; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data).to.not.have.property('gdpr'); expect(request[1].data).to.not.have.property('gdpr'); }); it('should not send the consent string, when consent string is undefined', function () { delete bidderRequest.gdprConsent.consentString; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data).to.not.have.property('gdpr_consent'); expect(request[1].data).to.not.have.property('gdpr_consent'); }); it('should not send the consent management framework code, when format is undefined', function () { delete mockConfig.consentManagement.cmpApi; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data).to.not.have.property('x_gdpr_f'); expect(request[1].data).to.not.have.property('x_gdpr_f'); }); }); }); it('should not send a coppa query param when there are no coppa param settings in the bid requests', function () { const bidRequestsWithoutCoppa = [{ bidder: 'openx', params: { unit: '11', delDomain: 'test-del-domain', coppa: false }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1' }, { bidder: 'openx', params: { unit: '22', delDomain: 'test-del-domain' }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[728, 90]] } }, bidId: 'test-bid-id-2', bidderRequestId: 'test-bid-request-2', auctionId: 'test-auction-2' }]; const request = spec.buildRequests(bidRequestsWithoutCoppa, mockBidderRequest); expect(request[0].data).to.not.have.any.keys('tfcd'); }); it('should send a coppa flag there is when there is coppa param settings in the bid requests', function () { const bidRequestsWithCoppa = [{ bidder: 'openx', params: { unit: '11', delDomain: 'test-del-domain', coppa: false }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1' }, { bidder: 'openx', params: { unit: '22', delDomain: 'test-del-domain', coppa: true }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[728, 90]] } }, bidId: 'test-bid-id-2', bidderRequestId: 'test-bid-request-2', auctionId: 'test-auction-2' }]; const request = spec.buildRequests(bidRequestsWithCoppa, mockBidderRequest); expect(request[0].data.tfcd).to.equal(1); }); it('should not send a "no segmentation" flag there no DoNotTrack setting that is set to true', function () { const bidRequestsWithoutDnt = [{ bidder: 'openx', params: { unit: '11', delDomain: 'test-del-domain', doNotTrack: false }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1' }, { bidder: 'openx', params: { unit: '22', delDomain: 'test-del-domain' }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[728, 90]] } }, bidId: 'test-bid-id-2', bidderRequestId: 'test-bid-request-2', auctionId: 'test-auction-2' }]; const request = spec.buildRequests(bidRequestsWithoutDnt, mockBidderRequest); expect(request[0].data).to.not.have.any.keys('ns'); }); it('should send a "no segmentation" flag there is any DoNotTrack setting that is set to true', function () { const bidRequestsWithDnt = [{ bidder: 'openx', params: { unit: '11', delDomain: 'test-del-domain', doNotTrack: false }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1' }, { bidder: 'openx', params: { unit: '22', delDomain: 'test-del-domain', doNotTrack: true }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[728, 90]] } }, bidId: 'test-bid-id-2', bidderRequestId: 'test-bid-request-2', auctionId: 'test-auction-2' }]; const request = spec.buildRequests(bidRequestsWithDnt, mockBidderRequest); expect(request[0].data.ns).to.equal(1); }); describe('when schain is provided', function () { let bidRequests; let schainConfig; const supplyChainNodePropertyOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain']; beforeEach(function () { schainConfig = { 'ver': '1.0', 'complete': 1, 'nodes': [ { 'asi': 'exchange1.com', 'sid': '1234', 'hp': 1, 'rid': 'bid-request-1', 'name': 'publisher', 'domain': 'publisher.com' // omitted ext }, { 'asi': 'exchange2.com', 'sid': 'abcd', 'hp': 1, 'rid': 'bid-request-2', // name field missing 'domain': 'intermediary.com' }, { 'asi': 'exchange3.com', 'sid': '4321', 'hp': 1, // request id // name field missing 'domain': 'intermediary-2.com' } ] }; bidRequests = [{ 'bidder': 'openx', 'params': { 'unit': '11', 'delDomain': 'test-del-domain' }, 'adUnitCode': '/adunit-code/test-path', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', 'auctionId': 'test-auction-1', 'schain': schainConfig }]; }); it('should send a schain parameter with the proper delimiter symbols', function () { const request = spec.buildRequests(bidRequests, mockBidderRequest); const dataParams = request[0].data; const numNodes = schainConfig.nodes.length; // each node will have a ! to denote beginning of a new node expect(dataParams.schain.match(/!/g).length).to.equal(numNodes); // 1 comma in the front for version // 5 commas per node expect(dataParams.schain.match(/,/g).length).to.equal(numNodes * 5 + 1); }); it('should send a schain with the right version', function () { const request = spec.buildRequests(bidRequests, mockBidderRequest); const dataParams = request[0].data; let serializedSupplyChain = dataParams.schain.split('!'); let version = serializedSupplyChain.shift().split(',')[0]; expect(version).to.equal(bidRequests[0].schain.ver); }); it('should send a schain with the right complete value', function () { const request = spec.buildRequests(bidRequests, mockBidderRequest); const dataParams = request[0].data; let serializedSupplyChain = dataParams.schain.split('!'); let isComplete = serializedSupplyChain.shift().split(',')[1]; expect(isComplete).to.equal(String(bidRequests[0].schain.complete)); }); it('should send all available params in the right order', function () { const request = spec.buildRequests(bidRequests, mockBidderRequest); const dataParams = request[0].data; let serializedSupplyChain = dataParams.schain.split('!'); serializedSupplyChain.shift(); serializedSupplyChain.forEach((serializedNode, nodeIndex) => { let nodeProperties = serializedNode.split(','); nodeProperties.forEach((nodeProperty, propertyIndex) => { let node = schainConfig.nodes[nodeIndex]; let key = supplyChainNodePropertyOrder[propertyIndex]; expect(nodeProperty).to.equal(node[key] ? String(node[key]) : '', `expected node '${nodeIndex}' property '${nodeProperty}' to key '${key}' to be the same value`) }); }); }); }); describe('when there are userid providers', function () { const EXAMPLE_DATA_BY_ATTR = { britepoolid: '1111-britepoolid', criteoId: '1111-criteoId', fabrickId: '1111-fabrickid', haloId: '1111-haloid', id5id: {uid: '1111-id5id'}, idl_env: '1111-idl_env', IDP: '1111-zeotap-idplusid', idxId: '1111-idxid', intentIqId: '1111-intentiqid', lipb: {lipbid: '1111-lipb'}, lotamePanoramaId: '1111-lotameid', merkleId: '1111-merkleid', netId: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', parrableId: { eid: 'eidVersion.encryptionKeyReference.encryptedValue' }, pubcid: '1111-pubcid', quantcastId: '1111-quantcastid', tapadId: '111-tapadid', tdid: '1111-tdid', uid2: {id: '1111-uid2'}, flocId: {id: '12144', version: 'chrome.1.1'}, novatiq: {snowflake: '1111-novatiqid'}, admixerId: '1111-admixerid', deepintentId: '1111-deepintentid', dmdId: '111-dmdid', nextrollId: '1111-nextrollid', mwOpenLinkId: '1111-mwopenlinkid', dapId: '1111-dapId', amxId: '1111-amxid', }; // generates the same set of tests for each id provider utils._each(USER_ID_CODE_TO_QUERY_ARG, (userIdQueryArg, userIdProviderKey) => { describe(`with userId attribute: ${userIdProviderKey}`, function () { it(`should not send a ${userIdQueryArg} query param when there is no userId.${userIdProviderKey} defined in the bid requests`, function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].data).to.not.have.any.keys(userIdQueryArg); }); it(`should send a ${userIdQueryArg} query param when userId.${userIdProviderKey} is defined in the bid requests`, function () { const bidRequestsWithUserId = [{ bidder: 'openx', params: { unit: '11', delDomain: 'test-del-domain' }, userId: { }, adUnitCode: 'adunit-code', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1' }]; // enrich bid request with userId key/value bidRequestsWithUserId[0].userId[userIdProviderKey] = EXAMPLE_DATA_BY_ATTR[userIdProviderKey]; const request = spec.buildRequests(bidRequestsWithUserId, mockBidderRequest); let userIdValue; // handle cases where userId key refers to an object switch (userIdProviderKey) { case 'flocId': userIdValue = EXAMPLE_DATA_BY_ATTR.flocId.id; break; case 'uid2': userIdValue = EXAMPLE_DATA_BY_ATTR.uid2.id; break; case 'lipb': userIdValue = EXAMPLE_DATA_BY_ATTR.lipb.lipbid; break; case 'parrableId': userIdValue = EXAMPLE_DATA_BY_ATTR.parrableId.eid; break; case 'id5id': userIdValue = EXAMPLE_DATA_BY_ATTR.id5id.uid; break; case 'novatiq': userIdValue = EXAMPLE_DATA_BY_ATTR.novatiq.snowflake; break; default: userIdValue = EXAMPLE_DATA_BY_ATTR[userIdProviderKey]; } expect(request[0].data[USER_ID_CODE_TO_QUERY_ARG[userIdProviderKey]]).to.equal(userIdValue); }); }); }); }); describe('floors', function () { it('should send out custom floors on bids that have customFloors specified', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], { params: { 'unit': '12345678', 'delDomain': 'test-del-domain', 'customFloor': 1.500001 } } ); const request = spec.buildRequests([bidRequest], mockBidderRequest); const dataParams = request[0].data; expect(dataParams.aumfs).to.exist; expect(dataParams.aumfs).to.equal('1500'); }); context('with floors module', function () { let adServerCurrencyStub; beforeEach(function () { adServerCurrencyStub = sinon .stub(config, 'getConfig') .withArgs('currency.adServerCurrency') }); afterEach(function () { config.getConfig.restore(); }); it('should send out floors on bids', function () { const bidRequest1 = Object.assign({}, bidRequestsWithMediaTypes[0], { getFloor: () => { return { currency: 'AUS', floor: 9.99 } } } ); const bidRequest2 = Object.assign({}, bidRequestsWithMediaTypes[1], { getFloor: () => { return { currency: 'AUS', floor: 18.881 } } } ); const request = spec.buildRequests([bidRequest1, bidRequest2], mockBidderRequest); const dataParams = request[0].data; expect(dataParams.aumfs).to.exist; expect(dataParams.aumfs).to.equal('9990,18881'); }); it('should send out floors on bids in the default currency', function () { const bidRequest1 = Object.assign({}, bidRequestsWithMediaTypes[0], { getFloor: () => { return {}; } } ); let getFloorSpy = sinon.spy(bidRequest1, 'getFloor'); spec.buildRequests([bidRequest1], mockBidderRequest); expect(getFloorSpy.args[0][0].mediaType).to.equal(BANNER); expect(getFloorSpy.args[0][0].currency).to.equal('USD'); }); it('should send out floors on bids in the ad server currency if defined', function () { adServerCurrencyStub.returns('bitcoin'); const bidRequest1 = Object.assign({}, bidRequestsWithMediaTypes[0], { getFloor: () => { return {}; } } ); let getFloorSpy = sinon.spy(bidRequest1, 'getFloor'); spec.buildRequests([bidRequest1], mockBidderRequest); expect(getFloorSpy.args[0][0].mediaType).to.equal(BANNER); expect(getFloorSpy.args[0][0].currency).to.equal('bitcoin'); }); }) }) }); describe('buildRequests for video', function () { const bidRequestsWithMediaTypes = VIDEO_BID_REQUESTS_WITH_MEDIA_TYPES; const mockBidderRequest = {refererInfo: {}}; it('should send bid request to openx url via GET, with mediaTypes having video parameter', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].url).to.equal('https://' + bidRequestsWithMediaTypes[0].params.delDomain + URLBASEVIDEO); expect(request[0].method).to.equal('GET'); }); it('should have the correct parameters', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); const dataParams = request[0].data; expect(dataParams.auid).to.equal('12345678'); expect(dataParams.vht).to.equal(480); expect(dataParams.vwd).to.equal(640); expect(dataParams.aucs).to.equal(encodeURIComponent('/12345/my-gpt-tag-0')); }); it('shouldn\'t have the test parameter', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].data.vtest).to.be.undefined; }); it('should send a bc parameter', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); const dataParams = request[0].data; expect(dataParams.bc).to.have.string('hb_pb'); }); describe('when using the video param', function () { let videoBidRequest; let mockBidderRequest = {refererInfo: {}}; beforeEach(function () { videoBidRequest = { 'bidder': 'openx', 'mediaTypes': { video: { context: 'instream', playerSize: [640, 480] } }, 'params': { 'unit': '12345678', 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' }; mockBidderRequest = {refererInfo: {}}; }); it('should not allow you to set a url', function () { videoBidRequest.params.video = { url: 'test-url' }; const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.url).to.be.undefined; }); it('should not allow you to override the javascript url', function () { let myUrl = 'my-url'; videoBidRequest.params.video = { ju: myUrl }; const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.ju).to.not.equal(myUrl); }); describe('when using the openrtb video params', function () { it('should parse legacy params.video.openrtb', function () { let myOpenRTBObject = {mimes: ['application/javascript']}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); it('should parse legacy params.openrtb', function () { let myOpenRTBObject = {mimes: ['application/javascript']}; videoBidRequest.params.openrtb = myOpenRTBObject; const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); it('should parse legacy params.video', function () { let myOpenRTBObject = {mimes: ['application/javascript']}; videoBidRequest.params.video = myOpenRTBObject; const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); it('should parse legacy params.video as full openrtb', function () { let myOpenRTBObject = {imp: [{video: {mimes: ['application/javascript']}}]}; videoBidRequest.params.video = myOpenRTBObject; const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); it('should parse legacy video.openrtb', function () { let myOpenRTBObject = {mimes: ['application/javascript']}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); it('should omit filtered values for legacy', function () { let myOpenRTBObject = {mimes: ['application/javascript'], dont: 'use'}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); it('should parse mediatypes.video', function () { videoBidRequest.mediaTypes.video.mimes = ['application/javascript'] videoBidRequest.mediaTypes.video.minduration = 15 const request = spec.buildRequests([videoBidRequest], mockBidderRequest); const openRtbRequestParams = JSON.parse(request[0].data.openrtb); expect(openRtbRequestParams.imp[0].video.mimes).to.eql(['application/javascript']); expect(openRtbRequestParams.imp[0].video.minduration).to.equal(15); }); it('should filter mediatypes.video', function () { videoBidRequest.mediaTypes.video.mimes = ['application/javascript'] videoBidRequest.mediaTypes.video.minnothing = 15 const request = spec.buildRequests([videoBidRequest], mockBidderRequest); const openRtbRequestParams = JSON.parse(request[0].data.openrtb); expect(openRtbRequestParams.imp[0].video.mimes).to.eql(['application/javascript']); expect(openRtbRequestParams.imp[0].video.minnothing).to.equal(undefined); }); it("should use the bidRequest's playerSize", function () { const width = 200; const height = 100; const myOpenRTBObject = {v: height, w: width}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; const request = spec.buildRequests([videoBidRequest], mockBidderRequest); const openRtbRequestParams = JSON.parse(request[0].data.openrtb); expect(openRtbRequestParams.imp[0].video.w).to.equal(640); expect(openRtbRequestParams.imp[0].video.h).to.equal(480); }); }); }); describe('floors', function () { it('should send out custom floors on bids that have customFloors specified', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], { params: { 'unit': '12345678', 'delDomain': 'test-del-domain', 'customFloor': 1.500001 } } ); const request = spec.buildRequests([bidRequest], mockBidderRequest); const dataParams = request[0].data; expect(dataParams.aumfs).to.exist; expect(dataParams.aumfs).to.equal('1500'); }); context('with floors module', function () { let adServerCurrencyStub; function makeBidWithFloorInfo(floorInfo) { return Object.assign(utils.deepClone(bidRequestsWithMediaTypes[0]), { getFloor: () => { return floorInfo; } }); } beforeEach(function () { a