diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 28fb564320d..c02a0cb138f 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -80,7 +80,7 @@ export const spec = { code: 'adkernelAdn', - supportedMediaTypes: [VIDEO], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bidRequest) { return 'params' in bidRequest && (typeof bidRequest.params.host === 'undefined' || typeof bidRequest.params.host === 'string') && diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 016cc5794fb..c3f34274692 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -16,7 +16,7 @@ export const spec = { code: 'adkernel', aliases: ['headbidding'], - supportedMediaTypes: [VIDEO], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bidRequest) { return 'params' in bidRequest && typeof bidRequest.params.host !== 'undefined' && 'zoneId' in bidRequest.params && !isNaN(Number(bidRequest.params.zoneId)); diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index 44b1f381bbe..91bf0746afc 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -1,7 +1,7 @@ import * as utils from 'src/utils'; import * as url from 'src/url'; import {registerBidder} from 'src/adapters/bidderFactory'; -import {NATIVE, VIDEO} from 'src/mediaTypes'; +import {BANNER, NATIVE, VIDEO} from 'src/mediaTypes'; /** * Adapter for requesting bids from adxcg.net @@ -9,7 +9,7 @@ import {NATIVE, VIDEO} from 'src/mediaTypes'; */ const BIDDER_CODE = 'adxcg'; -const SUPPORTED_AD_TYPES = [VIDEO, NATIVE]; +const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE]; const SOURCE = 'pbjs10'; export const spec = { code: BIDDER_CODE, diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 21af777bdc5..6e5d4820ee1 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -1,13 +1,12 @@ import { Renderer } from 'src/Renderer'; import * as utils from 'src/utils'; import { registerBidder } from 'src/adapters/bidderFactory'; -import { NATIVE, VIDEO } from 'src/mediaTypes'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes'; import find from 'core-js/library/fn/array/find'; import includes from 'core-js/library/fn/array/includes'; const BIDDER_CODE = 'appnexus'; const URL = '//ib.adnxs.com/ut/v3/prebid'; -const SUPPORTED_AD_TYPES = ['banner', 'video', 'native']; const VIDEO_TARGETING = ['id', 'mimes', 'minduration', 'maxduration', 'startdelay', 'skippable', 'playback_method', 'frameworks']; const USER_PARAMS = ['age', 'external_uid', 'segments', 'gender', 'dnt', 'language']; @@ -31,7 +30,7 @@ const SOURCE = 'pbjs'; export const spec = { code: BIDDER_CODE, aliases: ['appnexusAst', 'brealtime', 'pagescience', 'defymedia', 'gourmetads', 'matomy', 'featureforward', 'oftmedia', 'districtm'], - supportedMediaTypes: [VIDEO, NATIVE], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** * Determines whether or not the given bid request is valid. @@ -103,7 +102,7 @@ export const spec = { serverResponse.tags.forEach(serverBid => { const rtbBid = getRtbBid(serverBid); if (rtbBid) { - if (rtbBid.cpm !== 0 && includes(SUPPORTED_AD_TYPES, rtbBid.ad_type)) { + if (rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) { const bid = newBid(serverBid, rtbBid); bid.mediaType = parseMediaType(rtbBid); bids.push(bid); @@ -197,7 +196,6 @@ function newBid(serverBid, rtbBid) { width: rtbBid.rtb.video.player_width, height: rtbBid.rtb.video.player_height, vastUrl: rtbBid.rtb.video.asset_url, - descriptionUrl: rtbBid.rtb.video.asset_url, ttl: 3600 }); // This supports Outstream Video @@ -209,9 +207,9 @@ function newBid(serverBid, rtbBid) { bid.adResponse.ad = bid.adResponse.ads[0]; bid.adResponse.ad.video = bid.adResponse.ad.rtb.video; } - } else if (rtbBid.rtb['native']) { - const nativeAd = rtbBid.rtb['native']; - bid['native'] = { + } else if (rtbBid.rtb[NATIVE]) { + const nativeAd = rtbBid.rtb[NATIVE]; + bid[NATIVE] = { title: nativeAd.title, body: nativeAd.desc, cta: nativeAd.ctatext, @@ -256,6 +254,7 @@ function bidToTag(bid) { const tag = {}; tag.sizes = transformSizes(bid.sizes); tag.primary_size = tag.sizes[0]; + tag.ad_types = []; tag.uuid = bid.bidId; if (bid.params.placementId) { tag.id = parseInt(bid.params.placementId, 10); @@ -294,19 +293,24 @@ function bidToTag(bid) { tag.keywords = getKeywords(bid.params.keywords); } - if (bid.mediaType === 'native' || utils.deepAccess(bid, 'mediaTypes.native')) { - tag.ad_types = ['native']; + if (bid.mediaType === NATIVE || utils.deepAccess(bid, `mediaTypes.${NATIVE}`)) { + tag.ad_types.push(NATIVE); if (bid.nativeParams) { const nativeRequest = buildNativeRequest(bid.nativeParams); - tag['native'] = {layouts: [nativeRequest]}; + tag[NATIVE] = {layouts: [nativeRequest]}; } } - const videoMediaType = utils.deepAccess(bid, 'mediaTypes.video'); + const videoMediaType = utils.deepAccess(bid, `mediaTypes.${VIDEO}`); const context = utils.deepAccess(bid, 'mediaTypes.video.context'); - if (bid.mediaType === 'video' || (videoMediaType && context !== 'outstream')) { + if (bid.mediaType === VIDEO || videoMediaType) { + tag.ad_types.push(VIDEO); + } + + // instream gets vastUrl, outstream gets vastXml + if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) { tag.require_asset_url = true; } @@ -318,6 +322,13 @@ function bidToTag(bid) { .forEach(param => tag.video[param] = bid.params.video[param]); } + if ( + (utils.isEmpty(bid.mediaType) && utils.isEmpty(bid.mediaTypes)) || + (bid.mediaType === BANNER || (bid.mediaTypes && bid.mediaTypes[BANNER])) + ) { + tag.ad_types.push(BANNER); + } + return tag; } @@ -414,12 +425,12 @@ function handleOutstreamRendererEvents(bid, id, eventName) { function parseMediaType(rtbBid) { const adType = rtbBid.ad_type; - if (adType === 'video') { - return 'video'; - } else if (adType === 'native') { - return 'native'; + if (adType === VIDEO) { + return VIDEO; + } else if (adType === NATIVE) { + return NATIVE; } else { - return 'banner'; + return BANNER; } } diff --git a/modules/audienceNetworkBidAdapter.js b/modules/audienceNetworkBidAdapter.js index 41ca539ccba..2b0e4cd8190 100644 --- a/modules/audienceNetworkBidAdapter.js +++ b/modules/audienceNetworkBidAdapter.js @@ -12,7 +12,7 @@ const code = 'audienceNetwork'; const currency = 'USD'; const method = 'GET'; const url = 'https://an.facebook.com/v2/placementbid.json'; -const supportedMediaTypes = ['video']; +const supportedMediaTypes = ['banner', 'video']; const netRevenue = true; const hb_bidder = 'fan'; diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js new file mode 100644 index 00000000000..df011bc102d --- /dev/null +++ b/modules/colossussspBidAdapter.js @@ -0,0 +1,132 @@ +import { registerBidder } from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; + +const BIDDER_CODE = 'colossusssp'; +const URL = '//colossusssp.com/?c=o&m=multi'; +const URL_SYNC = '//colossusssp.com/?c=o&m=cookie'; + +let sizeObj = { + '468x60': 1, + '728x90': 2, + '300x600': 10, + '300x250': 15, + '300x100': 19, + '320x50': 43, + '300x50': 44, + '300x300': 48, + '300x1050': 54, + '970x90': 55, + '970x250': 57, + '1000x90': 58, + '320x80': 59, + '640x480': 65, + '320x480': 67, + '320x320': 72, + '320x160': 73, + '480x300': 83, + '970x310': 94, + '970x210': 96, + '480x320': 101, + '768x1024': 102, + '1000x300': 113, + '320x100': 117, + '800x250': 118, + '200x600': 119 +}; + +utils._each(sizeObj, (item, key) => sizeObj[item] = key); + +export const spec = { + code: BIDDER_CODE, + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: (bid) => { + return (!isNaN(bid.params.placement_id) && + ((bid.params.sizes !== undefined && bid.params.sizes.length > 0 && bid.params.sizes.some((sizeIndex) => sizeObj[sizeIndex] !== undefined)) || + (bid.sizes !== undefined && bid.sizes.length > 0 && bid.sizes.map((size) => `${size[0]}x${size[1]}`).some((size) => sizeObj[size] !== undefined)))); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: (validBidRequests) => { + let winTop = window; + try { + window.top.location.toString(); + winTop = window.top; + } catch (e) { + utils.logMessage(e); + }; + let location = utils.getTopWindowLocation(); + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language : '', + 'secure': location.protocol === 'https:' ? 1 : 0, + 'host': location.host, + 'page': location.pathname, + 'placements': placements + }; + for (let i = 0; i < validBidRequests.length; i++) { + let bid = validBidRequests[i]; + let placement = {}; + placement['placementId'] = bid.params.placement_id; + placement['bidId'] = bid.bidId; + placement['sizes'] = bid.sizes; + placements.push(placement); + } + return { + method: 'POST', + url: URL, + data: request + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: (serverResponse) => { + let response = []; + try { + serverResponse = serverResponse.body; + for (let i = 0; i < serverResponse.length; i++) { + let resItem = serverResponse[i]; + if (resItem.width && !isNaN(resItem.width) && + resItem.height && !isNaN(resItem.height) && + resItem.requestId && typeof resItem.requestId === 'string' && + resItem.cpm && !isNaN(resItem.cpm) && + resItem.ad && typeof resItem.ad === 'string' && + resItem.ttl && !isNaN(resItem.ttl) && + resItem.creativeId && typeof resItem.creativeId === 'string' && + resItem.netRevenue && typeof resItem.netRevenue === 'boolean' && + resItem.currency && typeof resItem.currency === 'string') { + response.push(resItem); + } + } + } catch (e) { + utils.logMessage(e); + }; + return response; + }, + + getUserSyncs: () => { + return [{ + type: 'image', + url: URL_SYNC + }]; + } +}; + +registerBidder(spec); diff --git a/modules/colossussspBidAdapter.md b/modules/colossussspBidAdapter.md new file mode 100644 index 00000000000..9a5b9a0fe39 --- /dev/null +++ b/modules/colossussspBidAdapter.md @@ -0,0 +1,26 @@ +# Overview + +``` +Module Name: Colossus SSP Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@colossusmediallc.com +``` + +# Description + +Module that connects to Colossus SSP demand sources + +# Test Parameters +``` + var adUnits = [{ + code: 'placementid_0', + sizes: [[300, 250]], + bids: [{ + bidder: 'colossusssp', + params: { + placement_id: 0 + } + }] + } + ]; +``` diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 1f6c4f27b77..cae64983089 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from 'src/utils'; import {registerBidder} from 'src/adapters/bidderFactory'; -import { VIDEO } from 'src/mediaTypes'; +import { BANNER, VIDEO } from 'src/mediaTypes'; const BIDDER_CODE = 'conversant'; const URL = '//media.msg.dotomi.com/s2s/header/24'; @@ -10,7 +10,7 @@ const VERSION = '2.2.1'; export const spec = { code: BIDDER_CODE, aliases: ['cnvr'], // short code - supportedMediaTypes: [VIDEO], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. diff --git a/modules/freewheelSSPBidAdapter.js b/modules/freewheelSSPBidAdapter.js index 64ebb36478b..632df8fe93c 100644 --- a/modules/freewheelSSPBidAdapter.js +++ b/modules/freewheelSSPBidAdapter.js @@ -190,7 +190,7 @@ var getOutstreamScript = function(bid) { export const spec = { code: BIDDER_CODE, - supportedMediaTypes: ['video'], + supportedMediaTypes: ['banner', 'video'], aliases: ['stickyadstv'], // former name for freewheel-ssp /** * Determines whether or not the given bid request is valid. diff --git a/modules/inskinBidAdapter.js b/modules/inskinBidAdapter.js new file mode 100644 index 00000000000..5274b5b2ba8 --- /dev/null +++ b/modules/inskinBidAdapter.js @@ -0,0 +1,210 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'inskin'; + +const CONFIG = { + 'inskin': { + 'BASE_URI': 'https://mfad.inskinad.com/api/v2' + } +}; + +export const spec = { + code: BIDDER_CODE, + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + return !!(bid.params.networkId && bid.params.siteId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + + buildRequests: function(validBidRequests) { + // Do we need to group by bidder? i.e. to make multiple requests for + // different endpoints. + + let ret = { + method: 'POST', + url: '', + data: '', + bidRequest: [] + }; + + if (validBidRequests.length < 1) { + return ret; + } + + let ENDPOINT_URL; + + const data = Object.assign({ + placements: [], + time: Date.now(), + user: {}, + url: utils.getTopWindowUrl(), + referrer: document.referrer, + enableBotFiltering: true, + includePricingData: true, + parallel: true + }, validBidRequests[0].params); + + validBidRequests.map(bid => { + let config = CONFIG[bid.bidder]; + ENDPOINT_URL = config.BASE_URI; + + const placement = Object.assign({ + divName: bid.bidId, + adTypes: bid.adTypes || getSize(bid.sizes), + eventIds: [40, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295] + }, bid.params); + + placement.adTypes.push(5, 9, 163, 2163, 3006); + + if (placement.networkId && placement.siteId) { + data.placements.push(placement); + } + }); + + ret.data = JSON.stringify(data); + ret.bidRequest = validBidRequests; + ret.url = ENDPOINT_URL; + + return ret; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + let bid; + let bids; + let bidId; + let bidObj; + let bidResponses = []; + let bidsMap = {}; + + bids = bidRequest.bidRequest; + + serverResponse = (serverResponse || {}).body; + for (let i = 0; i < bids.length; i++) { + bid = {}; + bidObj = bids[i]; + bidId = bidObj.bidId; + + bidsMap[bidId] = bidObj; + + if (serverResponse) { + const decision = serverResponse.decisions && serverResponse.decisions[bidId]; + const price = decision && decision.pricing && decision.pricing.clearPrice; + + if (decision && price) { + bid.requestId = bidId; + bid.cpm = price; + bid.width = decision.width; + bid.height = decision.height; + bid.ad = retrieveAd(bidId, decision); + bid.currency = 'USD'; + bid.creativeId = decision.adId; + bid.ttl = 360; + bid.netRevenue = true; + bid.referrer = utils.getTopWindowUrl(); + + bidResponses.push(bid); + } + } + } + + if (bidResponses.length) { + window.addEventListener('message', function(e) { + if (!e.data || e.data.from !== 'ism-bid') { + return; + } + + const decision = serverResponse.decisions && serverResponse.decisions[e.data.bidId]; + if (!decision) { + return; + } + + const id = 'ism_tag_' + Math.floor((Math.random() * 10e16)); + window[id] = { + bidId: e.data.bidId, + serverResponse + }; + const script = document.createElement('script'); + script.src = 'https://cdn.inskinad.com/isfe/publishercode/' + bidsMap[e.data.bidId].params.siteId + '/default.js?autoload&id=' + id; + document.getElementsByTagName('head')[0].appendChild(script); + }); + } + + return bidResponses; + }, + + getUserSyncs: function(syncOptions) { + return []; + } +}; + +const sizeMap = [ + null, + '120x90', + '120x90', + '468x60', + '728x90', + '300x250', + '160x600', + '120x600', + '300x100', + '180x150', + '336x280', + '240x400', + '234x60', + '88x31', + '120x60', + '120x240', + '125x125', + '220x250', + '250x250', + '250x90', + '0x0', + '200x90', + '300x50', + '320x50', + '320x480', + '185x185', + '620x45', + '300x125', + '800x250' +]; + +sizeMap[77] = '970x90'; +sizeMap[123] = '970x250'; +sizeMap[43] = '300x600'; + +function getSize(sizes) { + const result = []; + sizes.forEach(function(size) { + const index = sizeMap.indexOf(size[0] + 'x' + size[1]); + if (index >= 0) { + result.push(index); + } + }); + return result; +} + +function retrieveAd(bidId, decision) { + return "'; + + let responseCreativeId = '274'; + let responseCurrency = 'USD'; + + let responseWidth = 300; + let responseHeight = 250; + let responseTtl = 213; + + let sampleResponse = { + id: '66043f5ca44ecd8f8769093b1615b2d9', + seatbid: [ + { + bid: [ + { + id: 'c21bab0e-7668-4d8f-908a-63e094c09197', + impid: '1', + price: responsePrice, + adid: responseCreativeId, + adm: responseCreative, + adomain: [ + 'www.rockyouteststudios.com' + ], + cid: '274', + attr: [], + w: responseWidth, + h: responseHeight, + ext: { + ttl: responseTtl + } + } + ], + seat: '201', + group: 0 + } + ], + bidid: 'c21bab0e-7668-4d8f-908a-63e094c09197', + cur: responseCurrency + }; + + let sampleRequest = { + bidId: incomingRequestId, + mediaTypes: { banner: {} }, + requestId: incomingRequestId + }; + + let result = spec.interpretResponse( + { + body: sampleResponse + }, + sampleRequest + ); + + expect(result.length).to.equal(1); + + let processedBid = result[0]; + + // expect(processedBid.requestId).to.equal(incomingRequestId); + expect(processedBid.cpm).to.equal(responsePrice); + expect(processedBid.width).to.equal(responseWidth); + expect(processedBid.height).to.equal(responseHeight); + expect(processedBid.ad).to.equal(responseCreative); + expect(processedBid.ttl).to.equal(responseTtl); + expect(processedBid.creativeId).to.equal(responseCreativeId); + expect(processedBid.netRevenue).to.equal(true); + expect(processedBid.currency).to.equal(responseCurrency); + }); + + it('returns an valid bid response on sucessful video request', () => { + let incomingRequestId = 'XXtesting-275XX'; + let responsePrice = 6 + + let responseCreative = '\n\n \n \n Mediatastic\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n'; + + let responseCreativeId = '1556'; + let responseCurrency = 'USD'; + + let responseWidth = 284; + let responseHeight = 285; + let responseTtl = 286; + + let sampleResponse = { + id: '1234567890', + seatbid: [ + { + bid: [ + { + id: 'a8ae0b48-a8db-4220-ba0c-7458f452b1f5', + impid: '1', + price: responsePrice, + adid: responseCreativeId, + adm: responseCreative, + cid: '270', + attr: [], + w: responseWidth, + h: responseHeight, + ext: { + ttl: responseTtl + } + } + ], + seat: '201', + group: 0 + } + ], + bidid: 'a8ae0b48-a8db-4220-ba0c-7458f452b1f5', + cur: 'USD' + }; + + let sampleRequest = { + bidId: incomingRequestId, + mediaTypes: { + video: { + } + }, + requestId: incomingRequestId + }; + + let result = spec.interpretResponse( + { + body: sampleResponse + }, + sampleRequest + ); + + expect(result.length).to.equal(1); + + let processedBid = result[0]; + + // expect(processedBid.requestId).to.equal(incomingRequestId); + expect(processedBid.cpm).to.equal(responsePrice); + expect(processedBid.width).to.equal(responseWidth); + expect(processedBid.height).to.equal(responseHeight); + expect(processedBid.ad).to.equal(null); + expect(processedBid.ttl).to.equal(responseTtl); + expect(processedBid.creativeId).to.equal(responseCreativeId); + expect(processedBid.netRevenue).to.equal(true); + expect(processedBid.currency).to.equal(responseCurrency); + expect(processedBid.vastXml).to.equal(responseCreative); + }); + + it('generates event callbacks as expected', () => { + let tally = {}; + let renderer = { + handleVideoEvent: (eventObject) => { + let eventName = eventObject.eventName; + if (tally[eventName]) { + tally[eventName] = tally[eventName] + 1; + } else { + tally[eventName] = 1; + } + } + }; + + let callbacks = internals.playerCallbacks(renderer); + + let validCallbacks = ['LOAD', 'IMPRESSION', 'COMPLETE', 'ERROR']; + + validCallbacks.forEach(event => { + callbacks('n/a', event); + }); + + let callbackKeys = Object.keys(tally); + expect(callbackKeys.length).to.equal(3); + expect(tally['loaded']).to.equal(1); + expect(tally['impression']).to.equal(1); + expect(tally['ended']).to.equal(2); + }); + + it('generates a renderer that will hide on complete', () => { + let elementName = 'test_element_id'; + let selector = `#${elementName}`; + + let mockElement = { + style: { + display: 'some' + } + }; + + document.querySelector = (name) => { + if (name === selector) { + return mockElement; + } else { + return null; + } + }; + + let renderer = internals.generateRenderer({}, elementName); + + renderer.handlers['ended'](); + + expect(mockElement.style.display).to.equal('none'); + }) + }); +}); diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js new file mode 100644 index 00000000000..5cb4a1277fb --- /dev/null +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -0,0 +1,157 @@ +import { + expect +} from 'chai'; +import { + spec +} from 'modules/smartadserverBidAdapter'; +import { + getTopWindowLocation +} from 'src/utils'; +import { + newBidder +} from 'src/adapters/bidderFactory'; +import { + config +} from 'src/config'; +import * as utils from 'src/utils'; + +describe('Smart ad server bid adapter tests', () => { + var DEFAULT_PARAMS = [{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + sizes: [ + [300, 250], + [300, 200] + ], + bidder: 'smartadserver', + params: { + domain: 'http://prg.smartadserver.com', + siteId: '1234', + pageId: '5678', + formatId: '90', + target: 'test=prebid', + bidfloor: 0.420 + }, + requestId: 'efgh5678', + transactionId: 'zsfgzzg' + }]; + + var DEFAULT_PARAMS_WO_OPTIONAL = [{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + sizes: [ + [300, 250], + [300, 200] + ], + bidder: 'smartadserver', + params: { + domain: 'http://prg.smartadserver.com', + siteId: '1234', + pageId: '5678', + formatId: '90' + }, + requestId: 'efgh5678' + }]; + + var BID_RESPONSE = { + body: { + cpm: 12, + width: 300, + height: 250, + creativeId: 'zioeufg', + currency: 'GBP', + isNetCpm: true, + ttl: 300, + adUrl: 'http://awesome.fake.url', + ad: '< --- awesome script --- >' + } + }; + + it('Verify build request', () => { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests(DEFAULT_PARAMS); + expect(request).to.have.property('url').and.to.equal('http://prg.smartadserver.com/prebid/v1'); + expect(request).to.have.property('method').and.to.equal('POST'); + const requestContent = JSON.parse(request.data); + expect(requestContent).to.have.property('siteid').and.to.equal('1234'); + expect(requestContent).to.have.property('pageid').and.to.equal('5678'); + expect(requestContent).to.have.property('formatid').and.to.equal('90'); + expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR'); + expect(requestContent).to.have.property('bidfloor').and.to.equal(0.42); + expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid'); + expect(requestContent).to.have.property('tagId').and.to.equal('sas_42'); + expect(requestContent).to.have.property('sizes'); + expect(requestContent.sizes[0]).to.have.property('w').and.to.equal(300); + expect(requestContent.sizes[0]).to.have.property('h').and.to.equal(250); + expect(requestContent.sizes[1]).to.have.property('w').and.to.equal(300); + expect(requestContent.sizes[1]).to.have.property('h').and.to.equal(200); + expect(requestContent).to.have.property('pageDomain').and.to.equal(utils.getTopWindowUrl()); + expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + }); + + it('Verify parse response', () => { + const request = spec.buildRequests(DEFAULT_PARAMS); + const bids = spec.interpretResponse(BID_RESPONSE, request); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(12); + expect(bid.adUrl).to.equal('http://awesome.fake.url'); + expect(bid.ad).to.equal('< --- awesome script --- >'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('zioeufg'); + expect(bid.currency).to.equal('GBP'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(300); + expect(bid.requestId).to.equal(DEFAULT_PARAMS[0].bidId); + expect(bid.referrer).to.equal(utils.getTopWindowUrl()); + }); + + it('Verifies bidder code', () => { + expect(spec.code).to.equal('smartadserver'); + }); + + it('Verifies bidder aliases', () => { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('smart'); + }); + + it('Verifies if bid request valid', () => { + expect(spec.isBidRequestValid(DEFAULT_PARAMS[0])).to.equal(true); + expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ + params: {} + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + pageid: 123 + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + siteid: 123 + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + formatid: 123, + pageid: 234 + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + domain: 'www.test.com', + pageid: 234 + } + })).to.equal(false); + }); + + it('Verifies sync options', () => { + expect(spec.getUserSyncs).to.be.undefined; + }); +}); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index c1f7fe82252..9605babf3d9 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -897,54 +897,22 @@ describe('Unit: Prebid Module', function () { }); }); - describe.skip('#video', () => { + describe('multiformat requests', () => { let spyCallBids; let createAuctionStub; let adUnits; - before(() => { + beforeEach(() => { adUnits = [{ code: 'adUnit-code', - mediaType: 'video', + mediaTypes: { + banner: {}, + native: {}, + }, sizes: [[300, 250], [300, 600]], bids: [ {bidder: 'appnexus', params: {placementId: 'id'}}, - {bidder: 'sampleBidder', params: {placementId: 'id'}} - ] - }]; - adUnitCodes = ['adUnit-code']; - let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: timeout}); - spyCallBids = sinon.spy(adaptermanager, 'callBids'); - createAuctionStub = sinon.stub(auctionModule, 'newAuction'); - createAuctionStub.returns(auction); - }); - - after(() => { - auctionModule.newAuction.restore(); - adaptermanager.callBids.restore(); - }); - - it('should not callBids if a video adUnit has non-video bidders', () => { - const videoAdaptersBackup = adaptermanager.videoAdapters; - adaptermanager.videoAdapters = ['appnexus']; - $$PREBID_GLOBAL$$.requestBids({adUnits}); - sinon.assert.notCalled(adaptermanager.callBids); - adaptermanager.videoAdapters = videoAdaptersBackup; - }); - }); - - describe('#video', () => { - let spyCallBids; - let createAuctionStub; - let adUnits; - - before(() => { - adUnits = [{ - code: 'adUnit-code', - mediaType: 'video', - sizes: [[300, 250], [300, 600]], - bids: [ - {bidder: 'appnexus', params: {placementId: 'id'}} + {bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}} ] }]; adUnitCodes = ['adUnit-code']; @@ -954,53 +922,32 @@ describe('Unit: Prebid Module', function () { createAuctionStub.returns(auction); }) - after(() => { + afterEach(() => { auctionModule.newAuction.restore(); adaptermanager.callBids.restore(); }); - it('should callBids if a video adUnit has all video bidders', () => { - const videoAdaptersBackup = adaptermanager.videoAdapters; - adaptermanager.videoAdapters = ['appnexus']; + it('bidders that support one of the declared formats are allowed to participate', () => { $$PREBID_GLOBAL$$.requestBids({adUnits}); sinon.assert.calledOnce(adaptermanager.callBids); - adaptermanager.videoAdapters = videoAdaptersBackup; - }); - }); - describe('#native', () => { - let spyCallBids; - let createAuctionStub; - let adUnits; + const spyArgs = adaptermanager.callBids.getCall(0); + const biddersCalled = spyArgs.args[0][0].bids; - before(() => { - adUnits = [{ - code: 'adUnit-code', - mediaType: 'native', - sizes: [[300, 250], [300, 600]], - bids: [ - {bidder: 'appnexus', params: {placementId: 'id'}}, - {bidder: 'sampleBidder', params: {placementId: 'id'}} - ] - }]; - adUnitCodes = ['adUnit-code']; - let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: timeout}); - spyCallBids = sinon.spy(adaptermanager, 'callBids'); - createAuctionStub = sinon.stub(auctionModule, 'newAuction'); - createAuctionStub.returns(auction); + // appnexus and sampleBidder both support banner + expect(biddersCalled.length).to.equal(2); }); - after(() => { - auctionModule.newAuction.restore(); - adaptermanager.callBids.restore(); - }); + it('bidders that do not support one of the declared formats are dropped', () => { + delete adUnits[0].mediaTypes.banner; - it('should only request native bidders on native adunits', () => { - // appnexus is a native bidder, appnexus is not $$PREBID_GLOBAL$$.requestBids({adUnits}); sinon.assert.calledOnce(adaptermanager.callBids); + const spyArgs = adaptermanager.callBids.getCall(0); const biddersCalled = spyArgs.args[0][0].bids; + + // only appnexus supports native expect(biddersCalled.length).to.equal(1); }); });