diff --git a/.github/stale.yml b/.github/stale.yml index 41b1c7ba7478..0925c69c7037 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -7,8 +7,9 @@ exemptLabels: - pinned - security - bug + - feature # Label to use when marking an issue as stale -staleLabel: wontfix +staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js index 222dfaa7ce73..f961db718c7e 100644 --- a/modules/a4gBidAdapter.js +++ b/modules/a4gBidAdapter.js @@ -24,7 +24,6 @@ export const spec = { buildRequests: function(validBidRequests) { let deliveryUrl = ''; - let bidId = ''; const idParams = []; const sizeParams = []; const zoneIds = []; @@ -33,10 +32,7 @@ export const spec = { if (!deliveryUrl && typeof bid.params.deliveryUrl === 'string') { deliveryUrl = bid.params.deliveryUrl; } - if (!bidId) { - bidId = bid.bidId; - } - idParams.push(bid.placementCode); + idParams.push(bid.bidId); sizeParams.push(bid.sizes.map(size => size.join(SIZE_SEPARATOR)).join(ARRAY_SIZE_SEPARATOR)); zoneIds.push(bid.params.zoneId); }); @@ -48,7 +44,6 @@ export const spec = { return { method: 'GET', url: deliveryUrl, - bidId: bidId, data: { [IFRAME_PARAM_NAME]: 0, [LOCATION_PARAM_NAME]: utils.getTopWindowUrl(), @@ -62,21 +57,22 @@ export const spec = { interpretResponse: function(serverResponses, request) { const bidResponses = []; utils._each(serverResponses.body, function(response) { - const bidResponse = { - requestId: request.bidId, - cpm: response.cpm, - width: response.width, - height: response.height, - creativeId: response.zoneid, - currency: A4G_CURRENCY, - netRevenue: true, - ttl: A4G_TTL, - ad: response.ad, - adId: response.id - }; - bidResponses.push(bidResponse); + if (response.cpm > 0) { + const bidResponse = { + requestId: response.id, + creativeId: response.id, + adId: response.id, + cpm: response.cpm, + width: response.width, + height: response.height, + currency: A4G_CURRENCY, + netRevenue: true, + ttl: A4G_TTL, + ad: response.ad + }; + bidResponses.push(bidResponse); + } }); - return bidResponses; } }; diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js new file mode 100644 index 000000000000..ed7da360e0b3 --- /dev/null +++ b/modules/adtelligentBidAdapter.js @@ -0,0 +1,199 @@ +import * as utils from 'src/utils'; +import {registerBidder} from 'src/adapters/bidderFactory'; +import {VIDEO, BANNER} from 'src/mediaTypes'; +import {Renderer} from 'src/Renderer'; +import findIndex from 'core-js/library/fn/array/find-index'; + +const URL = '//hb.adtelligent.com/auction/'; +const OUTSTREAM_SRC = '//player.adtelligent.com/outstream-unit/2.01/outstream.min.js'; +const BIDDER_CODE = 'adtelligent'; +const OUTSTREAM = 'outstream'; +const DISPLAY = 'display'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [VIDEO, BANNER], + isBidRequestValid: function (bid) { + return bid && bid.params && bid.params.aid; + }, + + /** + * Make a server request from the list of BidRequests + * @param bidRequests + * @param bidderRequest + */ + buildRequests: function (bidRequests, bidderRequest) { + return { + data: bidToTag(bidRequests), + bidderRequest, + method: 'GET', + url: URL + }; + }, + + /** + * Unpack the response from the server into a list of bids + * @param serverResponse + * @param bidderRequest + * @return {Bid[]} An array of bids which were nested inside the server + */ + interpretResponse: function (serverResponse, {bidderRequest}) { + serverResponse = serverResponse.body; + let bids = []; + + if (!utils.isArray(serverResponse)) { + return parseRTBResponse(serverResponse, bidderRequest); + } + + serverResponse.forEach(serverBidResponse => { + bids = utils.flatten(bids, parseRTBResponse(serverBidResponse, bidderRequest)); + }); + + return bids; + } +}; + +function parseRTBResponse(serverResponse, bidderRequest) { + const isInvalidValidResp = !serverResponse || !serverResponse.bids || !serverResponse.bids.length; + + let bids = []; + + if (isInvalidValidResp) { + let extMessage = serverResponse && serverResponse.ext && serverResponse.ext.message ? `: ${serverResponse.ext.message}` : ''; + let errorMessage = `in response for ${bidderRequest.bidderCode} adapter ${extMessage}`; + + utils.logError(errorMessage); + + return bids; + } + + serverResponse.bids.forEach(serverBid => { + const requestId = findIndex(bidderRequest.bids, (bidRequest) => { + return bidRequest.bidId === serverBid.requestId; + }); + + if (serverBid.cpm !== 0 && requestId !== -1) { + const bid = createBid(serverBid, getMediaType(bidderRequest.bids[requestId])); + + bids.push(bid); + } + }); + + return bids; +} + +function bidToTag(bidRequests) { + let tag = { + domain: utils.getTopWindowLocation().hostname + }; + + for (let i = 0, length = bidRequests.length; i < length; i++) { + Object.assign(tag, prepareRTBRequestParams(i, bidRequests[i])); + } + + return tag; +} + +/** + * Parse mediaType + * @param _index {number} + * @param bid {object} + * @returns {object} + */ +function prepareRTBRequestParams(_index, bid) { + const mediaType = utils.deepAccess(bid, 'mediaTypes.video') ? VIDEO : DISPLAY; + const index = !_index ? '' : `${_index + 1}`; + + return { + ['callbackId' + index]: bid.bidId, + ['aid' + index]: bid.params.aid, + ['ad_type' + index]: mediaType, + ['sizes' + index]: utils.parseSizesInput(bid.sizes).join() + }; +} + +/** + * Prepare all parameters for request + * @param bidderRequest {object} + * @returns {object} + */ +function getMediaType(bidderRequest) { + const videoMediaType = utils.deepAccess(bidderRequest, 'mediaTypes.video'); + const context = utils.deepAccess(bidderRequest, 'mediaTypes.video.context'); + + return !videoMediaType ? DISPLAY : context === OUTSTREAM ? OUTSTREAM : VIDEO; +} + +/** + * Configure new bid by response + * @param bidResponse {object} + * @param mediaType {Object} + * @returns {object} + */ +function createBid(bidResponse, mediaType) { + let bid = { + requestId: bidResponse.requestId, + creativeId: bidResponse.cmpId, + height: bidResponse.height, + currency: bidResponse.cur, + width: bidResponse.width, + cpm: bidResponse.cpm, + netRevenue: true, + mediaType, + ttl: 3600 + }; + + if (mediaType === DISPLAY) { + return Object.assign(bid, { + ad: bidResponse.ad + }); + } + + Object.assign(bid, { + vastUrl: bidResponse.vastUrl + }); + + if (mediaType === OUTSTREAM) { + Object.assign(bid, { + mediaType: 'video', + adResponse: bidResponse, + renderer: newRenderer(bidResponse.requestId) + }); + } + + return bid; +} + +/** + * Create Adtelligent renderer + * @param requestId + * @returns {*} + */ +function newRenderer(requestId) { + const renderer = Renderer.install({ + id: requestId, + url: OUTSTREAM_SRC, + loaded: false + }); + + renderer.setRender(outstreamRender); + + return renderer; +} + +/** + * Initialise Adtelligent outstream + * @param bid + */ +function outstreamRender(bid) { + bid.renderer.push(() => { + window.VOutstreamAPI.initOutstreams([{ + width: bid.width, + height: bid.height, + vastUrl: bid.vastUrl, + elId: bid.adUnitCode + }]); + }); +} + +registerBidder(spec); diff --git a/modules/adtelligentBidAdapter.md b/modules/adtelligentBidAdapter.md new file mode 100644 index 000000000000..2b48c9726c9b --- /dev/null +++ b/modules/adtelligentBidAdapter.md @@ -0,0 +1,65 @@ +# Overview + +**Module Name**: Adtelligent Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: support@adtelligent.com + +# Description + +Get access to multiple demand partners across Adtelligent Marketplace and maximize your yield with Adtelligent header bidding adapter. + +Adtelligent header bidding adapter connects with Adtelligent demand sources in order to fetch bids. +This adapter provides a solution for accessing Video demand and display demand + + +# Test Parameters +``` + var adUnits = [ + + // Video instream adUnit + { + code: 'div-test-div', + sizes: [[640, 480]], + mediaTypes: { + video: { + context: 'instream' + } + }, + bids: [{ + bidder: 'adtelligent', + params: { + aid: 331133 + } + }] + }, + + // Video outstream adUnit + { + code: 'outstream-test-div', + sizes: [[640, 480]], + mediaTypes: { + video: { + context: 'outstream' + } + }, + bids: [{ + bidder: 'adtelligent', + params: { + aid: 331133 + } + }] + }, + + // Banner adUnit + { + code: 'div-test-div', + sizes: [[300, 250]], + bids: [{ + bidder: 'adtelligent', + params: { + aid: 350975 + } + }] + } + ]; +``` diff --git a/modules/audienceNetworkBidAdapter.js b/modules/audienceNetworkBidAdapter.js index ed78f336102e..263edba878a2 100644 --- a/modules/audienceNetworkBidAdapter.js +++ b/modules/audienceNetworkBidAdapter.js @@ -4,7 +4,7 @@ import { registerBidder } from 'src/adapters/bidderFactory'; import { config } from 'src/config'; import { formatQS } from 'src/url'; -import { getTopWindowUrl } from 'src/utils'; +import { generateUUID, getTopWindowUrl, isSafariBrowser } from 'src/utils'; import findIndex from 'core-js/library/fn/array/find-index'; import includes from 'core-js/library/fn/array/includes'; @@ -15,6 +15,7 @@ const url = 'https://an.facebook.com/v2/placementbid.json'; const supportedMediaTypes = ['banner', 'video']; const netRevenue = true; const hb_bidder = 'fan'; +const pbv = '$prebid.version$'; /** * Does this bid request contain valid parameters? @@ -164,12 +165,16 @@ const buildRequests = bids => { adformats, testmode, pageurl, - sdk + sdk, + pbv }; const video = findIndex(adformats, isVideo); if (video !== -1) { [search.playerwidth, search.playerheight] = expandSize(sizes[video]); } + if (isSafariBrowser()) { + search.cb = generateUUID(); + } const data = formatQS(search); return [{ adformats, data, method, requestIds, sizes, url }]; diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index ec70342c9646..2a7dc0b35c35 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -15,7 +15,25 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - return bid && bid.params && !!bid.params.ChannelID; + let valid = false; + let typeOfCpmWeight; + + if (bid && bid.params) { + if (bid.params.ChannelID) { + // cpmWeight is optinal parameter and should above than zero + typeOfCpmWeight = typeof bid.params.cpmWeight; + if (typeOfCpmWeight === 'undefined') { + bid.params.cpmWeight = 1; + valid = true; + } else if (typeOfCpmWeight === 'number' && bid.params.cpmWeight > 0) { + valid = true; + } else { + valid = false; + } + } + } + + return valid; }, /** @@ -59,9 +77,14 @@ export const spec = { return; } + const anotherFormatSize = []; // for store width and height let matchedResponse = find(serverResponse.body, function(res) { return !!res && !res.consumed && find(req.sizes, function(size) { - return res.width === size[0] && res.height === size[1]; + let width = res.width; + let height = res.height; + if (typeof size === 'number') anotherFormatSize.push(size); // if sizes format is Array[Number], push width and height into anotherFormatSize + return (width === size[0] && height === size[1]) || // for format Array[Array[Number]] check + (width === anotherFormatSize[0] && height === anotherFormatSize[1]); // for foramt Array[Number] check }); }); @@ -82,7 +105,7 @@ export const spec = { } bidResponse.requestId = req.bidId; - bidResponse.cpm = matchedResponse.cpm; + bidResponse.cpm = matchedResponse.cpm * req.params.cpmWeight; bidResponse.width = matchedResponse.width; bidResponse.height = matchedResponse.height; bidResponse.ad = matchedResponse.ad; diff --git a/modules/bridgewellBidAdapter.md b/modules/bridgewellBidAdapter.md index b11ce21b0951..b9d065054fa6 100644 --- a/modules/bridgewellBidAdapter.md +++ b/modules/bridgewellBidAdapter.md @@ -18,7 +18,7 @@ Module that connects to Bridgewell demand source to fetch bids. { bidder: 'bridgewell', params: { - ChannelID: 'CgUxMjMzOBIBNiIFcGVubnkqCQisAhD6ARoBOQ', + ChannelID: 'CgUxMjMzOBIBNiIFcGVubnkqCQisAhD6ARoBOQ' } } ] @@ -30,6 +30,18 @@ Module that connects to Bridgewell demand source to fetch bids. bidder: 'bridgewell', params: { ChannelID: 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ', + cpmWeight: 1.5 + } + } + ] + },{ + code: 'test-div', + sizes: [728, 90], + bids: [ + { + bidder: 'bridgewell', + params: { + ChannelID: 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ' } } ] diff --git a/modules/contentigniteBidAdapter.js b/modules/contentigniteBidAdapter.js new file mode 100644 index 000000000000..423ec0ba8da4 --- /dev/null +++ b/modules/contentigniteBidAdapter.js @@ -0,0 +1,115 @@ +import { registerBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; +import * as utils from 'src/utils'; + +const BIDDER_CODE = 'contentignite'; + +export const spec = { + code: BIDDER_CODE, + pageID: Math.floor(Math.random() * 10e6), + + isBidRequestValid: (bid) => { + return !!(bid.params.accountID && bid.params.zoneID); + }, + + buildRequests: (validBidRequests) => { + let i; + let zoneID; + let bidRequest; + let accountID; + let keyword; + let requestURI; + const serverRequests = []; + const zoneCounters = {}; + + for (i = 0; i < validBidRequests.length; i++) { + bidRequest = validBidRequests[i]; + zoneID = utils.getBidIdParameter('zoneID', bidRequest.params); + accountID = utils.getBidIdParameter('accountID', bidRequest.params); + keyword = utils.getBidIdParameter('keyword', bidRequest.params); + + if (!(zoneID in zoneCounters)) { + zoneCounters[zoneID] = 0; + } + + requestURI = + location.protocol + '//serve.connectignite.com/adserve/;type=hbr;'; + requestURI += `ID=${encodeURIComponent(accountID)};`; + requestURI += `setID=${encodeURIComponent(zoneID)};`; + requestURI += `pid=${spec.pageID};`; + requestURI += `place=${encodeURIComponent(zoneCounters[zoneID])};`; + + // append the keyword for targeting if one was passed in + if (keyword !== '') { + requestURI += `kw=${encodeURIComponent(keyword)};`; + } + + zoneCounters[zoneID]++; + serverRequests.push({ + method: 'GET', + url: requestURI, + data: {}, + bidRequest: bidRequest + }); + } + return serverRequests; + }, + + // tslint:disable-next-line:cyclomatic-complexity + interpretResponse: (serverResponse, bidRequest) => { + const bidObj = bidRequest.bidRequest; + const bidResponses = []; + const bidResponse = {}; + let isCorrectSize = false; + let isCorrectCPM = true; + let cpm; + let minCPM; + let maxCPM; + let width; + let height; + + serverResponse = serverResponse.body; + if (serverResponse && serverResponse.status === 'SUCCESS' && bidObj) { + cpm = serverResponse.cpm; + minCPM = utils.getBidIdParameter('minCPM', bidObj.params); + maxCPM = utils.getBidIdParameter('maxCPM', bidObj.params); + width = parseInt(serverResponse.width); + height = parseInt(serverResponse.height); + + // Ensure response CPM is within the given bounds + if (minCPM !== '' && cpm < parseFloat(minCPM)) { + isCorrectCPM = false; + utils.logWarn('ContentIgnite: CPM did not meet minCPM requirements.'); + } else if (maxCPM !== '' && cpm > parseFloat(maxCPM)) { + isCorrectCPM = false; + utils.logWarn('ContentIgnite: CPM did not meet maxCPM requirements.'); + } + + // Ensure that response ad matches one of the placement sizes. + utils._each(bidObj.sizes, (size) => { + if (width === size[0] && height === size[1]) { + isCorrectSize = true; + } else { + utils.logWarn( + 'ContentIgnite: Returned ad is of a different size to that requested.' + ); + } + }); + if (isCorrectCPM && isCorrectSize) { + bidResponse.requestId = bidObj.bidId; + bidResponse.creativeId = serverResponse.placement_id; + bidResponse.cpm = cpm; + bidResponse.width = width; + bidResponse.height = height; + bidResponse.ad = serverResponse.ad_code; + bidResponse.currency = 'USD'; + bidResponse.netRevenue = true; + bidResponse.ttl = config.getConfig('_bidderTimeout'); + bidResponse.referrer = utils.getTopWindowUrl(); + bidResponses.push(bidResponse); + } + } + return bidResponses; + } +}; +registerBidder(spec); diff --git a/modules/contentigniteBidAdapter.md b/modules/contentigniteBidAdapter.md new file mode 100644 index 000000000000..1f3a543b6216 --- /dev/null +++ b/modules/contentigniteBidAdapter.md @@ -0,0 +1,30 @@ +# Overview + +``` +Module Name: Content Ignite Bidder Adapter +Module Type: Bidder Adapter +Maintainer: jamie@contentignite.com +``` + +# Description + +Module that connects to Content Ignites bidder application. + +# Test Parameters + +``` + var adUnits = [{ + code: 'display-div', + sizes: [[728, 90]], // a display size + bids: [{ + bidder: "contentignite", + params: { + accountID: '168237', + zoneID: '299680', + keyword: 'business', //optional + minCPM: '0.10', //optional + maxCPM: '1.00' //optional + } + }] + }]; +``` diff --git a/modules/currency.js b/modules/currency.js index aeeabc3817cb..90941538363a 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -5,7 +5,7 @@ import * as utils from 'src/utils'; import { config } from 'src/config'; import { hooks } from 'src/hook.js'; -const DEFAULT_CURRENCY_RATE_URL = 'http://currency.prebid.org/latest.json'; +const DEFAULT_CURRENCY_RATE_URL = 'https://currency.prebid.org/latest.json'; const CURRENCY_RATE_PRECISION = 4; var bidResponseQueue = []; diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 609e9f46234d..d029c6d07a06 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -88,6 +88,8 @@ export default function buildDfpVideoUrl(options) { const customParams = Object.assign({}, adserverTargeting, { hb_uuid: bid && bid.videoCacheKey }, + // hb_uuid will be deprecated and replaced by hb_cache_id + {hb_cache_id: bid && bid.videoCacheKey}, options.params.cust_params); const queryParams = Object.assign({}, diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js new file mode 100644 index 000000000000..63a308f1f623 --- /dev/null +++ b/modules/ebdrBidAdapter.js @@ -0,0 +1,133 @@ +import * as utils from 'src/utils'; +import { VIDEO, BANNER } from 'src/mediaTypes'; +import { registerBidder } from 'src/adapters/bidderFactory'; +const BIDDER_CODE = 'ebdr'; +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [ BANNER, VIDEO ], + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.zoneid); + }, + buildRequests: function(bids) { + const rtbServerDomain = 'dsp.bnmla.com'; + let domain = window.location.host; + let page = window.location.pathname + location.search + location.hash; + let ebdrImps = []; + const ebdrReq = {}; + let ebdrParams = {}; + let zoneid = ''; + let requestId = ''; + bids.forEach(bid => { + utils.logInfo('Log bid', bid); + let bidFloor = utils.getBidIdParameter('bidfloor', bid.params); + let whArr = getWidthAndHeight(bid); + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video) ? VIDEO : BANNER; + zoneid = utils.getBidIdParameter('zoneid', bid.params); + requestId = bid.bidderRequestId; + ebdrImps.push({ + id: bid.bidId, + [_mediaTypes]: { + w: whArr[0], + h: whArr[1] + }, + bidfloor: bidFloor + }) + ebdrReq[bid.bidId] = {mediaTypes: _mediaTypes, + w: whArr[0], + h: whArr[1] + }; + ebdrParams['latitude'] = utils.getBidIdParameter('latitude', bid.params); + ebdrParams['longitude'] = utils.getBidIdParameter('longitude', bid.params); + ebdrParams['ifa'] = (utils.getBidIdParameter('IDFA', bid.params).length > utils.getBidIdParameter('ADID', bid.params).length) ? utils.getBidIdParameter('IDFA', bid.params) : utils.getBidIdParameter('ADID', bid.params); + }); + let ebdrBidReq = { + id: requestId, + imp: ebdrImps, + site: { + domain: domain, + page: page + }, + device: { + geo: { + lat: ebdrParams.latitude, + log: ebdrParams.longitude + }, + ifa: ebdrParams.ifa + } + }; + return { + method: 'GET', + url: '//' + rtbServerDomain + '/hb?' + '&zoneid=' + zoneid + '&br=' + encodeURIComponent(JSON.stringify(ebdrBidReq)), + bids: ebdrReq + }; + }, + interpretResponse: function(serverResponse, bidRequest) { + utils.logInfo('Log serverResponse', serverResponse); + utils.logInfo('Log bidRequest', bidRequest); + let ebdrResponseImps = []; + const ebdrResponseObj = serverResponse.body; + if (!ebdrResponseObj || !ebdrResponseObj.seatbid || ebdrResponseObj.seatbid.length === 0 || !ebdrResponseObj.seatbid[0].bid || ebdrResponseObj.seatbid[0].bid.length === 0) { + return []; + } + ebdrResponseObj.seatbid[0].bid.forEach(ebdrBid => { + let responseCPM; + responseCPM = parseFloat(ebdrBid.price); + let adm; + let type; + let _mediaTypes; + let vastURL; + if (bidRequest.bids[ebdrBid.id].mediaTypes == BANNER) { + adm = decodeURIComponent(ebdrBid.adm) + type = 'ad'; + _mediaTypes = BANNER; + } else { + adm = ebdrBid.adm + type = 'vastXml' + _mediaTypes = VIDEO; + if (ebdrBid.nurl) { + vastURL = ebdrBid.nurl; + } + } + let response = { + requestId: ebdrBid.id, + [type]: adm, + mediaType: _mediaTypes, + creativeId: ebdrBid.crid, + cpm: responseCPM, + width: ebdrBid.w, + height: ebdrBid.h, + currency: 'USD', + netRevenue: true, + ttl: 3600 } + if (vastURL) { + response.vastUrl = vastURL; + } + ebdrResponseImps.push(response); + }); + return ebdrResponseImps; + } +} +function getWidthAndHeight(bid) { + let adW = null; + let adH = null; + // Handing old bidder only has size object + if (bid.sizes && bid.sizes.length) { + let sizeArrayLength = bid.sizes.length; + if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') { + adW = bid.sizes[0]; + adH = bid.sizes[1]; + } + } + let _mediaTypes = bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER; + if (bid.mediaTypes && bid.mediaTypes[_mediaTypes]) { + if (_mediaTypes == BANNER && bid.mediaTypes[_mediaTypes].sizes && bid.mediaTypes[_mediaTypes].sizes[0] && bid.mediaTypes[_mediaTypes].sizes[0].length === 2) { + adW = bid.mediaTypes[_mediaTypes].sizes[0][0]; + adH = bid.mediaTypes[_mediaTypes].sizes[0][1]; + } else if (_mediaTypes == VIDEO && bid.mediaTypes[_mediaTypes].playerSize && bid.mediaTypes[_mediaTypes].playerSize.length === 2) { + adW = bid.mediaTypes[_mediaTypes].playerSize[0]; + adH = bid.mediaTypes[_mediaTypes].playerSize[1]; + } + } + return [adW, adH]; +} +registerBidder(spec); diff --git a/modules/ebdrBidAdapter.md b/modules/ebdrBidAdapter.md new file mode 100644 index 000000000000..64483797b25f --- /dev/null +++ b/modules/ebdrBidAdapter.md @@ -0,0 +1,53 @@ +# Overview + +``` +Module Name: EngageBDR Bid Adapter +Module Type: Bidder Adapter +Maintainer: tech@engagebdr.com +``` + +# Description + +Adapter that connects to EngageBDR's demand sources. + +# Test Parameters +``` + var adUnits = [{ + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]], + } + }, + bids: [{ + bidder: 'ebdr', + params: { + zoneid: '99999', + bidfloor: '1.00', + IDFA:'xxx-xxx', + ADID:'xxx-xxx', + latitude:'34.089811', + longitude:'-118.392805' + } + }] + },{ + code: 'test-video', + mediaTypes: { + video: { + context: 'instream', + playerSize: [300, 250] + } + }, + bids: [{ + bidder: 'ebdr', + params: { + zoneid: '99998', + bidfloor: '1.00', + IDFA:'xxx-xxx', + ADID:'xxx-xxx', + latitude:'34.089811', + longitude:'-118.392805' + } + }] + }]; +``` diff --git a/modules/eplanningAnalyticsAdapter.js b/modules/eplanningAnalyticsAdapter.js new file mode 100644 index 000000000000..2fe26488ebe5 --- /dev/null +++ b/modules/eplanningAnalyticsAdapter.js @@ -0,0 +1,131 @@ +import {ajax} from 'src/ajax'; +import adapter from 'src/AnalyticsAdapter'; +import adaptermanager from 'src/adaptermanager'; +import * as utils from 'src/utils'; + +const CONSTANTS = require('src/constants.json'); + +const analyticsType = 'endpoint'; +const EPL_HOST = 'https://ads.us.e-planning.net/hba/1/'; + +function auctionEndHandler(args) { + return {auctionId: args.auctionId}; +} + +function auctionInitHandler(args) { + return { + auctionId: args.auctionId, + time: args.timestamp + }; +} + +function bidRequestedHandler(args) { + return { + auctionId: args.auctionId, + time: args.start, + bidder: args.bidderCode, + bids: args.bids.map(function(bid) { + return { + time: bid.startTime, + bidder: bid.bidder, + placementCode: bid.placementCode, + auctionId: bid.auctionId, + sizes: bid.sizes + }; + }), + }; +} + +function bidResponseHandler(args) { + return { + bidder: args.bidder, + size: args.size, + auctionId: args.auctionId, + cpm: args.cpm, + time: args.responseTimestamp, + }; +} + +function bidWonHandler(args) { + return { + auctionId: args.auctionId, + size: args.width + 'x' + args.height, + }; +} + +function bidTimeoutHandler(args) { + return args.map(function(bid) { + return { + bidder: bid.bidder, + auctionId: bid.auctionId + }; + }) +} + +function callHandler(evtype, args) { + let handler = null; + + if (evtype === CONSTANTS.EVENTS.AUCTION_INIT) { + handler = auctionInitHandler; + eplAnalyticsAdapter.context.events = []; + } else if (evtype === CONSTANTS.EVENTS.AUCTION_END) { + handler = auctionEndHandler; + } else if (evtype === CONSTANTS.EVENTS.BID_REQUESTED) { + handler = bidRequestedHandler; + } else if (evtype === CONSTANTS.EVENTS.BID_RESPONSE) { + handler = bidResponseHandler + } else if (evtype === CONSTANTS.EVENTS.BID_TIMEOUT) { + handler = bidTimeoutHandler; + } else if (evtype === CONSTANTS.EVENTS.BID_WON) { + handler = bidWonHandler; + } + + if (handler) { + eplAnalyticsAdapter.context.events.push({ec: evtype, p: handler(args)}); + } +} + +var eplAnalyticsAdapter = Object.assign(adapter( + { + EPL_HOST, + analyticsType + }), +{ + track({eventType, args}) { + if (typeof args !== 'undefined') { + callHandler(eventType, args); + } + + if (eventType === CONSTANTS.EVENTS.AUCTION_END) { + try { + let strjson = JSON.stringify(eplAnalyticsAdapter.context.events); + ajax(eplAnalyticsAdapter.context.host + eplAnalyticsAdapter.context.ci + '?d=' + encodeURIComponent(strjson)); + } catch (err) {} + } + } +} +); + +eplAnalyticsAdapter.originEnableAnalytics = eplAnalyticsAdapter.enableAnalytics; + +eplAnalyticsAdapter.enableAnalytics = function (config) { + if (!config.options.ci) { + utils.logError('Client ID (ci) option is not defined. Analytics won\'t work'); + return; + } + + eplAnalyticsAdapter.context = { + events: [], + host: config.options.host || EPL_HOST, + ci: config.options.ci + }; + + eplAnalyticsAdapter.originEnableAnalytics(config); +}; + +adaptermanager.registerAnalyticsAdapter({ + adapter: eplAnalyticsAdapter, + code: 'eplanning' +}); + +export default eplAnalyticsAdapter; diff --git a/modules/eplanningAnalyticsAdapter.md b/modules/eplanningAnalyticsAdapter.md new file mode 100644 index 000000000000..3127b523be03 --- /dev/null +++ b/modules/eplanningAnalyticsAdapter.md @@ -0,0 +1,23 @@ +# Overview + +``` +Module Name: E-Planning Analytics Adapter +Module Type: Analytics Adapter +Maintainer: mmartinho@e-planning.net +``` + +# Description + +Analytics adapter for E-Planning. + +# Test Parameters + +``` +{ + provider: 'eplanning', + options : { + host: 'https://ads.us.e-planning.net/hba/1/', // Host (optional) + ci: "123456" // Client ID (required) + } +} +``` diff --git a/modules/gammaBidAdapter.js b/modules/gammaBidAdapter.js index 516f716fa76b..1fba73fd6ab9 100644 --- a/modules/gammaBidAdapter.js +++ b/modules/gammaBidAdapter.js @@ -8,6 +8,7 @@ const BIDDER_CODE = 'gamma'; export const spec = { code: BIDDER_CODE, aliases: ['gamma'], + supportedMediaTypes: ['banner', 'video'], /** * Determines whether or not the given bid request is valid. @@ -85,6 +86,7 @@ function newBid(serverBid) { if (serverBid.type == 'video') { Object.assign(bid, { vastXml: serverBid.seatbid[0].bid[0].vastXml, + vastUrl: serverBid.seatbid[0].bid[0].vastUrl, ttl: 3600 }); } diff --git a/modules/gammaBidAdapter.md b/modules/gammaBidAdapter.md index 83c1fdf48535..2902be784928 100644 --- a/modules/gammaBidAdapter.md +++ b/modules/gammaBidAdapter.md @@ -12,7 +12,7 @@ Connects to Gamma exchange for bids. Gamma bid adapter supports Banner, Video. -# Test Parameters +# Test Parameters: For Banner ``` var adUnits = [{ code: 'gamma-hb-ad-123456-0', @@ -29,6 +29,23 @@ var adUnits = [{ }]; ``` +# Test Parameters: For Video +``` +var adUnits = [{ + code: 'gamma-hb-ad-78910-0', + sizes: [[640, 480]], + + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'gamma', + params: { + siteId: '1465446377', + zoneId: '1493280341' + } + }] + + }]; +``` # Ad Unit and Setup: For Testing In order to receive bids please map localhost to (any) test domain. diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index c0b3fff3c2aa..e8a98721e0cc 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -1,9 +1,12 @@ import { registerBidder } from 'src/adapters/bidderFactory'; import * as utils from 'src/utils'; +import { config } from 'src/config'; const BIDDER_CODE = 'medianet'; const BID_URL = 'https://prebid.media.net/rtb/prebid'; +$$PREBID_GLOBAL$$.medianetGlobals = {}; + function siteDetails(site) { site = site || {}; @@ -35,7 +38,8 @@ function getSize(size) { function configuredParams(params) { return { - customer_id: params.cid + customer_id: params.cid, + prebid_version: $$PREBID_GLOBAL$$.version } } @@ -46,7 +50,8 @@ function slotParams(bidRequest) { ext: { dfp_id: bidRequest.adUnitCode }, - banner: transformSizes(bidRequest.sizes) + banner: transformSizes(bidRequest.sizes), + all: bidRequest.params }; if (bidRequest.params.crid) { @@ -65,7 +70,8 @@ function generatePayload(bidRequests) { site: siteDetails(bidRequests[0].params.site), ext: configuredParams(bidRequests[0].params), id: bidRequests[0].auctionId, - imp: bidRequests.map(request => slotParams(request)) + imp: bidRequests.map(request => slotParams(request)), + tmax: config.getConfig('bidderTimeout') } } @@ -103,6 +109,8 @@ export const spec = { return false; } + Object.assign($$PREBID_GLOBAL$$.medianetGlobals, !$$PREBID_GLOBAL$$.medianetGlobals.cid && {cid: bid.params.cid}); + return true; }, diff --git a/modules/oneplanetonlyBidAdapter.js b/modules/oneplanetonlyBidAdapter.js new file mode 100644 index 000000000000..a6a3257a28bf --- /dev/null +++ b/modules/oneplanetonlyBidAdapter.js @@ -0,0 +1,76 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; + +const BIDDER_CODE = 'oneplanetonly'; +const EDNPOINT = '//show.oneplanetonly.com/prebid'; + +function createEndpoint(siteId) { + return `${EDNPOINT}?siteId=${siteId}`; +} + +function isBidRequestValid (bid) { + return !!(bid.params.siteId && bid.params.adUnitId); +} + +function buildRequests(bidReqs) { + let firstBid = bidReqs[0] || {} + let siteId = utils.getBidIdParameter('siteId', firstBid.params) + let adUnits = bidReqs.map((bid) => { + return { + id: utils.getBidIdParameter('adUnitId', bid.params), + bidId: bid.bidId, + sizes: utils.parseSizesInput(bid.sizes), + }; + }); + + const bidRequest = { + id: firstBid.auctionId, + ver: 1, + prebidVer: `$prebid.version$`, + transactionId: firstBid.transactionId, + currency: config.getConfig('currency.adServerCurrency'), + timeout: config.getConfig('bidderTimeout'), + siteId, + domain: utils.getTopWindowLocation().hostname, + page: config.getConfig('pageUrl') || utils.getTopWindowUrl(), + referrer: utils.getTopWindowReferrer(), + adUnits, + }; + + return { + method: 'POST', + url: createEndpoint(siteId), + data: bidRequest, + options: {contentType: 'application/json', withCredentials: true} + }; +} + +function interpretResponse(serverResponse, request) { + if (!serverResponse.body.bids) { + return []; + } + return serverResponse.body.bids.map((bid) => { + return { + requestId: bid.requestId, + cpm: bid.cpm, + width: bid.width, + height: bid.height, + creativeId: bid.creativeId, + currency: bid.currency, + netRevenue: true, + ad: bid.ad, + ttl: bid.ttl + }; + }); +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['opo'], // short code + isBidRequestValid, + buildRequests, + interpretResponse +} + +registerBidder(spec); diff --git a/modules/oneplanetonlyBidAdapter.md b/modules/oneplanetonlyBidAdapter.md new file mode 100644 index 000000000000..973adb33efd8 --- /dev/null +++ b/modules/oneplanetonlyBidAdapter.md @@ -0,0 +1,50 @@ +# Overview + +``` +Module Name: OnePlanetOnly Bidder Adapter +Module Type: Bidder Adapter +Maintainer: vitaly@oneplanetonly.com +``` + +# Description + +Module that connects to OnePlanetOnly's demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: 'desktop-banner-ad-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bids: [ + { + bidder: 'oneplanetonly', + params: { + siteId: '5', + adUnitId: '5-4587544' + } + } + ] + },{ + code: 'mobile-banner-ad-div', + mediaTypes: { + banner: { + sizes: [[320, 50], [320, 100]], + } + }, + bids: [ + { + bidder: "oneplanetonly", + params: { + siteId: '5', + adUnitId: '5-81037880' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/prebidServerBidAdapter.js b/modules/prebidServerBidAdapter.js index 80157f323443..ebb630ceb7a6 100644 --- a/modules/prebidServerBidAdapter.js +++ b/modules/prebidServerBidAdapter.js @@ -213,7 +213,10 @@ const paramTypes = { function convertTypes(adUnits) { adUnits.forEach(adUnit => { adUnit.bids.forEach(bid => { - const types = paramTypes[bid.bidder] || []; + // aliases use the base bidder's paramTypes + const bidder = adaptermanager.aliasRegistry[bid.bidder] || bid.bidder; + const types = paramTypes[bidder] || []; + Object.keys(types).forEach(key => { if (bid.params[key]) { bid.params[key] = types[key](bid.params[key]); @@ -288,7 +291,7 @@ const LEGACY_PROTOCOL = { max_bids: _s2sConfig.maxBids, timeout_millis: _s2sConfig.timeout, secure: _s2sConfig.secure, - cache_markup: _s2sConfig.cacheMarkup, + cache_markup: _s2sConfig.cacheMarkup === 1 || _s2sConfig.cacheMarkup === 2 ? _s2sConfig.cacheMarkup : 0, url: utils.getTopWindowUrl(), prebid_version: '$prebid.version$', ad_units: adUnits, @@ -351,6 +354,11 @@ const LEGACY_PROTOCOL = { if (bidObj.nurl) { bidObject.vastUrl = bidObj.nurl; } + // when video bid is already cached by Prebid Server, videoCacheKey and vastUrl should be provided properly + if (bidObj.cache_id && bidObj.cache_url) { + bidObject.videoCacheKey = bidObj.cache_id; + bidObject.vastUrl = bidObj.cache_url; + } } else { if (bidObj.adm && bidObj.nurl) { bidObject.ad = bidObj.adm; @@ -397,14 +405,20 @@ const OPEN_RTB_PROTOCOL = { buildRequest(s2sBidRequest, adUnits) { let imps = []; + let aliases = {}; // transform ad unit into array of OpenRTB impression objects adUnits.forEach(adUnit => { - // OpenRTB response contains the adunit code and bidder name. These are - // combined to create a unique key for each bid since an id isn't returned adUnit.bids.forEach(bid => { + // OpenRTB response contains the adunit code and bidder name. These are + // combined to create a unique key for each bid since an id isn't returned const key = `${adUnit.code}${bid.bidder}`; this.bidMap[key] = bid; + + // check for and store valid aliases to add to the request + if (adaptermanager.aliasRegistry[bid.bidder]) { + aliases[bid.bidder] = adaptermanager.aliasRegistry[bid.bidder]; + } }); let banner; @@ -464,6 +478,10 @@ const OPEN_RTB_PROTOCOL = { request.user = { ext: { digitrust: digiTrust } }; } + if (!utils.isEmpty(aliases)) { + request.ext = { prebid: { aliases } }; + } + return request; }, diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 625d34b06482..b60755f24213 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1,6 +1,7 @@ import * as utils from 'src/utils'; import { registerBidder } from 'src/adapters/bidderFactory'; import { config } from 'src/config'; +import { BANNER, VIDEO } from 'src/mediaTypes'; const INTEGRATION = 'pbjs_lite_v$prebid.version$'; @@ -73,7 +74,7 @@ utils._each(sizeMap, (item, key) => sizeMap[item] = key); export const spec = { code: 'rubicon', aliases: ['rubiconLite'], - supportedMediaTypes: ['banner', 'video'], + supportedMediaTypes: [BANNER, VIDEO], /** * @param {object} bid * @return boolean @@ -93,8 +94,9 @@ export const spec = { return false; } - if (bid.mediaType === 'video') { - if (typeof params.video !== 'object' || !params.video.size_id) { + if (spec.hasVideoMediaType(bid)) { + // support instream only + if ((utils.deepAccess(bid, `mediaTypes.${VIDEO}`) && utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) !== 'instream') || typeof params.video !== 'object' || !params.video.size_id) { return false; } } @@ -118,7 +120,7 @@ export const spec = { page_url = bidRequest.params.secure ? page_url.replace(/^http:/i, 'https:') : page_url; - if (bidRequest.mediaType === 'video') { + if (spec.hasVideoMediaType(bidRequest)) { let params = bidRequest.params; let size = parseSizes(bidRequest); @@ -237,6 +239,15 @@ export const spec = { }; }); }, + /** + * Test if bid has mediaType or mediaTypes set for video. + * note: 'mediaType' has been deprecated, however support will remain for a transitional period + * @param {BidRequest} bidRequest + * @returns {boolean} + */ + hasVideoMediaType: function(bidRequest) { + return bidRequest.mediaType === VIDEO || typeof utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`) !== 'undefined'; + }, /** * @param {*} responseObj * @param {BidRequest} bidRequest @@ -252,7 +263,7 @@ export const spec = { } // video ads array is wrapped in an object - if (typeof bidRequest === 'object' && bidRequest.mediaType === 'video' && typeof ads === 'object') { + if (typeof bidRequest === 'object' && spec.hasVideoMediaType(bidRequest) && typeof ads === 'object') { ads = ads[bidRequest.adUnitCode]; } @@ -287,7 +298,7 @@ export const spec = { bid.mediaType = ad.creative_type; } - if (bidRequest.mediaType === 'video') { + if (ad.creative_type === VIDEO) { bid.width = bidRequest.params.video.playerWidth; bid.height = bidRequest.params.video.playerHeight; bid.vastUrl = ad.creative_depot_url; @@ -360,17 +371,14 @@ function _renderCreative(script, impId) { function parseSizes(bid) { let params = bid.params; - if (bid.mediaType === 'video') { + if (spec.hasVideoMediaType(bid)) { let size = []; - if (params.video.playerWidth && params.video.playerHeight) { + if (typeof params.video === 'object' && params.video.playerWidth && params.video.playerHeight) { size = [ params.video.playerWidth, params.video.playerHeight ]; - } else if ( - Array.isArray(bid.sizes) && bid.sizes.length > 0 && - Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1 - ) { + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0 && Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1) { size = bid.sizes[0]; } return size; diff --git a/modules/serverbidBidAdapter.js b/modules/serverbidBidAdapter.js index 24bb5aa6255a..8497a67f4015 100644 --- a/modules/serverbidBidAdapter.js +++ b/modules/serverbidBidAdapter.js @@ -21,6 +21,9 @@ const CONFIG = { }, 'automatad': { 'BASE_URI': 'https://e.serverbid.com/api/v2' + }, + 'archon': { + 'BASE_URI': 'https://e.serverbid.com/api/v2' } }; @@ -29,7 +32,7 @@ let bidder = 'serverbid'; export const spec = { code: BIDDER_CODE, - aliases: ['connectad', 'onefiftytwo', 'insticator', 'adsparc', 'automatad'], + aliases: ['connectad', 'onefiftytwo', 'insticator', 'adsparc', 'automatad', 'archon'], /** * Determines whether or not the given bid request is valid. diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 4828f3a36a85..3d2eaa4de83f 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -31,30 +31,31 @@ export const spec = { // to find which one the ad server needs // pull requested transaction ID from bidderRequest.bids[].transactionId - var bid = validBidRequests[0]; - const payload = { - siteid: bid.params.siteId, - pageid: bid.params.pageId, - formatid: bid.params.formatId, - currencyCode: config.getConfig('currency.adServerCurrency'), - bidfloor: bid.params.bidfloor || 0.0, - targeting: bid.params.target && bid.params.target != '' ? bid.params.target : undefined, - tagId: bid.adUnitCode, - sizes: bid.sizes.map(size => ({ - w: size[0], - h: size[1] - })), - pageDomain: utils.getTopWindowUrl(), - transactionId: bid.transactionId, - timeout: config.getConfig('bidderTimeout'), - bidId: bid.bidId - }; - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: bid.params.domain + '/prebid/v1', - data: payloadString, - }; + return validBidRequests.map(bid => { + var payload = { + siteid: bid.params.siteId, + pageid: bid.params.pageId, + formatid: bid.params.formatId, + currencyCode: config.getConfig('currency.adServerCurrency'), + bidfloor: bid.params.bidfloor || 0.0, + targeting: bid.params.target && bid.params.target != '' ? bid.params.target : undefined, + tagId: bid.adUnitCode, + sizes: bid.sizes.map(size => ({ + w: size[0], + h: size[1] + })), + pageDomain: utils.getTopWindowUrl(), + transactionId: bid.transactionId, + timeout: config.getConfig('bidderTimeout'), + bidId: bid.bidId + }; + var payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: bid.params.domain + '/prebid/v1', + data: payloadString, + }; + }); }, /** * Unpack the response from the server into a list of bids. diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index d13331b91796..564dca856904 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -22,6 +22,7 @@ export const spec = { * @return object of parameters for Prebid AJAX request */ buildRequests: function(bidReqs) { + const loc = utils.getTopWindowLocation(); let sovrnImps = []; let iv; utils._each(bidReqs, function (bid) { @@ -29,7 +30,7 @@ export const spec = { sovrnImps.push({ id: bid.bidId, banner: { w: 1, h: 1 }, - tagid: utils.getBidIdParameter('tagid', bid.params), + tagid: String(utils.getBidIdParameter('tagid', bid.params)), bidfloor: utils.getBidIdParameter('bidfloor', bid.params) }); }); @@ -37,8 +38,8 @@ export const spec = { id: utils.getUniqueIdentifierStr(), imp: sovrnImps, site: { - domain: window.location.host, - page: window.location.host + window.location.pathname + location.search + location.hash + domain: loc.host, + page: loc.host + loc.pathname + loc.search + loc.hash } }; if (iv) sovrnBidReq.iv = iv; diff --git a/modules/vertamediaBidAdapter.md b/modules/vertamediaBidAdapter.md index 2f6faf5c840e..6b1265fa7922 100644 --- a/modules/vertamediaBidAdapter.md +++ b/modules/vertamediaBidAdapter.md @@ -28,7 +28,7 @@ This adapter provides a solution for accessing Video demand and display demand bids: [{ bidder: 'vertamedia', params: { - aid: 332842 + aid: 331133 } }] }, @@ -45,7 +45,7 @@ This adapter provides a solution for accessing Video demand and display demand bids: [{ bidder: 'vertamedia', params: { - aid: 332842 + aid: 331133 } }] }, @@ -57,7 +57,7 @@ This adapter provides a solution for accessing Video demand and display demand bids: [{ bidder: 'vertamedia', params: { - aid: 324758 + aid: 350975 } }] } diff --git a/modules/yieldbotBidAdapter.js b/modules/yieldbotBidAdapter.js index bf21613e31ff..43d942209078 100644 --- a/modules/yieldbotBidAdapter.js +++ b/modules/yieldbotBidAdapter.js @@ -369,7 +369,7 @@ export const YieldbotAdapter = { const adUrl = this.buildAdUrl(urlPrefix, publisherNumber, commonSearchParams, bid); const impressionUrl = this.buildImpressionUrl(urlPrefix, publisherNumber, commonSearchParams); - const htmlMarkup = `
`; + const htmlMarkup = `
`; return { ad: htmlMarkup, creativeId: ybotAdRequestId }; }, diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 17c205359dee..dde5c70077ee 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -5,7 +5,7 @@ import { VIDEO, BANNER } from 'src/mediaTypes' const ENDPOINT = 'https://ad.yieldlab.net' const BIDDER_CODE = 'yieldlab' -const BID_RESPONSE_TTL_SEC = 600 +const BID_RESPONSE_TTL_SEC = 300 const CURRENCY_CODE = 'EUR' export const spec = { @@ -13,7 +13,7 @@ export const spec = { supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { - if (bid && bid.params && bid.params.placementId && bid.params.adSize) { + if (bid && bid.params && bid.params.adslotId && bid.params.adSize) { return true } return false @@ -25,18 +25,18 @@ export const spec = { * @returns {{method: string, url: string}} */ buildRequests: function (validBidRequests) { - const placementIds = [] + const adslotIds = [] const timestamp = Date.now() utils._each(validBidRequests, function (bid) { - placementIds.push(bid.params.placementId) + adslotIds.push(bid.params.adslotId) }) - const placements = placementIds.join(',') + const adslots = adslotIds.join(',') return { method: 'GET', - url: `${ENDPOINT}/yp/${placements}?ts=${timestamp}&json=true`, + url: `${ENDPOINT}/yp/${adslots}?ts=${timestamp}&json=true`, validBidRequests: validBidRequests } }, @@ -56,7 +56,7 @@ export const spec = { } let matchedBid = find(serverResponse.body, function (bidResponse) { - return bidRequest.params.placementId == bidResponse.id + return bidRequest.params.adslotId == bidResponse.id }) if (matchedBid) { @@ -67,16 +67,16 @@ export const spec = { width: sizes[0], height: sizes[1], creativeId: '' + matchedBid.id, - dealId: matchedBid.did, + dealId: matchedBid.pid, currency: CURRENCY_CODE, - netRevenue: true, + netRevenue: false, ttl: BID_RESPONSE_TTL_SEC, referrer: '', - ad: `` + ad: `` } if (isVideo(bidRequest)) { bidResponse.mediaType = VIDEO - bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.accountId}/1x1?ts=${timestamp}` + bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/${sizes[0]}x${sizes[1]}?ts=${timestamp}` } bidResponses.push(bidResponse) diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index 0c9183aa4cde..0b11794ac8f8 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Yieldlab Bidder Adapter Module Type: Bidder Adapter -Maintainer: api@platform-lunar.com +Maintainer: solutions@yieldlab.de ``` # Description @@ -14,19 +14,19 @@ Module that connects to Yieldlab's demand sources ``` var adUnits = [ { - code: "test1", - sizes: [[800, 250]] + code: "banner", + sizes: [[728, 90]], bids: [{ bidder: "yieldlab", params: { - placement: "4206978", - accountId: "2358365", - adSize: "800x250" + adslotId: "5220336", + supplyId: "1381604", + adSize: "728x90" } }] }, { - code: "test2", - sizes: [[1, 1]], + code: "video", + sizes: [[640, 480]], mediaTypes: { video: { context: "instream" @@ -35,9 +35,9 @@ Module that connects to Yieldlab's demand sources bids: [{ bidder: "yieldlab", params: { - placementId: "4207034", - accountId: "2358365", - adSize: "1x1" + adslotId: "5220339", + supplyId: "1381604", + adSize: "640x480" } }] } diff --git a/package.json b/package.json index 663e89ea7e09..f742753c7ae4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "1.6.0-pre", + "version": "1.7.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { diff --git a/src/AnalyticsAdapter.js b/src/AnalyticsAdapter.js index 3a54e1c230e8..0d570f14eb01 100644 --- a/src/AnalyticsAdapter.js +++ b/src/AnalyticsAdapter.js @@ -13,6 +13,7 @@ const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; const BID_WON = CONSTANTS.EVENTS.BID_WON; const BID_ADJUSTMENT = CONSTANTS.EVENTS.BID_ADJUSTMENT; const SET_TARGETING = CONSTANTS.EVENTS.SET_TARGETING; +const AD_RENDER_FAILED = CONSTANTS.EVENTS.AD_RENDER_FAILED; const LIBRARY = 'library'; const ENDPOINT = 'endpoint'; @@ -105,6 +106,7 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler } [BID_ADJUSTMENT]: args => this.enqueue({ eventType: BID_ADJUSTMENT, args }), [SET_TARGETING]: args => this.enqueue({ eventType: SET_TARGETING, args }), [AUCTION_END]: args => this.enqueue({ eventType: AUCTION_END, args }), + [AD_RENDER_FAILED]: args => this.enqueue({ eventType: AD_RENDER_FAILED, args }), [AUCTION_INIT]: args => { args.config = config.options; // enableAnaltyics configuration object this.enqueue({ eventType: AUCTION_INIT, args }); diff --git a/src/adaptermanager.js b/src/adaptermanager.js index b4a874a0736f..6ff2387cca21 100644 --- a/src/adaptermanager.js +++ b/src/adaptermanager.js @@ -15,6 +15,7 @@ let s2sTestingModule; // store s2sTesting module if it's loaded var _bidderRegistry = {}; exports.bidderRegistry = _bidderRegistry; +exports.aliasRegistry = {}; let _s2sConfig = {}; config.getConfig('s2sConfig', config => { @@ -383,6 +384,7 @@ exports.aliasBidAdapter = function (bidderCode, alias) { } else { let spec = bidAdaptor.getSpec(); newAdapter = newBidder(Object.assign({}, spec, { code: alias })); + exports.aliasRegistry[alias] = bidderCode; } this.registerBidAdapter(newAdapter, alias, { supportedMediaTypes diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index b9930c3f3630..2497572cd100 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -139,6 +139,7 @@ export function registerBidder(spec) { putBidder(spec); if (Array.isArray(spec.aliases)) { spec.aliases.forEach(alias => { + adaptermanager.aliasRegistry[alias] = spec.code; putBidder(Object.assign({}, spec, { code: alias })); }); } diff --git a/src/constants.json b/src/constants.json index 9a2639ab83fa..ef382b153af3 100644 --- a/src/constants.json +++ b/src/constants.json @@ -33,7 +33,15 @@ "BID_WON": "bidWon", "SET_TARGETING": "setTargeting", "REQUEST_BIDS": "requestBids", - "ADD_AD_UNITS": "addAdUnits" + "ADD_AD_UNITS": "addAdUnits", + "AD_RENDER_FAILED" : "adRenderFailed" + }, + "AD_RENDER_FAILED_REASON" : { + "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocuemnt", + "NO_AD": "noAd", + "EXCEPTION": "exception", + "CANNOT_FIND_AD": "cannotFindAd", + "MISSING_DOC_OR_ADID": "missingDocOrAdid" }, "EVENT_ID_PATHS": { "bidWon": "adUnitCode" diff --git a/src/prebid.js b/src/prebid.js index 7fdce6c3cf7b..d5ab040e8881 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -23,7 +23,8 @@ const { triggerUserSyncs } = userSync; /* private variables */ const RENDERED = 'rendered'; -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; +const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; var eventValidators = { bidWon: checkDefinedPlacement @@ -196,6 +197,18 @@ $$PREBID_GLOBAL$$.setTargetingForAst = function() { events.emit(SET_TARGETING); }; +function emitAdRenderFail(reason, message, bid) { + const data = {}; + + data.reason = reason; + data.message = message; + if (bid) { + data.bid = bid; + } + + utils.logError(message); + events.emit(AD_RENDER_FAILED, data); +} /** * This function will render the ad (based on params) in the given iframe document passed through. * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously @@ -206,6 +219,7 @@ $$PREBID_GLOBAL$$.setTargetingForAst = function() { $$PREBID_GLOBAL$$.renderAd = function (doc, id) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.renderAd', arguments); utils.logMessage('Calling renderAd with adId :' + id); + if (doc && id) { try { // lookup ad by ad Id @@ -229,7 +243,8 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { if (renderer && renderer.url) { renderer.render(bid); } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { - utils.logError(`Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`); + const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; + emitAdRenderFail(PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid); } else if (ad) { doc.write(ad); doc.close(); @@ -247,16 +262,20 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { setRenderSize(doc, width, height); utils.callBurl(bid); } else { - utils.logError('Error trying to write ad. No ad for bid response id: ' + id); + const message = `Error trying to write ad. No ad for bid response id: ${id}`; + emitAdRenderFail(NO_AD, message, bid); } } else { - utils.logError('Error trying to write ad. Cannot find ad by given id : ' + id); + const message = `Error trying to write ad. Cannot find ad by given id : ${id}`; + emitAdRenderFail(CANNOT_FIND_AD, message); } } catch (e) { - utils.logError('Error trying to write ad Id :' + id + ' to the page:' + e.message); + const message = `Error trying to write ad Id :${id} to the page:${e.message}`; + emitAdRenderFail(EXCEPTION, message); } } else { - utils.logError('Error trying to write ad Id :' + id + ' to the page. Missing document or adId'); + const message = `Error trying to write ad Id :${id} to the page. Missing document or adId`; + emitAdRenderFail(MISSING_DOC_OR_ADID, message); } }; diff --git a/src/url.js b/src/url.js index 502245f3abd3..c63bca2ca416 100644 --- a/src/url.js +++ b/src/url.js @@ -31,12 +31,15 @@ export function parse(url, options) { } else { parsed.href = decodeURIComponent(url); } + // in window.location 'search' is string, not object + let qsAsString = (options && 'decodeSearchAsString' in options && options.decodeSearchAsString); return { + href: parsed.href, protocol: (parsed.protocol || '').replace(/:$/, ''), hostname: parsed.hostname, port: +parsed.port, pathname: parsed.pathname.replace(/^(?!\/)/, '/'), - search: parseQS(parsed.search || ''), + search: (qsAsString) ? parsed.search : parseQS(parsed.search || ''), hash: (parsed.hash || '').replace(/^#/, ''), host: parsed.host || window.location.host }; diff --git a/src/userSync.js b/src/userSync.js index 56641b5cb8e9..059d3ea62c12 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -146,7 +146,7 @@ export function newUserSync(userSyncDependencies) { return utils.logWarn(`Bidder is required for registering sync`); } if (Number(numAdapterBids[bidder]) >= usConfig.syncsPerBidder) { - return utils.logWarn(`Number of user syncs exceeded for "{$bidder}"`); + return utils.logWarn(`Number of user syncs exceeded for "${bidder}"`); } // All bidders are enabled by default. If specified only register for enabled bidders. let hasEnabledBidders = usConfig.enabledBidders && usConfig.enabledBidders.length; diff --git a/src/utils.js b/src/utils.js index 3738d0e7d52f..5436a4aa167b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,6 +2,7 @@ import { config } from './config'; import clone from 'just-clone'; import find from 'core-js/library/fn/array/find'; import includes from 'core-js/library/fn/array/includes'; +import { parse } from './url'; const CONSTANTS = require('./constants'); var _loggingChecked = false; @@ -157,17 +158,55 @@ export function parseGPTSingleSizeArray(singleSize) { } }; -exports.getTopWindowLocation = function () { - let location; +exports.getTopWindowLocation = function() { + if (exports.inIframe()) { + let loc; + try { + loc = exports.getAncestorOrigins() || exports.getTopFrameReferrer(); + } catch (e) { + logInfo('could not obtain top window location', e); + } + if (loc) return parse(loc, {'decodeSearchAsString': true}); + } + return exports.getWindowLocation(); +} + +exports.getTopFrameReferrer = function () { try { - // force an exception in x-domain enviornments. #1509 + // force an exception in x-domain environments. #1509 window.top.location.toString(); - location = window.top.location; + let referrerLoc = ''; + let currentWindow; + do { + currentWindow = currentWindow ? currentWindow.parent : window; + if (currentWindow.document && currentWindow.document.referrer) { + referrerLoc = currentWindow.document.referrer; + } + } + while (currentWindow !== window.top); + return referrerLoc; } catch (e) { - location = window.location; + return window.document.referrer; + } +}; + +exports.getAncestorOrigins = function () { + if (window.document.location && window.document.location.ancestorOrigins && + window.document.location.ancestorOrigins.length >= 1) { + return window.document.location.ancestorOrigins[window.document.location.ancestorOrigins.length - 1]; } +}; + +exports.getWindowTop = function () { + return window.top; +}; + +exports.getWindowSelf = function () { + return window.self; +}; - return location; +exports.getWindowLocation = function () { + return window.location; }; exports.getTopWindowUrl = function () { @@ -658,7 +697,7 @@ export function deepClone(obj) { export function inIframe() { try { - return window.self !== window.top; + return exports.getWindowSelf() !== exports.getWindowTop(); } catch (e) { return true; } diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index 92710d475682..d6b227e9aac2 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -6,6 +6,8 @@ const BID_REQUESTED = CONSTANTS.EVENTS.BID_REQUESTED; const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; const BID_WON = CONSTANTS.EVENTS.BID_WON; const BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; +const AD_RENDER_FAILED = CONSTANTS.EVENTS.AD_RENDER_FAILED; + const AnalyticsAdapter = require('src/AnalyticsAdapter').default; const config = { url: 'http://localhost:9999/src/adapters/analytics/libraries/example.js', @@ -90,6 +92,17 @@ FEATURE: Analytics Adapters API assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); }); + it('SHOULD call global when a adRenderFailed event occurs', () => { + const eventType = AD_RENDER_FAILED; + const args = { call: 'adRenderFailed' }; + + adapter.enableAnalytics(); + events.emit(eventType, args); + + assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); + assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); + }); + it('SHOULD call global when a bidRequest event occurs', () => { const eventType = BID_REQUESTED; const args = { call: 'request' }; diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js index 861ab5c22815..30fcb2f8497e 100644 --- a/test/spec/modules/a4gBidAdapter_spec.js +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -109,15 +109,15 @@ describe('a4gAdapterTests', () => { let requiredKeys = [ 'requestId', + 'creativeId', + 'adId', 'cpm', 'width', 'height', - 'ad', - 'adId', - 'ttl', - 'creativeId', + 'currency', 'netRevenue', - 'currency' + 'ttl', + 'ad' ]; let resultKeys = Object.keys(result[0]); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js new file mode 100644 index 000000000000..fa3e04793725 --- /dev/null +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -0,0 +1,240 @@ +import {expect} from 'chai'; +import {spec} from 'modules/adtelligentBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//hb.adtelligent.com/auction/'; + +const DISPLAY_REQUEST = { + 'bidder': 'adtelligent', + 'params': { + 'aid': 12345 + }, + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '84ab500420319d', + 'sizes': [300, 250] +}; + +const VIDEO_REQUEST = { + 'bidder': 'adtelligent', + 'mediaTypes': { + 'video': {} + }, + 'params': { + 'aid': 12345 + }, + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '84ab500420319d', + 'sizes': [[480, 360], [640, 480]] +}; + +const SERVER_VIDEO_RESPONSE = { + 'source': {'aid': 12345, 'pubId': 54321}, + 'bids': [{ + 'vastUrl': 'http://rtb.adtelligent.com/vast/?adid=44F2AEB9BFC881B3', + 'requestId': '2e41f65424c87c', + 'url': '44F2AEB9BFC881B3', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 480, + 'cur': 'USD', + 'width': 640, + 'cpm': 0.9 + } + ] +}; +const SERVER_DISPLAY_RESPONSE = { + 'source': {'aid': 12345, 'pubId': 54321}, + 'bids': [{ + 'ad': '', + 'requestId': '2e41f65424c87c', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 250, + 'cur': 'USD', + 'width': 300, + 'cpm': 0.9 + }] +}; + +const videoBidderRequest = { + bidderCode: 'bidderCode', + bids: [{mediaTypes: {video: {}}, bidId: '2e41f65424c87c'}] +}; + +const displayBidderRequest = { + bidderCode: 'bidderCode', + bids: [{bidId: '2e41f65424c87c'}] +}; + +const videoEqResponse = [{ + vastUrl: 'http://rtb.adtelligent.com/vast/?adid=44F2AEB9BFC881B3', + requestId: '2e41f65424c87c', + creativeId: 342516, + mediaType: 'video', + netRevenue: true, + currency: 'USD', + height: 480, + width: 640, + ttl: 3600, + cpm: 0.9 +}]; + +const displayEqResponse = [{ + requestId: '2e41f65424c87c', + creativeId: 342516, + mediaType: 'display', + netRevenue: true, + currency: 'USD', + ad: '', + height: 250, + width: 300, + ttl: 3600, + cpm: 0.9 +}]; + +describe('adtelligentBidAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(VIDEO_REQUEST)).to.equal(12345); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, VIDEO_REQUEST); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(undefined); + }); + }); + + describe('buildRequests', () => { + let videoBidRequests = [VIDEO_REQUEST]; + let displayBidRequests = [DISPLAY_REQUEST]; + let videoAndDisplayBidRequests = [DISPLAY_REQUEST, VIDEO_REQUEST]; + + const displayRequest = spec.buildRequests(displayBidRequests, {}); + const videoRequest = spec.buildRequests(videoBidRequests, {}); + const videoAndDisplayRequests = spec.buildRequests(videoAndDisplayBidRequests, {}); + + it('sends bid request to ENDPOINT via GET', () => { + expect(videoRequest.method).to.equal('GET'); + expect(displayRequest.method).to.equal('GET'); + expect(videoAndDisplayRequests.method).to.equal('GET'); + }); + + it('sends bid request to correct ENDPOINT', () => { + expect(videoRequest.url).to.equal(ENDPOINT); + expect(displayRequest.url).to.equal(ENDPOINT); + expect(videoAndDisplayRequests.url).to.equal(ENDPOINT); + }); + + it('sends correct video bid parameters', () => { + const bid = Object.assign({}, videoRequest.data); + delete bid.domain; + + const eq = { + callbackId: '84ab500420319d', + ad_type: 'video', + aid: 12345, + sizes: '480x360,640x480' + }; + + expect(bid).to.deep.equal(eq); + }); + + it('sends correct display bid parameters', () => { + const bid = Object.assign({}, displayRequest.data); + delete bid.domain; + + const eq = { + callbackId: '84ab500420319d', + ad_type: 'display', + aid: 12345, + sizes: '300x250' + }; + + expect(bid).to.deep.equal(eq); + }); + + it('sends correct video and display bid parameters', () => { + const bid = Object.assign({}, videoAndDisplayRequests.data); + delete bid.domain; + + const eq = { + callbackId: '84ab500420319d', + ad_type: 'display', + aid: 12345, + sizes: '300x250', + callbackId2: '84ab500420319d', + ad_type2: 'video', + aid2: 12345, + sizes2: '480x360,640x480' + }; + + expect(bid).to.deep.equal(eq); + }); + }); + + describe('interpretResponse', () => { + let serverResponse; + let bidderRequest; + let eqResponse; + + afterEach(() => { + serverResponse = null; + bidderRequest = null; + eqResponse = null; + }); + + it('should get correct video bid response', () => { + serverResponse = SERVER_VIDEO_RESPONSE; + bidderRequest = videoBidderRequest; + eqResponse = videoEqResponse; + + bidServerResponseCheck(); + }); + + it('should get correct display bid response', () => { + serverResponse = SERVER_DISPLAY_RESPONSE; + bidderRequest = displayBidderRequest; + eqResponse = displayEqResponse; + + bidServerResponseCheck(); + }); + + function bidServerResponseCheck() { + const result = spec.interpretResponse({body: serverResponse}, {bidderRequest}); + + expect(result).to.deep.equal(eqResponse); + } + + function nobidServerResponseCheck() { + const noBidServerResponse = {bids: []}; + const noBidResult = spec.interpretResponse({body: noBidServerResponse}, {bidderRequest}); + + expect(noBidResult.length).to.equal(0); + } + + it('handles video nobid responses', () => { + bidderRequest = videoBidderRequest; + + nobidServerResponseCheck(); + }); + + it('handles display nobid responses', () => { + bidderRequest = displayBidderRequest; + + nobidServerResponseCheck(); + }); + }); +}); diff --git a/test/spec/modules/audienceNetworkBidAdapter_spec.js b/test/spec/modules/audienceNetworkBidAdapter_spec.js index d92597c913c1..41a6d955c6a9 100644 --- a/test/spec/modules/audienceNetworkBidAdapter_spec.js +++ b/test/spec/modules/audienceNetworkBidAdapter_spec.js @@ -4,6 +4,7 @@ import { expect } from 'chai'; import { spec } from 'modules/audienceNetworkBidAdapter'; +import * as utils from 'src/utils'; const { code, @@ -18,6 +19,7 @@ const placementId = 'test-placement-id'; const playerwidth = 320; const playerheight = 180; const requestId = 'test-request-id'; +const pbv = '$prebid.version$'; describe('AudienceNetwork adapter', () => { describe('Public API', () => { @@ -116,6 +118,15 @@ describe('AudienceNetwork adapter', () => { }); describe('buildRequests', () => { + let isSafariBrowserStub; + before(() => { + isSafariBrowserStub = sinon.stub(utils, 'isSafariBrowser'); + }); + + after(() => { + isSafariBrowserStub.restore(); + }); + it('can build URL for IAB unit', () => { expect(buildRequests([{ bidder, @@ -128,7 +139,7 @@ describe('AudienceNetwork adapter', () => { requestIds: [requestId], sizes: ['300x250'], url: 'https://an.facebook.com/v2/placementbid.json', - data: 'placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=&sdk[]=5.5.web' + data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=&sdk[]=5.5.web&pbv=${pbv}` }]); }); @@ -147,7 +158,7 @@ describe('AudienceNetwork adapter', () => { requestIds: [requestId], sizes: ['640x480'], url: 'https://an.facebook.com/v2/placementbid.json', - data: 'placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=&sdk[]=&playerwidth=640&playerheight=480' + data: `placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=&sdk[]=&pbv=${pbv}&playerwidth=640&playerheight=480` }]); }); @@ -166,7 +177,7 @@ describe('AudienceNetwork adapter', () => { requestIds: [requestId], sizes: ['728x90'], url: 'https://an.facebook.com/v2/placementbid.json', - data: 'placementids[]=test-placement-id&adformats[]=native&testmode=false&pageurl=&sdk[]=5.5.web' + data: `placementids[]=test-placement-id&adformats[]=native&testmode=false&pageurl=&sdk[]=5.5.web&pbv=${pbv}` }]); }); @@ -185,9 +196,19 @@ describe('AudienceNetwork adapter', () => { requestIds: [requestId], sizes: ['300x250'], url: 'https://an.facebook.com/v2/placementbid.json', - data: 'placementids[]=test-placement-id&adformats[]=fullwidth&testmode=false&pageurl=&sdk[]=5.5.web' + data: `placementids[]=test-placement-id&adformats[]=fullwidth&testmode=false&pageurl=&sdk[]=5.5.web&pbv=${pbv}` }]); }); + + it('can build URL on Safari that includes a cachebuster param', () => { + isSafariBrowserStub.returns(true); + expect(buildRequests([{ + bidder, + bidId: requestId, + sizes: [[300, 250]], + params: { placementId } + }])[0].data).to.contain('&cb='); + }); }); describe('interpretResponse', () => { diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 7670d992d0d6..6b95b44dfe58 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -37,6 +37,41 @@ describe('bridgewellBidAdapter', function () { 'bidId': '42dbe3a7168a6a', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CgUxMjMzOBIBNiIFcGVubnkqCQisAhD6ARoBOQ', + 'cpmWeight': 0.5 + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250]], + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ', + 'cpmWeight': -0.5 + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ', + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [728, 90], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', } ]; const adapter = newBidder(spec); @@ -48,7 +83,7 @@ describe('bridgewellBidAdapter', function () { }); describe('isBidRequestValid', () => { - let bid = { + let bidWithoutCpmWeight = { 'bidder': 'bridgewell', 'params': { 'ChannelID': 'CLJgEAYYvxUiBXBlbm55KgkIrAIQ-gEaATk' @@ -60,17 +95,83 @@ describe('bridgewellBidAdapter', function () { 'auctionId': '1d1a030790a475', }; + let bidWithCorrectCpmWeight = { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CLJgEAYYvxUiBXBlbm55KgkIrAIQ-gEaATk', + 'cpmWeight': 0.5 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + let bidWithUncorrectCpmWeight = { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CLJgEAYYvxUiBXBlbm55KgkIrAIQ-gEaATk', + 'cpmWeight': -1.0 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + let bidWithZeroCpmWeight = { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CLJgEAYYvxUiBXBlbm55KgkIrAIQ-gEaATk', + 'cpmWeight': 0 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(spec.isBidRequestValid(bidWithoutCpmWeight)).to.equal(true); + expect(spec.isBidRequestValid(bidWithCorrectCpmWeight)).to.equal(true); + expect(spec.isBidRequestValid(bidWithUncorrectCpmWeight)).to.equal(false); + expect(spec.isBidRequestValid(bidWithZeroCpmWeight)).to.equal(false); }); it('should return false when required params are not passed', () => { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { + let bidWithoutCpmWeight = Object.assign({}, bidWithoutCpmWeight); + let bidWithCorrectCpmWeight = Object.assign({}, bidWithCorrectCpmWeight); + let bidWithUncorrectCpmWeight = Object.assign({}, bidWithUncorrectCpmWeight); + let bidWithZeroCpmWeight = Object.assign({}, bidWithZeroCpmWeight); + + delete bidWithoutCpmWeight.params; + delete bidWithCorrectCpmWeight.params; + delete bidWithUncorrectCpmWeight.params; + delete bidWithZeroCpmWeight.params; + + bidWithoutCpmWeight.params = { + 'ChannelID': 0 + }; + + bidWithCorrectCpmWeight.params = { 'ChannelID': 0 }; - expect(spec.isBidRequestValid(bid)).to.equal(false); + + bidWithUncorrectCpmWeight.params = { + 'ChannelID': 0 + }; + + bidWithZeroCpmWeight.params = { + 'ChannelID': 0 + }; + + expect(spec.isBidRequestValid(bidWithoutCpmWeight)).to.equal(false); + expect(spec.isBidRequestValid(bidWithCorrectCpmWeight)).to.equal(false); + expect(spec.isBidRequestValid(bidWithUncorrectCpmWeight)).to.equal(false); + expect(spec.isBidRequestValid(bidWithZeroCpmWeight)).to.equal(false); }); }); @@ -129,6 +230,36 @@ describe('bridgewellBidAdapter', function () { 'ttl': 360, 'net_revenue': 'true', 'currency': 'NTD' + }, { + 'id': '8f12c646-3b87-4326-a837-c2a76999f168', + 'bidder_code': 'bridgewell', + 'cpm': 5.0, + 'width': 300, + 'height': 250, + 'ad': '
test 300x250
', + 'ttl': 360, + 'net_revenue': 'true', + 'currency': 'NTD' + }, { + 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', + 'bidder_code': 'bridgewell', + 'cpm': 5.0, + 'width': 728, + 'height': 90, + 'ad': '
test 728x90
', + 'ttl': 360, + 'net_revenue': 'true', + 'currency': 'NTD' + }, { + 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', + 'bidder_code': 'bridgewell', + 'cpm': 5.0, + 'width': 728, + 'height': 90, + 'ad': '
test 728x90
', + 'ttl': 360, + 'net_revenue': 'true', + 'currency': 'NTD' }]; it('should return all required parameters', () => { diff --git a/test/spec/modules/contentigniteBidAdapter_spec.js b/test/spec/modules/contentigniteBidAdapter_spec.js new file mode 100644 index 000000000000..cdf70e156152 --- /dev/null +++ b/test/spec/modules/contentigniteBidAdapter_spec.js @@ -0,0 +1,186 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/contentigniteBidAdapter'; + +describe('Content Ignite adapter', () => { + let bidRequests; + + beforeEach(() => { + bidRequests = [ + { + bidder: 'contentignite', + params: { + accountID: '168237', + zoneID: '299680', + keyword: 'business', + minCPM: '0.10', + maxCPM: '1.00' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[728, 90]], + bidId: '23acc48ad47af5', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + }); + + describe('implementation', () => { + describe('for requests', () => { + it('should accept valid bid', () => { + const validBid = { + bidder: 'contentignite', + params: { + accountID: '168237', + zoneID: '299680' + } + }, + isValid = spec.isBidRequestValid(validBid); + + expect(isValid).to.equal(true); + }); + + it('should reject invalid bid', () => { + const invalidBid = { + bidder: 'contentignite', + params: { + accountID: '168237' + } + }, + isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + + it('should set the keyword parameter', () => { + const requests = spec.buildRequests(bidRequests), + requestURL = requests[0].url; + + expect(requestURL).to.have.string(';kw=business;'); + }); + + it('should increment the count for the same zone', () => { + const bidRequests = [ + { + sizes: [[728, 90]], + bidder: 'contentignite', + params: { + accountID: '107878', + zoneID: '86133' + } + }, + { + sizes: [[728, 90]], + bidder: 'contentignite', + params: { + accountID: '107878', + zoneID: '86133' + } + } + ], + requests = spec.buildRequests(bidRequests), + firstRequest = requests[0].url, + secondRequest = requests[1].url; + + expect(firstRequest).to.have.string(';place=0;'); + expect(secondRequest).to.have.string(';place=1;'); + }); + }); + + describe('bid responses', () => { + it('should return complete bid response', () => { + const serverResponse = { + body: { + status: 'SUCCESS', + account_id: 107878, + zone_id: 86133, + cpm: 0.1, + width: 728, + height: 90, + place: 0, + ad_code: + '
', + tracking_pixels: [] + } + }, + bids = spec.interpretResponse(serverResponse, { + bidRequest: bidRequests[0] + }); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(0.1); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].ad).to.have.length.above(1); + }); + + it('should return empty bid response', () => { + const serverResponse = { + status: 'NO_ELIGIBLE_ADS', + zone_id: 299680, + width: 728, + height: 90, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, { + bidRequest: bidRequests[0] + }); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response on incorrect size', () => { + const serverResponse = { + status: 'SUCCESS', + account_id: 168237, + zone_id: 299680, + cpm: 0.1, + width: 300, + height: 250, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, { + bidRequest: bidRequests[0] + }); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response with CPM too low', () => { + const serverResponse = { + status: 'SUCCESS', + account_id: 168237, + zone_id: 299680, + cpm: 0.05, + width: 728, + height: 90, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, { + bidRequest: bidRequests[0] + }); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response with CPM too high', () => { + const serverResponse = { + status: 'SUCCESS', + account_id: 168237, + zone_id: 299680, + cpm: 7.0, + width: 728, + height: 90, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, { + bidRequest: bidRequests[0] + }); + + expect(bids).to.be.lengthOf(0); + }); + }); + }); +}); diff --git a/test/spec/modules/dfpAdServerVideo_spec.js b/test/spec/modules/dfpAdServerVideo_spec.js index f7895efa2f28..4cc6f3b93bda 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -104,6 +104,7 @@ describe('The DFP video support module', () => { expect(customParams).to.have.property('hb_adid', 'ad_id'); expect(customParams).to.have.property('hb_uuid', bid.videoCacheKey); + expect(customParams).to.have.property('hb_cache_id', bid.videoCacheKey); }); it('should merge the user-provided cust_params with the default ones', () => { diff --git a/test/spec/modules/ebdrBidAdapter_spec.js b/test/spec/modules/ebdrBidAdapter_spec.js new file mode 100644 index 000000000000..c5328f9ebb95 --- /dev/null +++ b/test/spec/modules/ebdrBidAdapter_spec.js @@ -0,0 +1,209 @@ +import { expect } from 'chai'; +import { spec } from 'modules/ebdrBidAdapter'; +import { VIDEO, BANNER } from 'src/mediaTypes'; +import * as utils from 'src/utils'; + +describe('ebdrBidAdapter', () => { + let bidRequests; + + beforeEach(() => { + bidRequests = [ + { + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bidder: 'ebdr', + params: { + zoneid: '99999', + bidfloor: '1.00', + IDFA: 'xxx-xxx', + ADID: 'xxx-xxx', + latitude: '34.089811', + longitude: '-118.392805' + }, + bidId: '2c5e8a1a84522d', + bidderRequestId: '1d0c4017f02458', + auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314' + }, { + adUnitCode: 'div-gpt-ad-1460505748561-1', + mediaTypes: { + video: { + context: 'instream', + playerSize: [300, 250] + } + }, + bidder: 'ebdr', + params: { + zoneid: '99998', + bidfloor: '1.00', + IDFA: 'xxx-xxx', + ADID: 'xxx-xxx', + latitude: '34.089811', + longitude: '-118.392805' + }, + bidId: '23a01e95856577', + bidderRequestId: '1d0c4017f02458', + auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314' + } + ]; + }); + + describe('spec.isBidRequestValid', () => { + it('should return true when the required params are passed', () => { + const bidRequest = bidRequests[0]; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when the only required param is missing', () => { + const bidRequest = bidRequests[0]; + bidRequest.params = { + zoneid: '99998', + bidfloor: '1.00', + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when the "bidfloor" param is missing', () => { + const bidRequest = bidRequests[0]; + bidRequest.params = { + zoneid: '99998', + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return false when no bid params are passed', () => { + const bidRequest = bidRequests[0]; + bidRequest.params = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when a bid request is not passed', () => { + expect(spec.isBidRequestValid()).to.equal(false); + expect(spec.isBidRequestValid({})).to.equal(false); + }); + }); + + describe('spec.buildRequests', () => { + describe('for banner bids', () => { + it('must handle an empty bid size', () => { + bidRequests[0].mediaTypes = { banner: {} }; + const requests = spec.buildRequests(bidRequests); + const bidRequest = {}; + bidRequest['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: null, h: null }; + expect(requests.bids['2c5e8a1a84522d']).to.deep.equals(bidRequest['2c5e8a1a84522d']); + }); + it('should create a single GET', () => { + bidRequests[0].mediaTypes = { banner: {} }; + bidRequests[1].mediaTypes = { banner: {} }; + const requests = spec.buildRequests(bidRequests); + expect(requests.method).to.equal('GET'); + }); + it('must parse bid size from a nested array', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {sizes: [[ width, height ]]} }; + const requests = spec.buildRequests([ bidRequest ]); + const data = {}; + data['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: width, h: height }; + expect(requests.bids['2c5e8a1a84522d']).to.deep.equal(data['2c5e8a1a84522d']); + }); + }); + describe('for video bids', () => { + it('must handle an empty bid size', () => { + bidRequests[1].mediaTypes = { video: {} }; + const requests = spec.buildRequests(bidRequests); + const bidRequest = {}; + bidRequest['23a01e95856577'] = { mediaTypes: VIDEO, w: null, h: null }; + expect(requests.bids['23a01e95856577']).to.deep.equals(bidRequest['23a01e95856577']); + }); + + it('should create a GET request for each bid', () => { + const bidRequest = bidRequests[1]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests.method).to.equal('GET'); + }); + }); + }); + + describe('spec.interpretResponse', () => { + describe('for video bids', () => { + it('should return no bids if the response is not valid', () => { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return a valid video bid response', () => { + const ebdrReq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + ebdrReq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], + h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] + }; + }); + const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '23a01e95856577', impid: '23a01e95856577', price: 0.81, adid: 'abcde-12345', nurl: 'https://cdn0.bnmla.com/vtest.xml', adm: '\nStatic VASTStatic VAST Tag00:00:15http://www.engagebdr.com/c', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, ebdrReq); + expect(bidResponse[0]).to.deep.equal({ + requestId: bidRequests[1].bidId, + vastXml: serverResponse.seatbid[0].bid[0].adm, + mediaType: 'video', + creativeId: serverResponse.seatbid[0].bid[0].crid, + cpm: serverResponse.seatbid[0].bid[0].price, + width: serverResponse.seatbid[0].bid[0].w, + height: serverResponse.seatbid[0].bid[0].h, + currency: 'USD', + netRevenue: true, + ttl: 3600, + vastUrl: serverResponse.seatbid[0].bid[0].nurl + }); + }); + }); + + describe('for banner bids', () => { + it('should return no bids if the response is not valid', () => { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return no bids if the response is empty', () => { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return valid banner bid responses', () => { + const ebdrReq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + ebdrReq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], + h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] + }; + }); + const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, ebdrReq); + expect(bidResponse[0]).to.deep.equal({ + requestId: bidRequests[ 0 ].bidId, + ad: serverResponse.seatbid[0].bid[0].adm, + mediaType: 'banner', + creativeId: serverResponse.seatbid[0].bid[0].crid, + cpm: serverResponse.seatbid[0].bid[0].price, + width: serverResponse.seatbid[0].bid[0].w, + height: serverResponse.seatbid[0].bid[0].h, + currency: 'USD', + netRevenue: true, + ttl: 3600 + }); + }); + }); + }); +}); diff --git a/test/spec/modules/eplanningAnalyticsAdapter_spec.js b/test/spec/modules/eplanningAnalyticsAdapter_spec.js new file mode 100644 index 000000000000..aba68efc27bb --- /dev/null +++ b/test/spec/modules/eplanningAnalyticsAdapter_spec.js @@ -0,0 +1,165 @@ +import eplAnalyticsAdapter from 'modules/eplanningAnalyticsAdapter'; +import includes from 'core-js/library/fn/array/includes'; +import { expect } from 'chai'; +import {parse as parseURL} from 'src/url'; +let adaptermanager = require('src/adaptermanager'); +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('eplanning analytics adapter', () => { + let xhr; + let requests; + + beforeEach(() => { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => { requests.push(request) }; + sinon.stub(events, 'getEvents').returns([]); + }); + + afterEach(() => { + xhr.restore(); + events.getEvents.restore(); + }); + + describe('track', () => { + it('builds and sends auction data', () => { + sinon.spy(eplAnalyticsAdapter, 'track'); + + let auctionTimestamp = 1496510254313; + let pauctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; + let initOptions = { + host: 'https://ads.ar.e-planning.net/hba/1/', + ci: '12345' + }; + let pbidderCode = 'adapter'; + + const bidRequest = { + bidderCode: pbidderCode, + auctionId: pauctionId, + bidderRequestId: '1a6fc81528d0f6', + bids: [{ + bidder: pbidderCode, + placementCode: 'container-1', + bidId: '208750227436c1', + bidderRequestId: '1a6fc81528d0f6', + auctionId: pauctionId, + startTime: 1509369418389, + sizes: [[300, 250]], + }], + auctionStart: 1509369418387, + timeout: 3000, + start: 1509369418389 + }; + + const bidResponse = { + bidderCode: pbidderCode, + adId: '208750227436c1', + cpm: 0.015, + auctionId: pauctionId, + responseTimestamp: 1509369418832, + requestTimestamp: 1509369418389, + bidder: pbidderCode, + timeToRespond: 443, + size: '300x250', + width: 300, + height: 250, + }; + + let bidTimeout = [ + { + bidId: '208750227436c1', + bidder: pbidderCode, + auctionId: pauctionId + } + ]; + + adaptermanager.registerAnalyticsAdapter({ + code: 'eplanning', + adapter: eplAnalyticsAdapter + }); + + adaptermanager.enableAnalytics({ + provider: 'eplanning', + options: initOptions + }); + + // Emit the events with the "real" arguments + + // Step 1: Send auction init event + events.emit(constants.EVENTS.AUCTION_INIT, { + auctionId: pauctionId, + timestamp: auctionTimestamp + }); + + // Step 2: Send bid requested event + events.emit(constants.EVENTS.BID_REQUESTED, bidRequest); + + // Step 3: Send bid response event + events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + + // Step 4: Send bid time out event + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + + // Step 5: Send auction bid won event + events.emit(constants.EVENTS.BID_WON, { + adId: 'adIdData', + ad: 'adContent', + auctionId: pauctionId, + width: 300, + height: 250 + }); + + // Step 6: Send auction end event + events.emit(constants.EVENTS.AUCTION_END, {auctionId: pauctionId}); + + // Step 7: Find the request data sent (filtering other hosts) + requests = requests.filter(req => { + return req.url.indexOf(initOptions.host) > -1; + }); + expect(requests.length).to.equal(1); + + expect(includes([initOptions.host + initOptions.ci], requests[0].url)); + expect(includes(['https://ads.ar.e-planning.net/hba/1/12345?d='], requests[0].url)); + + let info = requests[0].url; + let purl = parseURL(info); + let eplData = JSON.parse(decodeURIComponent(purl.search.d)); + + // Step 8 check that 6 events were sent + expect(eplData.length).to.equal(6); + + // Step 9 verify that we only receive the parameters we need + let expectedEventValues = [ + // AUCTION INIT + {ec: constants.EVENTS.AUCTION_INIT, + p: {auctionId: pauctionId, time: auctionTimestamp}}, + // BID REQ + {ec: constants.EVENTS.BID_REQUESTED, + p: {auctionId: pauctionId, time: 1509369418389, bidder: pbidderCode, bids: [{time: 1509369418389, sizes: [[300, 250]], bidder: pbidderCode, placementCode: 'container-1', auctionId: pauctionId}]}}, + // BID RESP + {ec: constants.EVENTS.BID_RESPONSE, + p: {auctionId: pauctionId, bidder: pbidderCode, cpm: 0.015, size: '300x250', time: 1509369418832}}, + // BID T.O. + {ec: constants.EVENTS.BID_TIMEOUT, + p: [{auctionId: pauctionId, bidder: pbidderCode}]}, + // BID WON + {ec: constants.EVENTS.BID_WON, + p: {auctionId: pauctionId, size: '300x250'}}, + // AUCTION END + {ec: constants.EVENTS.AUCTION_END, + p: {auctionId: pauctionId}} + ]; + + for (let evid = 0; evid < eplData.length; evid++) { + expect(eplData[evid]).to.deep.equal(expectedEventValues[evid]); + } + + // Step 10 check that the host to send the ajax request is configurable via options + expect(eplAnalyticsAdapter.context.host).to.equal(initOptions.host); + + // Step 11 verify that we received 6 events + sinon.assert.callCount(eplAnalyticsAdapter.track, 6); + }); + }); +}); diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index db543eda433d..36868e7f9931 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -1,5 +1,6 @@ import {expect} from 'chai'; import {spec} from 'modules/medianetBidAdapter'; +import { config } from 'src/config'; let VALID_BID_REQUEST = [{ 'bidder': 'medianet', @@ -68,6 +69,55 @@ let VALID_BID_REQUEST = [{ 'bidderRequestId': '1e9b1f07797c1c', 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' }], + VALID_PAYLOAD_INVALID_BIDFLOOR = { + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + }, + 'ext': { + 'customer_id': 'customer_id', + 'prebid_version': $$PREBID_GLOBAL$$.version + }, + 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'imp': [{ + 'id': '28f8f8130a583e', + 'ext': { + 'dfp_id': 'div-gpt-ad-1460505748561-0' + }, + 'banner': [{ + 'w': 300, + 'h': 250 + }], + 'all': { + 'cid': 'customer_id', + 'bidfloor': 'abcdef', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + } + }, { + 'id': '3f97ca71b1e5c2', + 'ext': { + 'dfp_id': 'div-gpt-ad-1460505748561-123' + }, + 'banner': [{ + 'w': 300, + 'h': 251 + }], + 'all': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + } + }], + 'tmax': config.getConfig('bidderTimeout') + }, VALID_PAYLOAD = { 'site': { 'page': 'http://media.net/prebidtest', @@ -75,7 +125,8 @@ let VALID_BID_REQUEST = [{ 'ref': 'http://media.net/prebidtest' }, 'ext': { - 'customer_id': 'customer_id' + 'customer_id': 'customer_id', + 'prebid_version': $$PREBID_GLOBAL$$.version }, 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ @@ -86,7 +137,15 @@ let VALID_BID_REQUEST = [{ 'banner': [{ 'w': 300, 'h': 250 - }] + }], + 'all': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + } }, { 'id': '3f97ca71b1e5c2', 'ext': { @@ -95,8 +154,17 @@ let VALID_BID_REQUEST = [{ 'banner': [{ 'w': 300, 'h': 251 - }] - }] + }], + 'all': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + } + }], + 'tmax': config.getConfig('bidderTimeout') }, VALID_PARAMS = { bidder: 'medianet', @@ -286,7 +354,7 @@ describe('Media.net bid adapter', () => { it('should ignore bidfloor if not a valid number', () => { let bidReq = spec.buildRequests(VALID_BID_REQUEST_INVALID_BIDFLOOR); - expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD); + expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_INVALID_BIDFLOOR); }); }); diff --git a/test/spec/modules/oneplanetonlyBidAdapter_spec.js b/test/spec/modules/oneplanetonlyBidAdapter_spec.js new file mode 100644 index 000000000000..4a42b471b6f2 --- /dev/null +++ b/test/spec/modules/oneplanetonlyBidAdapter_spec.js @@ -0,0 +1,100 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/oneplanetonlyBidAdapter'; + +describe('OnePlanetOnlyAdapter', () => { + let bid = { + bidId: '51ef8751f9aead', + bidder: 'oneplanetonly', + params: { + siteId: '5', + adUnitId: '5-4587544', + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + sizes: [[300, 250], [300, 600]], + bidderRequestId: '418b37f85e772c', + auctionId: '18fd8b8b0bd757' + }; + + describe('isBidRequestValid', () => { + it('Should return true if there are params.siteId and params.adUnitId parameters present', () => { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false if at least one of parameters is not present', () => { + delete bid.params.adUnitId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', () => { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', () => { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', () => { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', () => { + expect(serverRequest.url).to.equal('//show.oneplanetonly.com/prebid?siteId=5'); + }); + it('Returns valid data if array of bids is valid', () => { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('id', 'ver', 'prebidVer', 'transactionId', 'currency', 'timeout', 'siteId', + 'domain', 'page', 'referrer', 'adUnits'); + + let adUnit = data.adUnits[0]; + expect(adUnit).to.have.keys('id', 'bidId', 'sizes'); + expect(adUnit.id).to.equal('5-4587544'); + expect(adUnit.bidId).to.equal('51ef8751f9aead'); + expect(adUnit.sizes).to.have.members(['300x250', '300x600']); + }); + it('Returns empty data if no valid requests are passed', () => { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.adUnits).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', () => { + it('Should interpret banner response', () => { + const serverResponse = { + body: { + bids: [{ + requestId: '51ef8751f9aead', + cpm: 0.4, + width: 300, + height: 250, + creativeId: '2', + currency: 'USD', + ad: 'Test', + ttl: 120, + }] + } + }; + let bannerResponses = spec.interpretResponse(serverResponse); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let bidObject = bannerResponses[0]; + expect(bidObject).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency'); + expect(bidObject.requestId).to.equal('51ef8751f9aead'); + expect(bidObject.cpm).to.equal(0.4); + expect(bidObject.width).to.equal(300); + expect(bidObject.height).to.equal(250); + expect(bidObject.ad).to.equal('Test'); + expect(bidObject.ttl).to.equal(120); + expect(bidObject.creativeId).to.equal('2'); + expect(bidObject.netRevenue).to.be.true; + expect(bidObject.currency).to.equal('USD'); + }); + it('Should return an empty array if invalid response is passed', () => { + const invalid = { + body: {} + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); +}); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index e28d5854ba24..383c599a1336 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -12,6 +12,7 @@ let CONFIG = { enabled: true, bidders: ['appnexus'], timeout: 1000, + cacheMarkup: 2, endpoint: 'https://prebid.adnxs.com/pbs/v1/auction' }; @@ -143,7 +144,40 @@ const RESPONSE = { 'deal_id': 'test-dealid', 'ad_server_targeting': { 'foo': 'bar' - } + }, + 'cache_id': '7654321', + 'cache_url': 'http://www.test.com/cache?uuid=7654321', + } + ] +}; + +const VIDEO_RESPONSE = { + 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', + 'status': 'OK', + 'bidder_status': [ + { + 'bidder': 'appnexus', + 'response_time_ms': 52, + 'num_bids': 1 + } + ], + 'bids': [ + { + 'bid_id': '123', + 'code': 'div-gpt-ad-1460505748561-0', + 'creative_id': '29681110', + 'bidder': 'appnexus', + 'price': 0.5, + 'adm': '', + 'width': 300, + 'height': 250, + 'deal_id': 'test-dealid', + 'ad_server_targeting': { + 'foo': 'bar' + }, + 'media_type': 'video', + 'cache_id': 'video_cache_id', + 'cache_url': 'video_cache_url', } ] }; @@ -357,10 +391,21 @@ describe('S2S Adapter', () => { config.setConfig({s2sConfig: CONFIG}); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); + expect(requestBid).to.have.property('cache_markup', 2); expect(requestBid.ad_units[0].bids[0].params.placementId).to.exist.and.to.be.a('number'); expect(requestBid.ad_units[0].bids[0].params.member).to.exist.and.to.be.a('string'); }); + it('sets invalid cacheMarkup value to 0', () => { + const s2sConfig = Object.assign({}, CONFIG, { + cacheMarkup: 999 + }); + config.setConfig({s2sConfig: s2sConfig}); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + expect(requestBid).to.have.property('cache_markup', 0); + }); + it('adds digitrust id is present and user is not optout', () => { let digiTrustObj = { success: true, @@ -453,6 +498,63 @@ describe('S2S Adapter', () => { expect(requestBid.site.publisher).to.exist.and.to.be.a('object'); expect(requestBid.site.page).to.exist.and.to.be.a('string'); }); + + it('adds appnexus aliases to request', () => { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + }); + config.setConfig({s2sConfig}); + + const aliasBidder = { + bidder: 'brealtime', + params: { placementId: '123456' } + }; + + const request = Object.assign({}, REQUEST); + request.ad_units[0].bids = [aliasBidder]; + + adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); + + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.ext).to.deep.equal({ + prebid: { + aliases: { + brealtime: 'appnexus' + } + } + }); + }); + + it('adds dynamic aliases to request', () => { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + }); + config.setConfig({s2sConfig}); + + const alias = 'foobar'; + const aliasBidder = { + bidder: alias, + params: { placementId: '123456' } + }; + + const request = Object.assign({}, REQUEST); + request.ad_units[0].bids = [aliasBidder]; + + // TODO: stub this + $$PREBID_GLOBAL$$.aliasBidder('appnexus', alias); + adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); + + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.ext).to.deep.equal({ + prebid: { + aliases: { + [alias]: 'appnexus' + } + } + }); + }); }); describe('response handler', () => { @@ -491,6 +593,28 @@ describe('S2S Adapter', () => { expect(response).to.have.property('statusMessage', 'Bid available'); expect(response).to.have.property('cpm', 0.5); expect(response).to.have.property('adId', '123'); + expect(response).to.not.have.property('videoCacheKey'); + expect(response).to.have.property('cache_id', '7654321'); + expect(response).to.have.property('cache_url', 'http://www.test.com/cache?uuid=7654321'); + expect(response).to.not.have.property('vastUrl'); + }); + + it('registers video bids', () => { + server.respondWith(JSON.stringify(VIDEO_RESPONSE)); + + config.setConfig({s2sConfig: CONFIG}); + adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + sinon.assert.calledOnce(addBidResponse); + + const response = addBidResponse.firstCall.args[1]; + expect(response).to.have.property('statusMessage', 'Bid available'); + expect(response).to.have.property('cpm', 0.5); + expect(response).to.have.property('adId', '123'); + expect(response).to.have.property('videoCacheKey', 'video_cache_id'); + expect(response).to.have.property('cache_id', 'video_cache_id'); + expect(response).to.have.property('cache_url', 'video_cache_url'); + expect(response).to.have.property('vastUrl', 'video_cache_url'); }); it('does not call addBidResponse and calls done when ad unit not set', () => { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 1c4bccc69c58..107f8d3ea2c3 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -17,6 +17,31 @@ describe('the rubicon adapter', () => { function createVideoBidderRequest() { let bid = bidderRequest.bids[0]; + + bid.mediaTypes = { + video: { + context: 'instream' + } + }; + + bid.params.video = { + 'language': 'en', + 'p_aso.video.ext.skip': true, + 'p_aso.video.ext.skipdelay': 15, + 'playerHeight': 320, + 'playerWidth': 640, + 'size_id': 201, + 'aeParams': { + 'p_aso.video.ext.skip': '1', + 'p_aso.video.ext.skipdelay': '15' + } + }; + } + + function createLegacyVideoBidderRequest() { + let bid = bidderRequest.bids[0]; + + // Legacy property (Prebid <1.0) bid.mediaType = 'video'; bid.params.video = { 'language': 'en', @@ -33,12 +58,62 @@ describe('the rubicon adapter', () => { } function createVideoBidderRequestNoVideo() { + let bid = bidderRequest.bids[0]; + bid.mediaTypes = { + video: { + context: 'instream' + }, + }; + bid.params.video = ''; + } + + function createLegacyVideoBidderRequestNoVideo() { let bid = bidderRequest.bids[0]; bid.mediaType = 'video'; bid.params.video = ''; } + function createVideoBidderRequestOutstream() { + let bid = bidderRequest.bids[0]; + bid.mediaTypes = { + video: { + context: 'outstream' + }, + }; + bid.params.video = { + 'language': 'en', + 'p_aso.video.ext.skip': true, + 'p_aso.video.ext.skipdelay': 15, + 'playerHeight': 320, + 'playerWidth': 640, + 'size_id': 201, + 'aeParams': { + 'p_aso.video.ext.skip': '1', + 'p_aso.video.ext.skipdelay': '15' + } + }; + } + function createVideoBidderRequestNoPlayer() { + let bid = bidderRequest.bids[0]; + bid.mediaTypes = { + video: { + context: 'instream' + }, + }; + bid.params.video = { + 'language': 'en', + 'p_aso.video.ext.skip': true, + 'p_aso.video.ext.skipdelay': 15, + 'size_id': 201, + 'aeParams': { + 'p_aso.video.ext.skip': '1', + 'p_aso.video.ext.skipdelay': '15' + } + }; + } + + function createLegacyVideoBidderRequestNoPlayer() { let bid = bidderRequest.bids[0]; bid.mediaType = 'video'; bid.params.video = { @@ -81,6 +156,7 @@ describe('the rubicon adapter', () => { referrer: 'localhost' }, adUnitCode: '/19968336/header-bid-tag-0', + code: 'div-1', sizes: [[300, 250], [320, 50]], bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', @@ -120,8 +196,7 @@ describe('the rubicon adapter', () => { describe('for requests', () => { describe('to fastlane', () => { it('should make a well-formed request objects', () => { - sandbox.stub(Math, 'random').returns(0.1); - + sandbox.stub(Math, 'random').callsFake(() => 0.1); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -451,6 +526,67 @@ describe('the rubicon adapter', () => { }); describe('for video requests', () => { + it('should make a well-formed video request with legacy mediaType config', () => { + createLegacyVideoBidderRequest(); + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let post = request.data; + + let url = request.url; + + expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); + + expect(post).to.have.property('page_url').that.is.a('string'); + expect(post.resolution).to.match(/\d+x\d+/); + expect(post.account_id).to.equal('14062'); + expect(post.integration).to.equal(INTEGRATION); + expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b'); + expect(post).to.have.property('timeout').that.is.a('number'); + expect(post.timeout < 5000).to.equal(true); + expect(post.stash_creatives).to.equal(true); + + expect(post).to.have.property('ae_pass_through_parameters'); + expect(post.ae_pass_through_parameters) + .to.have.property('p_aso.video.ext.skip') + .that.equals('1'); + expect(post.ae_pass_through_parameters) + .to.have.property('p_aso.video.ext.skipdelay') + .that.equals('15'); + + expect(post).to.have.property('slots') + .with.length.of(1); + + let slot = post.slots[0]; + + expect(slot.site_id).to.equal('70608'); + expect(slot.zone_id).to.equal('335918'); + expect(slot.position).to.equal('atf'); + expect(slot.floor).to.equal(0.01); + expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); + expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); + expect(slot.language).to.equal('en'); + expect(slot.width).to.equal(640); + expect(slot.height).to.equal(320); + expect(slot.size_id).to.equal(201); + + expect(slot).to.have.property('inventory').that.is.an('object'); + expect(slot.inventory).to.have.property('rating').that.equals('5-star'); + expect(slot.inventory).to.have.property('prodtype').that.equals('tech'); + + expect(slot).to.have.property('keywords') + .that.is.an('array') + .of.length(3) + .that.deep.equals(['a', 'b', 'c']); + + expect(slot).to.have.property('visitor').that.is.an('object'); + expect(slot.visitor).to.have.property('ucat').that.equals('new'); + expect(slot.visitor).to.have.property('lastsearch').that.equals('iphone'); + }); + it('should make a well-formed video request', () => { createVideoBidderRequest(); @@ -576,17 +712,60 @@ describe('the rubicon adapter', () => { expect(floor).to.equal(3.25); }); - it('should not validate bid request when no video object is passed in', () => { + it('should not validate bid request when a invalid video object is passed in', () => { createVideoBidderRequestNoVideo(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); - var floorBidderRequest = clone(bidderRequest); + const bidRequestCopy = clone(bidderRequest.bids[0]); + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - let result = spec.isBidRequestValid(floorBidderRequest.bids[0]); + bidRequestCopy.params.video = {}; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - expect(result).to.equal(false); + bidRequestCopy.params.video = undefined; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + + bidRequestCopy.params.video = 123; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + + bidRequestCopy.params.video = { size_id: '' }; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + + delete bidRequestCopy.params.video; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + }); + + it('should not validate bid request when an invalid video object is passed in with legacy config mediaType', () => { + createLegacyVideoBidderRequestNoVideo(); + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + const bidderRequestCopy = clone(bidderRequest); + expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + + bidderRequestCopy.bids[0].params.video = {}; + expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + + bidderRequestCopy.bids[0].params.video = undefined; + expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + + bidderRequestCopy.bids[0].params.video = NaN; + expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + + delete bidderRequestCopy.bids[0].params.video; + expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + }); + + it('should not validate bid request when video is outstream', () => { + createVideoBidderRequestOutstream(); + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); }); it('should get size from bid.sizes too', () => { @@ -595,13 +774,55 @@ describe('the rubicon adapter', () => { bidderRequest.auctionStart + 100 ); - var floorBidderRequest = clone(bidderRequest); + const bidRequestCopy = clone(bidderRequest); - let [request] = spec.buildRequests(floorBidderRequest.bids, floorBidderRequest); - let post = request.data; + let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); - expect(post.slots[0].width).to.equal(300); - expect(post.slots[0].height).to.equal(250); + expect(request.data.slots[0].width).to.equal(300); + expect(request.data.slots[0].height).to.equal(250); + }); + + it('should get size from bid.sizes too with legacy config mediaType', () => { + createLegacyVideoBidderRequestNoPlayer(); + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + const bidRequestCopy = clone(bidderRequest); + + let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); + + expect(request.data.slots[0].width).to.equal(300); + expect(request.data.slots[0].height).to.equal(250); + }); + }); + + describe('hasVideoMediaType', () => { + it('should return true if mediaType is true', () => { + createVideoBidderRequest(); + const legacyVideoTypeBidRequest = spec.hasVideoMediaType(bidderRequest.bids[0]); + expect(legacyVideoTypeBidRequest).is.equal(true); + }); + + it('should return false if bidRequest.mediaType is not equal to video', () => { + expect(spec.hasVideoMediaType({ + mediaType: 'banner' + })).is.equal(false); + }); + + it('should return false if bidRequest.mediaType is not defined', () => { + expect(spec.hasVideoMediaType({})).is.equal(false); + }); + + it('should return true if bidRequest.mediaTypes.video object exists', () => { + expect(spec.hasVideoMediaType({ + mediaTypes: { + video: { + context: 'outstream', + playerSize: [300, 250] + } + } + })).is.equal(true); }); }); }); diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 5cb4a1277fbe..6e0526546857 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -74,9 +74,9 @@ describe('Smart ad server bid adapter tests', () => { } }); 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(request[0]).to.have.property('url').and.to.equal('http://prg.smartadserver.com/prebid/v1'); + expect(request[0]).to.have.property('method').and.to.equal('POST'); + const requestContent = JSON.parse(request[0].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'); @@ -95,7 +95,7 @@ describe('Smart ad server bid adapter tests', () => { it('Verify parse response', () => { const request = spec.buildRequests(DEFAULT_PARAMS); - const bids = spec.interpretResponse(BID_RESPONSE, request); + const bids = spec.interpretResponse(BID_RESPONSE, request[0]); expect(bids).to.have.lengthOf(1); const bid = bids[0]; expect(bid.cpm).to.equal(12); diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 5fc7cb6a62ab..a440b3d43c4c 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -83,6 +83,26 @@ describe('sovrnBidAdapter', function() { expect(request.data).to.contain('"iv":"vet"') }) + + it('converts tagid to string', () => { + const ivBidRequests = [{ + 'bidder': 'sovrn', + 'params': { + 'tagid': 403370, + 'iv': 'vet' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + const request = spec.buildRequests(ivBidRequests); + + expect(request.data).to.contain('"tagid":"403370"') + }) }); describe('interpretResponse', () => { diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 2a19763b10a6..1a5ca59d3a22 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -5,15 +5,15 @@ import { newBidder } from 'src/adapters/bidderFactory' const REQUEST = { 'bidder': 'yieldlab', 'params': { - 'placementId': '1111', - 'accountId': '2222', - 'adSize': '800x250' + 'adslotId': '1111', + 'supplyId': '2222', + 'adSize': '728x90' }, 'bidderRequestId': '143346cf0f1731', 'auctionId': '2e41f65424c87c', 'adUnitCode': 'adunit-code', 'bidId': '2d925f27f5079f', - 'sizes': [1, 1] + 'sizes': [728, 90] } const RESPONSE = { @@ -21,7 +21,8 @@ const RESPONSE = { curl: 'https://www.yieldlab.de', format: 0, id: 1111, - price: 1 + price: 1, + pid: 2222 } describe('yieldlabBidAdapter', () => { @@ -37,9 +38,9 @@ describe('yieldlabBidAdapter', () => { it('should return true when required params found', () => { const request = { 'params': { - 'placementId': '1111', - 'accountId': '2222', - 'adSize': '800x250' + 'adslotId': '1111', + 'supplyId': '2222', + 'adSize': '728x90' } } expect(spec.isBidRequestValid(request)).to.equal(true) @@ -78,15 +79,15 @@ describe('yieldlabBidAdapter', () => { expect(result[0].requestId).to.equal('2d925f27f5079f') expect(result[0].cpm).to.equal(0.01) - expect(result[0].width).to.equal(800) - expect(result[0].height).to.equal(250) + expect(result[0].width).to.equal(728) + expect(result[0].height).to.equal(90) expect(result[0].creativeId).to.equal('1111') - expect(result[0].dealId).to.equal(undefined) + expect(result[0].dealId).to.equal(2222) expect(result[0].currency).to.equal('EUR') - expect(result[0].netRevenue).to.equal(true) - expect(result[0].ttl).to.equal(600) + expect(result[0].netRevenue).to.equal(false) + expect(result[0].ttl).to.equal(300) expect(result[0].referrer).to.equal('') - expect(result[0].ad).to.include('