UNPKG

mk9-prebid

Version:

Header Bidding Management Library

1,046 lines (889 loc) 32.4 kB
import { newBidder, registerBidder, preloadBidderMappingFile, storage } from 'src/adapters/bidderFactory.js'; import adapterManager from 'src/adapterManager.js'; import * as ajax from 'src/ajax.js'; import { expect } from 'chai'; import { userSync } from 'src/userSync.js' import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr.js'; import CONSTANTS from 'src/constants.json'; import events from 'src/events.js'; const CODE = 'sampleBidder'; const MOCK_BIDS_REQUEST = { bids: [ { bidId: 1, auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { param: 5 } }, { bidId: 2, auctionId: 'second-bid-id', adUnitCode: 'mock/placement2', params: { badParam: 6 } } ] } function onTimelyResponseStub() { } let wrappedCallback = config.callbackWithBidder(CODE); describe('bidders created by newBidder', function () { let spec; let bidder; let addBidResponseStub; let doneStub; beforeEach(function () { spec = { code: CODE, isBidRequestValid: sinon.stub(), buildRequests: sinon.stub(), interpretResponse: sinon.stub(), getUserSyncs: sinon.stub() }; addBidResponseStub = sinon.stub(); doneStub = sinon.stub(); }); describe('when the ajax response is irrelevant', function () { let ajaxStub; let getConfigSpy; beforeEach(function () { ajaxStub = sinon.stub(ajax, 'ajax'); addBidResponseStub.reset(); getConfigSpy = sinon.spy(config, 'getConfig'); doneStub.reset(); }); afterEach(function () { ajaxStub.restore(); getConfigSpy.restore(); }); it('should let registerSyncs run with invalid alias and aliasSync enabled', function () { config.setConfig({ userSync: { aliasSyncEnabled: true } }); spec.code = 'fakeBidder'; const bidder = newBidder(spec); bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); }); it('should let registerSyncs run with valid alias and aliasSync enabled', function () { config.setConfig({ userSync: { aliasSyncEnabled: true } }); spec.code = 'aliasBidder'; const bidder = newBidder(spec); bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); }); it('should let registerSyncs run with invalid alias and aliasSync disabled', function () { config.setConfig({ userSync: { aliasSyncEnabled: false } }); spec.code = 'fakeBidder'; const bidder = newBidder(spec); bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); }); it('should not let registerSyncs run with valid alias and aliasSync disabled', function () { config.setConfig({ userSync: { aliasSyncEnabled: false } }); spec.code = 'aliasBidder'; const bidder = newBidder(spec); bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(false); }); it('should handle bad bid requests gracefully', function () { const bidder = newBidder(spec); spec.getUserSyncs.returns([]); bidder.callBids({}); bidder.callBids({ bids: 'nothing useful' }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.called).to.equal(false); expect(spec.buildRequests.called).to.equal(false); expect(spec.interpretResponse.called).to.equal(false); }); it('should call buildRequests(bidRequest) the params are valid', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.calledTwice).to.equal(true); expect(spec.buildRequests.calledOnce).to.equal(true); expect(spec.buildRequests.firstCall.args[0]).to.deep.equal(MOCK_BIDS_REQUEST.bids); }); it('should not call buildRequests the params are invalid', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(false); spec.buildRequests.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.calledTwice).to.equal(true); expect(spec.buildRequests.called).to.equal(false); }); it('should filter out invalid bids before calling buildRequests', function () { const bidder = newBidder(spec); spec.isBidRequestValid.onFirstCall().returns(true); spec.isBidRequestValid.onSecondCall().returns(false); spec.buildRequests.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.calledTwice).to.equal(true); expect(spec.buildRequests.calledOnce).to.equal(true); expect(spec.buildRequests.firstCall.args[0]).to.deep.equal([MOCK_BIDS_REQUEST.bids[0]]); }); it('should make no server requests if the spec doesn\'t return any', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.called).to.equal(false); }); it('should make the appropriate POST request', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: url, data: data }); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(url); expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data)); expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'POST', contentType: 'text/plain', withCredentials: true }); }); it('should make the appropriate POST request when options are passed', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; const options = { contentType: 'application/json' }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: url, data: data, options: options }); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(url); expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data)); expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'POST', contentType: 'application/json', withCredentials: true }); }); it('should make the appropriate GET request', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'GET', url: url, data: data }); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`); expect(ajaxStub.firstCall.args[2]).to.be.undefined; expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('should make the appropriate GET request when options are passed', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; const opt = { withCredentials: false } spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'GET', url: url, data: data, options: opt }); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`); expect(ajaxStub.firstCall.args[2]).to.be.undefined; expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: false }); }); it('should make multiple calls if the spec returns them', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns([ { method: 'POST', url: url, data: data }, { method: 'GET', url: url, data: data } ]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.calledTwice).to.equal(true); }); it('should not add bids for each placement code if no requests are given', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns([]); spec.interpretResponse.returns([]); spec.getUserSyncs.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.callCount).to.equal(0); }); it('should emit BEFORE_BIDDER_HTTP events before network requests', function () { const bidder = newBidder(spec); const req = { method: 'POST', url: 'test.url.com', data: { arg: 2 } }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns([req, req]); const eventEmitterSpy = sinon.spy(events, 'emit'); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(ajaxStub.calledTwice).to.equal(true); expect(eventEmitterSpy.getCalls() .filter(call => call.args[0] === CONSTANTS.EVENTS.BEFORE_BIDDER_HTTP) ).to.length(2); eventEmitterSpy.restore(); }); }); describe('when the ajax call succeeds', function () { let ajaxStub; let userSyncStub; let logErrorSpy; beforeEach(function () { ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { const fakeResponse = sinon.stub(); fakeResponse.returns('headerContent'); callbacks.success('response body', { getResponseHeader: fakeResponse }); }); addBidResponseStub.reset(); doneStub.resetBehavior(); userSyncStub = sinon.stub(userSync, 'registerSync') logErrorSpy = sinon.spy(utils, 'logError'); }); afterEach(function () { ajaxStub.restore(); userSyncStub.restore(); utils.logError.restore(); }); it('should call spec.interpretResponse() with the response content', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.getUserSyncs.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(spec.interpretResponse.calledOnce).to.equal(true); const response = spec.interpretResponse.firstCall.args[0] expect(response.body).to.equal('response body') expect(response.headers.get('some-header')).to.equal('headerContent'); expect(spec.interpretResponse.firstCall.args[1]).to.deep.equal({ method: 'POST', url: 'test.url.com', data: {} }); expect(doneStub.calledOnce).to.equal(true); }); it('should call spec.interpretResponse() once for each request made', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns([ { method: 'POST', url: 'test.url.com', data: {} }, { method: 'POST', url: 'test.url.com', data: {} }, ]); spec.getUserSyncs.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(spec.interpretResponse.calledTwice).to.equal(true); expect(doneStub.calledOnce).to.equal(true); }); it('should only add bids for valid adUnit code into the auction, even if the bidder doesn\'t bid on all of them', function () { const bidder = newBidder(spec); const bid = { creativeId: 'creative-id', requestId: '1', ad: 'ad-url.com', cpm: 0.5, height: 200, width: 300, adUnitCode: 'mock/placement', currency: 'USD', netRevenue: true, ttl: 300, bidderCode: 'sampleBidder', sampleBidder: {advertiserId: '12345', networkId: '111222'} }; const bidderRequest = Object.assign({}, MOCK_BIDS_REQUEST); bidderRequest.bids[0].bidder = 'sampleBidder'; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.getUserSyncs.returns([]); spec.interpretResponse.returns(bid); bidder.callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); let bidObject = addBidResponseStub.firstCall.args[1]; // checking the fields added by our code expect(bidObject.originalCpm).to.equal(bid.cpm); expect(bidObject.originalCurrency).to.equal(bid.currency); expect(doneStub.calledOnce).to.equal(true); expect(logErrorSpy.callCount).to.equal(0); expect(bidObject.meta).to.exist; expect(bidObject.meta).to.deep.equal({advertiserId: '12345', networkId: '111222'}); }); it('should call spec.getUserSyncs() with the response', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.getUserSyncs.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(spec.getUserSyncs.calledOnce).to.equal(true); expect(spec.getUserSyncs.firstCall.args[1].length).to.equal(1); expect(spec.getUserSyncs.firstCall.args[1][0].body).to.equal('response body'); expect(spec.getUserSyncs.firstCall.args[1][0].headers).to.have.property('get'); expect(spec.getUserSyncs.firstCall.args[1][0].headers.get).to.be.a('function'); }); it('should register usersync pixels', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(false); spec.buildRequests.returns([]); spec.getUserSyncs.returns([{ type: 'iframe', url: 'usersync.com' }]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(userSyncStub.called).to.equal(true); expect(userSyncStub.firstCall.args[0]).to.equal('iframe'); expect(userSyncStub.firstCall.args[1]).to.equal(spec.code); expect(userSyncStub.firstCall.args[2]).to.equal('usersync.com'); }); it('should logError when required bid response params are missing', function () { const bidder = newBidder(spec); const bid = { requestId: '1', ad: 'ad-url.com', cpm: 0.5, height: 200, width: 300, placementCode: 'mock/placement' }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.getUserSyncs.returns([]); spec.interpretResponse.returns(bid); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(logErrorSpy.calledOnce).to.equal(true); }); it('should logError when required bid response params are undefined', function () { const bidder = newBidder(spec); const bid = { 'ad': 'creative', 'cpm': '1.99', 'width': 300, 'height': 250, 'requestId': '1', 'creativeId': 'some-id', 'currency': undefined, 'netRevenue': true, 'ttl': 360 }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.getUserSyncs.returns([]); spec.interpretResponse.returns(bid); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(logErrorSpy.calledOnce).to.equal(true); }); }); describe('when the ajax call fails', function () { let ajaxStub; beforeEach(function () { ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { callbacks.error('ajax call failed.'); }); addBidResponseStub.reset(); doneStub.reset(); }); afterEach(function () { ajaxStub.restore(); }); it('should not spec.interpretResponse()', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.getUserSyncs.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(spec.interpretResponse.called).to.equal(false); expect(doneStub.calledOnce).to.equal(true); }); it('should not add bids for each adunit code into the auction', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.interpretResponse.returns([]); spec.getUserSyncs.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.callCount).to.equal(0); expect(doneStub.calledOnce).to.equal(true); }); it('should call spec.getUserSyncs() with no responses', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); spec.getUserSyncs.returns([]); bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(spec.getUserSyncs.calledOnce).to.equal(true); expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); expect(doneStub.calledOnce).to.equal(true); }); }); }); describe('registerBidder', function () { let registerBidAdapterStub; let aliasBidAdapterStub; beforeEach(function () { registerBidAdapterStub = sinon.stub(adapterManager, 'registerBidAdapter'); aliasBidAdapterStub = sinon.stub(adapterManager, 'aliasBidAdapter'); }); afterEach(function () { registerBidAdapterStub.restore(); aliasBidAdapterStub.restore(); }); function newEmptySpec() { return { code: CODE, isBidRequestValid: function() { }, buildRequests: function() { }, interpretResponse: function() { }, }; } it('should register a bidder with the adapterManager', function () { registerBidder(newEmptySpec()); expect(registerBidAdapterStub.calledOnce).to.equal(true); expect(registerBidAdapterStub.firstCall.args[0]).to.have.property('callBids'); expect(registerBidAdapterStub.firstCall.args[0].callBids).to.be.a('function'); expect(registerBidAdapterStub.firstCall.args[1]).to.equal(CODE); expect(registerBidAdapterStub.firstCall.args[2]).to.be.undefined; }); it('should register a bidder with the appropriate mediaTypes', function () { const thisSpec = Object.assign(newEmptySpec(), { supportedMediaTypes: ['video'] }); registerBidder(thisSpec); expect(registerBidAdapterStub.calledOnce).to.equal(true); expect(registerBidAdapterStub.firstCall.args[2]).to.deep.equal({supportedMediaTypes: ['video']}); }); it('should register bidders with the appropriate aliases', function () { const thisSpec = Object.assign(newEmptySpec(), { aliases: ['foo', 'bar'] }); registerBidder(thisSpec); expect(registerBidAdapterStub.calledThrice).to.equal(true); // Make sure our later calls don't override the bidder code from previous calls. expect(registerBidAdapterStub.firstCall.args[0].getBidderCode()).to.equal(CODE); expect(registerBidAdapterStub.secondCall.args[0].getBidderCode()).to.equal('foo') expect(registerBidAdapterStub.thirdCall.args[0].getBidderCode()).to.equal('bar') expect(registerBidAdapterStub.firstCall.args[1]).to.equal(CODE); expect(registerBidAdapterStub.secondCall.args[1]).to.equal('foo') expect(registerBidAdapterStub.thirdCall.args[1]).to.equal('bar') }); it('should register alias with their gvlid', function() { const aliases = [ { code: 'foo', gvlid: 1 }, { code: 'bar', gvlid: 2 }, { code: 'baz' } ] const thisSpec = Object.assign(newEmptySpec(), { aliases: aliases }); registerBidder(thisSpec); expect(registerBidAdapterStub.getCall(1).args[0].getSpec().gvlid).to.equal(1); expect(registerBidAdapterStub.getCall(2).args[0].getSpec().gvlid).to.equal(2); expect(registerBidAdapterStub.getCall(3).args[0].getSpec().gvlid).to.equal(undefined); }) it('should register alias with skipPbsAliasing', function() { const aliases = [ { code: 'foo', skipPbsAliasing: true }, { code: 'bar', skipPbsAliasing: false }, { code: 'baz' } ] const thisSpec = Object.assign(newEmptySpec(), { aliases: aliases }); registerBidder(thisSpec); expect(registerBidAdapterStub.getCall(1).args[0].getSpec().skipPbsAliasing).to.equal(true); expect(registerBidAdapterStub.getCall(2).args[0].getSpec().skipPbsAliasing).to.equal(false); expect(registerBidAdapterStub.getCall(3).args[0].getSpec().skipPbsAliasing).to.equal(undefined); }) }) describe('validate bid response: ', function () { let spec; let bidder; let addBidResponseStub; let doneStub; let ajaxStub; let logErrorSpy; let bids = [{ 'ad': 'creative', 'cpm': '1.99', 'width': 300, 'height': 250, 'requestId': '1', 'creativeId': 'some-id', 'currency': 'USD', 'netRevenue': true, 'ttl': 360 }]; beforeEach(function () { spec = { code: CODE, isBidRequestValid: sinon.stub(), buildRequests: sinon.stub(), interpretResponse: sinon.stub(), }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', url: 'test.url.com', data: {} }); addBidResponseStub = sinon.stub(); doneStub = sinon.stub(); ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { const fakeResponse = sinon.stub(); fakeResponse.returns('headerContent'); callbacks.success('response body', { getResponseHeader: fakeResponse }); }); logErrorSpy = sinon.spy(utils, 'logError'); }); afterEach(function () { ajaxStub.restore(); logErrorSpy.restore(); }); it('should add native bids that do have required assets', function () { let bidRequest = { bids: [{ bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { param: 5 }, nativeParams: { title: {'required': true}, }, mediaType: 'native', }] }; let bids1 = Object.assign({}, bids[0], { 'mediaType': 'native', 'native': { 'title': 'Native Creative', 'clickUrl': 'https://www.link.example', } } ); const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); expect(logErrorSpy.callCount).to.equal(0); }); it('should not add native bids that do not have required assets', function () { let bidRequest = { bids: [{ bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { param: 5 }, nativeParams: { title: {'required': true}, }, mediaType: 'native', }] }; let bids1 = Object.assign({}, bids[0], { bidderCode: CODE, mediaType: 'native', native: { title: undefined, clickUrl: 'https://www.link.example', } } ); const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.calledOnce).to.equal(false); expect(logErrorSpy.callCount).to.equal(1); }); it('should add bid when renderer is present on outstream bids', function () { let bidRequest = { bids: [{ bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { param: 5 }, mediaTypes: { video: {context: 'outstream'} } }] }; let bids1 = Object.assign({}, bids[0], { bidderCode: CODE, mediaType: 'video', renderer: {render: () => true, url: 'render.js'}, } ); const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); expect(logErrorSpy.callCount).to.equal(0); }); it('should add banner bids that have no width or height but single adunit size', function () { let bidRequest = { bids: [{ bidder: CODE, bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { param: 5 }, sizes: [[300, 250]], }] }; let bids1 = Object.assign({}, bids[0], { width: undefined, height: undefined } ); const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); expect(logErrorSpy.callCount).to.equal(0); }); }); describe('preload mapping url hook', function() { let fakeTranslationServer; let getLocalStorageStub; let adapterManagerStub; let adUnits = [{ code: 'midroll_1', mediaTypes: { video: { context: 'adpod' } }, bids: [ { bidder: 'sampleBidder1', params: { placementId: 14542875, } } ] }]; beforeEach(function () { fakeTranslationServer = server; getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); config.setConfig({ 'adpod': { 'brandCategoryExclusion': true } }); adapterManagerStub.withArgs('sampleBidder1').returns({ getSpec: function() { return { 'getMappingFileInfo': function() { return { url: 'http://sample.com', refreshInDays: 7, key: `sampleBidder1MappingFile` } } } } }); }); afterEach(function() { getLocalStorageStub.restore(); adapterManagerStub.restore(); config.resetConfig(); }); it('should preload mapping url file', function() { getLocalStorageStub.returns(null); preloadBidderMappingFile(sinon.spy(), adUnits); expect(fakeTranslationServer.requests.length).to.equal(1); }); it('should preload mapping url file for all bidders', function() { let adUnits = [{ code: 'midroll_1', mediaTypes: { video: { context: 'adpod' } }, bids: [ { bidder: 'sampleBidder1', params: { placementId: 14542875, } }, { bidder: 'sampleBidder2', params: { placementId: 123456, } } ] }]; getLocalStorageStub.returns(null); adapterManagerStub.withArgs('sampleBidder2').returns({ getSpec: function() { return { 'getMappingFileInfo': function() { return { url: 'http://sample.com', refreshInDays: 7, key: `sampleBidder2MappingFile` } } } } }); preloadBidderMappingFile(sinon.spy(), adUnits); expect(fakeTranslationServer.requests.length).to.equal(2); config.setConfig({ 'adpod': { 'brandCategoryExclusion': false } }); preloadBidderMappingFile(sinon.spy(), adUnits); expect(fakeTranslationServer.requests.length).to.equal(2); }); it('should make ajax call to update mapping file if data found in localstorage is expired', function() { let clock = sinon.useFakeTimers(utils.timestamp()); getLocalStorageStub.returns(JSON.stringify({ lastUpdated: utils.timestamp() - 8 * 24 * 60 * 60 * 1000, mapping: { 'iab-1': '1' } })); preloadBidderMappingFile(sinon.spy(), adUnits); expect(fakeTranslationServer.requests.length).to.equal(1); clock.restore(); }); it('should not make ajax call to update mapping file if data found in localstorage and is not expired', function () { let clock = sinon.useFakeTimers(utils.timestamp()); getLocalStorageStub.returns(JSON.stringify({ lastUpdated: utils.timestamp(), mapping: { 'iab-1': '1' } })); preloadBidderMappingFile(sinon.spy(), adUnits); expect(fakeTranslationServer.requests.length).to.equal(0); clock.restore(); }); });