UNPKG

mk9-prebid

Version:

Header Bidding Management Library

537 lines (485 loc) 18.2 kB
import { expect } from 'chai'; import { spec } from 'modules/sortableBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; describe('sortableBidAdapter', function() { const adapter = newBidder(spec); describe('isBidRequestValid', function () { function makeBid() { return { 'bidder': 'sortable', 'params': { 'tagId': '403370', 'siteId': 'example.com', 'keywords': { 'key1': 'val1', 'key2': 'val2' } }, 'adUnitCode': 'adunit-code', 'sizes': [ [300, 250] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', }; } it('should return true when required params found', function () { expect(spec.isBidRequestValid(makeBid())).to.equal(true); }); it('should return false when tagId not passed correctly', function () { let bid = makeBid(); delete bid.params.tagId; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when sizes not passed correctly', function () { let bid = makeBid(); delete bid.sizes; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when sizes are wrong length', function () { let bid = makeBid(); bid.sizes = [[300]]; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when sizes are empty', function () { let bid = makeBid(); bid.sizes = []; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when require params are not passed', function () { let bid = makeBid(); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when the keywords are invalid', function () { let bid = makeBid(); bid.params.keywords = { 'badval': 1234 }; expect(spec.isBidRequestValid(bid)).to.equal(false); bid.params.keywords = 'a'; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return true when the keywords are missing or empty', function () { let bid = makeBid(); bid.params.keywords = {}; expect(spec.isBidRequestValid(bid)).to.equal(true); delete bid.params.keywords; expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should return true with video media type', () => { const videoBid = { 'bidder': 'sortable', 'params': { 'tagId': '403370', 'siteId': 'example.com', }, 'adUnitCode': 'adunit-code', 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', 'mediaTypes': { 'video': { } } }; expect(spec.isBidRequestValid(videoBid)).to.equal(true); }); }); describe('buildRequests', function () { const bidRequests = [{ 'bidder': 'sortable', 'params': { 'tagId': '403370', 'siteId': 'example.com', 'floor': 0.21, 'keywords': { 'key1': 'val1', 'key2': 'val2' } }, 'sizes': [ [300, 250] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475' }, { 'bidder': 'sortable', 'params': { 'tagId': '403371', 'siteId': 'example.com', 'floor': 0.21 }, 'sizes': [ [300, 250] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', 'mediaTypes': { 'native': { 'body': {'required': true, 'sendId': true}, 'clickUrl': {'required': true, 'sendId': true}, 'cta': {'required': true, 'sendId': true}, 'icon': {'required': true, 'sendId': true}, 'image': {'required': true, 'sendId': true}, 'sponsoredBy': {'required': true, 'sendId': true}, 'title': {'required': true, 'sendId': true, 'len': 100} } } }]; const request = spec.buildRequests(bidRequests, {refererInfo: { referer: 'http://example.com/page?param=val' }}); const requestBody = JSON.parse(request.data); it('sends bid request to our endpoint via POST', function () { expect(request.method).to.equal('POST'); }); it('attaches source and version to endpoint URL as query params', function () { const ENDPOINT = `https://c.deployads.com/openrtb2/auction?src=$$REPO_AND_VERSION$$&host=example.com`; expect(request.url).to.equal(ENDPOINT); }); it('sends screen dimensions', function () { expect(requestBody.site.device.w).to.equal(screen.width); expect(requestBody.site.device.h).to.equal(screen.height); }); it('includes the ad size in the bid request', function () { expect(requestBody.imp[0].banner.format[0].w).to.equal(300); expect(requestBody.imp[0].banner.format[0].h).to.equal(250); }); it('includes the params in the bid request', function () { expect(requestBody.imp[0].ext.keywords).to.deep.equal( {'key1': 'val1', 'key2': 'val2'} ); expect(requestBody.site.publisher.id).to.equal('example.com'); expect(requestBody.imp[0].tagid).to.equal('403370'); expect(requestBody.imp[0].floor).to.equal(0.21); }); it('sets domain and href correctly', function () { expect(requestBody.site.domain).to.equal('example.com'); expect(requestBody.site.page).to.equal('http://example.com/page?param=val'); }); it('should have the version in native object set for native bid', function() { expect(requestBody.imp[1].native.ver).to.equal('1'); }); it('should have the assets set for native bid', function() { const assets = JSON.parse(requestBody.imp[1].native.request).assets; expect(assets[0]).to.deep.equal({'title': {'len': 100}, 'required': 1, 'id': 0}); expect(assets[1]).to.deep.equal({'img': {'type': 3, 'wmin': 1, 'hmin': 1}, 'required': 1, 'id': 1}); expect(assets[2]).to.deep.equal({'img': {'type': 1, 'wmin': 1, 'hmin': 1}, 'required': 1, 'id': 2}); expect(assets[3]).to.deep.equal({'data': {'type': 2}, 'required': 1, 'id': 3}); expect(assets[4]).to.deep.equal({'data': {'type': 12}, 'required': 1, 'id': 4}); expect(assets[5]).to.deep.equal({'data': {'type': 1}, 'required': 1, 'id': 5}); }); const videoBidRequests = [{ 'bidder': 'sortable', 'params': { 'tagId': '403370', 'siteId': 'example.com', 'video': { 'minduration': 5, 'maxduration': 10, 'startdelay': 0 } }, 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', 'mediaTypes': { 'video': { 'context': 'instream', 'mimes': ['video/x-ms-wmv'], 'playerSize': [[400, 300]], 'api': [0], 'protocols': [2, 3], 'playbackmethod': [1] } } }]; const videoRequest = spec.buildRequests(videoBidRequests, {refererInfo: { referer: 'http://localhost:9876/' }}); const videoRequestBody = JSON.parse(videoRequest.data); it('should include video params', () => { const video = videoRequestBody.imp[0].video; expect(video.mimes).to.deep.equal(['video/x-ms-wmv']); expect(video.w).to.equal(400); expect(video.h).to.equal(300); expect(video.api).to.deep.equal([0]); expect(video.protocols).to.deep.equal([2, 3]); expect(video.playbackmethod).to.deep.equal([1]); expect(video.minduration).to.equal(5); expect(video.maxduration).to.equal(10); expect(video.startdelay).to.equal(0); }); it('sets domain and href correctly', function () { expect(videoRequestBody.site.domain).to.equal('localhost'); expect(videoRequestBody.site.page).to.equal('http://localhost:9876/'); }); const gdprBidRequests = [{ 'bidder': 'sortable', 'params': { 'tagId': '403370', 'siteId': 'example.com', 'floor': 0.21, 'keywords': {} }, 'sizes': [ [300, 250] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475' }]; const consentString = 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='; function getGdprRequestBody(gdprApplies, consentString) { const gdprRequest = spec.buildRequests(gdprBidRequests, {'gdprConsent': { 'gdprApplies': gdprApplies, 'consentString': consentString }, refererInfo: { referer: 'http://localhost:9876/' }}); return JSON.parse(gdprRequest.data); } it('should handle gdprApplies being present and true', function() { const gdprRequestBody = getGdprRequestBody(true, consentString); expect(gdprRequestBody.regs.ext.gdpr).to.equal(1); expect(gdprRequestBody.user.ext.consent).to.equal(consentString); }) it('should handle gdprApplies being present and false', function() { const gdprRequestBody = getGdprRequestBody(false, consentString); expect(gdprRequestBody.regs.ext.gdpr).to.equal(0); expect(gdprRequestBody.user.ext.consent).to.equal(consentString); }) it('should handle gdprApplies being undefined', function() { const gdprRequestBody = getGdprRequestBody(undefined, consentString); expect(gdprRequestBody.regs).to.deep.equal({ext: {}}); expect(gdprRequestBody.user.ext.consent).to.equal(consentString); }) it('should handle gdprConsent being undefined', function() { const gdprRequest = spec.buildRequests(gdprBidRequests, {refererInfo: { referer: 'http://localhost:9876/' }}); const gdprRequestBody = JSON.parse(gdprRequest.data); expect(gdprRequestBody.regs).to.deep.equal({ext: {}}); expect(gdprRequestBody.user.ext.consent).to.equal(undefined); }) const eidsBidRequests = [{ 'bidder': 'sortable', 'params': { 'tagId': '403370', 'siteId': 'example.com', 'floor': 0.21, 'keywords': {} }, 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475' }]; it('should not set user ids when none present', function() { const eidsRequest = spec.buildRequests(eidsBidRequests, {refererInfo: { referer: 'http://localhost:9876/' }}); const eidsRequestBody = JSON.parse(eidsRequest.data); expect(eidsRequestBody.user.ext.eids).to.equal(undefined); }) it('should set user ids when present', function() { eidsBidRequests[0].userId = { criteoId: 'sample-userid' }; const eidsRequest = spec.buildRequests(eidsBidRequests, {refererInfo: { referer: 'http://localhost:9876/' }}); const eidsRequestBody = JSON.parse(eidsRequest.data); expect(eidsRequestBody.user.ext.eids.length).to.equal(1); }) }); describe('interpretResponse', function () { function makeResponse() { return { body: { 'id': '5e5c23a5ba71e78', 'seatbid': [ { 'bid': [ { 'id': '6vmb3isptf', 'crid': 'sortablescreative', 'impid': '322add653672f68', 'price': 1.22, 'adm': '<!-- creative -->', 'attr': [5], 'h': 90, 'nurl': 'http://nurl', 'w': 728 } ], 'seat': 'MOCK' } ], 'bidid': '5e5c23a5ba71e78' } }; } function makeNativeResponse() { return { body: { 'id': '5e5c23a5ba71e77', 'seatbid': [ { 'bid': [ { 'id': '6vmb3isptf', 'crid': 'sortablescreative', 'impid': '322add653672f67', 'price': 1.55, 'adm': '{"native":{"link":{"clicktrackers":[],"url":"https://www.sortable.com/"},"assets":[{"title":{"text":"Ads With Sortable"},"id":1},{"img":{"w":790,"url":"https://path.to/image","h":294},"id":2},{"img":{"url":"https://path.to/icon"},"id":3},{"data":{"value":"Body here"},"id":4},{"data":{"value":"Learn More"},"id":5},{"data":{"value":"Sortable"},"id":6}],"imptrackers":[],"ver":1}}', 'ext': {'ad_format': 'native'}, 'h': 90, 'nurl': 'http://nurl', 'w': 728 } ], 'seat': 'MOCK' } ], 'bidid': '5e5c23a5ba71e77' } }; } const expectedBid = { 'requestId': '322add653672f68', 'cpm': 1.22, 'width': 728, 'height': 90, 'creativeId': 'sortablescreative', 'dealId': null, 'currency': 'USD', 'netRevenue': true, 'mediaType': 'banner', 'meta': { 'advertiserDomains': [] }, 'ttl': 60, 'ad': '<!-- creative --><div style="position:absolute;left:0px;top:0px;visibility:hidden;"><img src="http://nurl"></div>' }; const expectedNativeBid = { 'requestId': '322add653672f67', 'cpm': 1.55, 'width': 728, 'height': 90, 'creativeId': 'sortablescreative', 'dealId': null, 'currency': 'USD', 'netRevenue': true, 'sortable': { 'ad_format': 'native' }, 'mediaType': 'native', 'meta': { 'advertiserDomains': [] }, 'ttl': 60, 'native': { 'clickUrl': 'https://www.sortable.com/', 'title': 'Ads With Sortable', 'image': {'url': 'https://path.to/image', 'height': 294, 'width': 790}, 'icon': 'https://path.to/icon', 'body': 'Body here', 'cta': 'Learn More', 'sponsoredBy': 'Sortable' } }; it('should get the correct bid response', function () { let result = spec.interpretResponse(makeResponse()); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(expectedBid); }); it('should handle a missing crid', function () { let noCridResponse = makeResponse(); delete noCridResponse.body.seatbid[0].bid[0].crid; const fallbackCrid = noCridResponse.body.seatbid[0].bid[0].id; let noCridResult = Object.assign({}, expectedBid, {'creativeId': fallbackCrid}); let result = spec.interpretResponse(noCridResponse); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(noCridResult); }); it('should handle a missing nurl', function () { let noNurlResponse = makeResponse(); delete noNurlResponse.body.seatbid[0].bid[0].nurl; let noNurlResult = Object.assign({}, expectedBid); noNurlResult.ad = '<!-- creative -->'; let result = spec.interpretResponse(noNurlResponse); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(noNurlResult); }); it('should handle a missing adm', function () { let noAdmResponse = makeResponse(); delete noAdmResponse.body.seatbid[0].bid[0].adm; let noAdmResult = Object.assign({}, expectedBid); delete noAdmResult.ad; noAdmResult.adUrl = 'http://nurl'; let result = spec.interpretResponse(noAdmResponse); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(noAdmResult); }); it('handles empty bid response', function () { let response = { body: { 'id': '5e5c23a5ba71e78', 'seatbid': [] } }; let result = spec.interpretResponse(response); expect(result.length).to.equal(0); }); it('should get the correct native bid response', function () { let result = spec.interpretResponse(makeNativeResponse()); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(expectedNativeBid); }); it('fail to parse invalid native bid response', function () { let response = makeNativeResponse(); response.body.seatbid[0].bid[0].adm = '<!-- creative -->'; let result = spec.interpretResponse(response); expect(result.length).to.equal(0); }); it('should keep custom properties', () => { const customProperties = {test: 'a test message', param: {testParam: 1}}; const expectedResult = Object.assign({}, expectedBid, {[spec.code]: customProperties}); const response = makeResponse(); response.body.seatbid[0].bid[0].ext = customProperties; const result = spec.interpretResponse(response); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(expectedResult); }); it('should handle instream response', () => { const response = makeResponse(); const bid = response.body.seatbid[0].bid[0]; delete bid.nurl; bid.ext = {ad_format: 'instream'}; const result = spec.interpretResponse(response)[0]; expect(result.mediaType).to.equal('video'); expect(result.vastXml).to.equal(bid.adm); }); it('should return iframe syncs', () => { const syncResponse = { ext: { sync_dsps: [ ['iframe', 'http://example-dsp/sync-iframe'], ['image', 'http://example-dsp/sync-image'] ] } }; expect(spec.getUserSyncs({iframeEnabled: true}, [{body: syncResponse}])).to.deep.equal([{ type: 'iframe', url: 'http://example-dsp/sync-iframe' }]); }); it('should return image syncs', () => { const syncResponse = { ext: { sync_dsps: [ ['iframe', 'http://example-dsp/sync-iframe'], ['image', 'http://example-dsp/sync-image'] ] } }; expect(spec.getUserSyncs({pixelEnabled: true}, [{body: syncResponse}])).to.deep.equal([{ type: 'image', url: 'http://example-dsp/sync-image' }]); }); }); });