diff --git a/README.md b/README.md index 5602745980e9..7038ac6528de 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Code Climate](https://codeclimate.com/github/prebid/Prebid.js/badges/gpa.svg)](https://codeclimate.com/github/prebid/Prebid.js) [![Coverage Status](https://coveralls.io/repos/github/prebid/Prebid.js/badge.svg)](https://coveralls.io/github/prebid/Prebid.js) [![devDependencies Status](https://david-dm.org/prebid/Prebid.js/dev-status.svg)](https://david-dm.org/prebid/Prebid.js?type=dev) +[![Total Alerts](https://img.shields.io/lgtm/alerts/g/prebid/Prebid.js.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/prebid/Prebid.js/alerts/) # Prebid.js 1.9 diff --git a/RELEASE_SCHEDULE.md b/RELEASE_SCHEDULE.md index 0c424e76ed45..637ed354c748 100644 --- a/RELEASE_SCHEDULE.md +++ b/RELEASE_SCHEDULE.md @@ -17,7 +17,7 @@ Announcements regarding releases will be made to the #headerbidding-dev channel ## Release Process -1. Make Sure all browserstack tests are passing. On PR merge to master travis will run unit tests on browserstack. Checking the last travis build [here](https://travis-ci.org/prebid/Prebid.js/branches) for master branch will show you detailed results. +1. Make Sure all browserstack tests are passing. On PR merge to master CircleCI will run unit tests on browserstack. Checking the last CircleCI build [here](https://circleci.com/gh/prebid) for master branch will show you detailed results. In case of failure do following, - Try to fix the failing tests. diff --git a/governance.md b/governance.md index 9f8a1fe1e135..3d00f067194f 100644 --- a/governance.md +++ b/governance.md @@ -2,10 +2,10 @@ This document describes the governance model for the Prebid project. The Prebid project’s stated mission is to facilitate fair, transparent, and effective header bidding across the industry, and is responsible for creating and maintaining such projects as [Prebid.js](https://github.com/prebid/Prebid.js). -1. A single Tech Lead oversees the technical direction of the project and appoints Core Team members +1. A single Tech Lead (PMC Chair) oversees the technical direction of the project and appoints Core Team members 2. The Core Team members maintain the project on an ongoing basis with direction from the Tech Lead. 3. In the event of any disagreements, the Tech Lead will make a final decision. -4. If there is no Tech Lead available to perform his/her duties, AppNexus Inc. will appoint one. +4. If there is no Tech Lead available to perform his/her duties, Prebid.org will appoint one. ### Roles and Responsibilities: - **User:** Any individual who consumes / uses the Prebid.js library. @@ -15,9 +15,9 @@ This document describes the governance model for the Prebid project. The Prebid ### Current Prebid.js Core Team - @mkendall07 (Tech Lead) -- @protonate +- @jsnellbaker - @matthewlane - @jaiminpanchal27 - @snapwich - @harpere -- @dbemiller +- @mike-chowla diff --git a/gulpfile.js b/gulpfile.js index d2955f7d777d..92dd2a7c1f18 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -39,7 +39,7 @@ var port = 9999; // Tasks gulp.task('default', ['webpack']); -gulp.task('serve', ['lint', 'build-bundle-dev', 'watch', 'test']); +gulp.task('serve', ['build-bundle-dev', 'watch', 'test']); gulp.task('serve-nw', ['lint', 'watch', 'e2etest']); @@ -49,8 +49,8 @@ gulp.task('build', ['build-bundle-prod']); gulp.task('clean', function () { return gulp.src(['build'], { - read: false - }) + read: false + }) .pipe(clean()); }); @@ -78,13 +78,13 @@ var explicitModules = [ function bundle(dev, moduleArr) { var modules = moduleArr || helpers.getArgModules(), - allModules = helpers.getModuleNames(modules); + allModules = helpers.getModuleNames(modules); - if(modules.length === 0) { + if (modules.length === 0) { modules = allModules.filter(module => !explicitModules.includes(module)); } else { var diff = _.difference(modules, allModules); - if(diff.length !== 0) { + if (diff.length !== 0) { throw new gutil.PluginError({ plugin: 'bundle', message: 'invalid modules: ' + diff.join(', ') @@ -106,13 +106,13 @@ function bundle(dev, moduleArr) { gutil.log('Generating bundle:', outputFileName); return gulp.src( - entries - ) + entries + ) .pipe(gulpif(dev, sourcemaps.init({loadMaps: true}))) .pipe(concat(outputFileName)) .pipe(gulpif(!argv.manualEnable, footer('\n<%= global %>.processQueue();', { - global: prebid.globalVarName - } + global: prebid.globalVarName + } ))) .pipe(gulpif(dev, sourcemaps.write('.'))); } @@ -186,7 +186,7 @@ gulp.task('webpack', ['clean'], function () { // If --file "" is given, the task will only run tests in the specified file. // If --browserstack is given, it will run the full suite of currently supported browsers. // If --browsers is given, browsers can be chosen explicitly. e.g. --browsers=chrome,firefox,ie9 -gulp.task('test', ['clean'], function (done) { +gulp.task('test', ['clean', 'lint'], function (done) { var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file); var browserOverride = helpers.parseBrowserArgs(argv).map(helpers.toCapitalCase); @@ -229,7 +229,7 @@ gulp.task('watch', function () { 'modules/**/*.js', 'test/spec/**/*.js', '!test/spec/loaders/**/*.js' - ], ['lint', 'build-bundle-dev', 'test']); + ], ['build-bundle-dev', 'test']); gulp.watch([ 'loaders/**/*.js', 'test/spec/loaders/**/*.js' @@ -264,7 +264,7 @@ gulp.task('docs', ['clean-docs'], function () { gulp.task('e2etest', ['devpack', 'webpack'], function() { var cmdQueue = []; - if(argv.browserstack) { + if (argv.browserstack) { var browsers = require('./browsers.json'); delete browsers['bs_ie_9_windows_7']; @@ -276,11 +276,11 @@ gulp.task('e2etest', ['devpack', 'webpack'], function() { var startWith = 'bs'; - Object.keys(browsers).filter(function(v){ + Object.keys(browsers).filter(function(v) { return v.substring(0, startWith.length) === startWith && browsers[v].browser !== 'iphone'; - }).map(function(v,i,arr) { - var newArr = (i%2 === 0) ? arr.slice(i,i+2) : null; - if(newArr) { + }).map(function(v, i, arr) { + var newArr = (i % 2 === 0) ? arr.slice(i, i + 2) : null; + if (newArr) { var cmd = 'nightwatch --env ' + newArr.join(',') + cmdStr; cmdQueue.push(cmd); } diff --git a/integrationExamples/gpt/prebidServer_example.html b/integrationExamples/gpt/prebidServer_example.html index 2f91c2b966fa..f13c93963c64 100644 --- a/integrationExamples/gpt/prebidServer_example.html +++ b/integrationExamples/gpt/prebidServer_example.html @@ -4,7 +4,6 @@ var PREBID_TIMEOUT = 3000; var googletag = googletag || {}; - var sizes = [[728, 90],[300, 250], [300,600]]; googletag.cmd = googletag.cmd || []; function initAdserver() { @@ -37,12 +36,12 @@ pbjs.que.push(function() { var adUnits = [{ code: 'div-gpt-ad-1460505748561-0', - sizes: [[300, 250], [300,600]], + sizes: [[300, 250]], bids: [ { bidder: 'appnexus', params: { - placementId: '10433394' + placementId: '13144370' } } ] @@ -56,7 +55,7 @@ bidders : ['appnexus'], timeout : 1000, //default value is 1000 adapter : 'prebidServer', //if we have any other s2s adapter, default value is s2s - endpoint : 'https://prebid.adnxs.com/pbs/v1/auction?url_override=http%3A%2F%2Fwww.nytimes.com' + endpoint : 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' } }); @@ -72,7 +71,7 @@ ' + oad; - ad += ''; - ad += ''; - ad += '' - if (response.ext && response.ext.pixels) { - if (config.getConfig('consumable.userSyncOn') !== EVENTS.BID_RESPONSE) { - ad += _formatPixels(response.ext.pixels); +const sizeMap = [ + null, + '120x90', + '120x90', + '468x60', + '728x90', + '300x250', + '160x600', + '120x600', + '300x100', + '180x150', + '336x280', + '240x400', + '234x60', + '88x31', + '120x60', + '120x240', + '125x125', + '220x250', + '250x250', + '250x90', + '0x0', + '200x90', + '300x50', + '320x50', + '320x480', + '185x185', + '620x45', + '300x125', + '800x250' +]; + +sizeMap[77] = '970x90'; +sizeMap[123] = '970x250'; +sizeMap[43] = '300x600'; +sizeMap[286] = '970x66'; +sizeMap[3230] = '970x280'; +sizeMap[429] = '486x60'; +sizeMap[374] = '700x500'; +sizeMap[934] = '300x1050'; +sizeMap[1578] = '320x100'; +sizeMap[331] = '320x250'; +sizeMap[3301] = '320x267'; +sizeMap[2730] = '728x250'; + +function getSize(sizes) { + const result = []; + sizes.forEach(function(size) { + const index = sizeMap.indexOf(size[0] + 'x' + size[1]); + if (index >= 0) { + result.push(index); } - } - - return { - bidderCode: bidRequest.bidderCode, - requestId: bidRequest.bidId, - ad: ad, - cpm: cpm, - width: bidData.w, - height: bidData.h, - creativeId: bidData.crid, - pubapiId: response.id, - currency: response.cur, - dealId: bidData.dealid, - netRevenue: true, - ttl: CONSUMABLE_TTL - }; + }); + return result; } -function _formatPixels (pixels) { - let formattedPixels = pixels.replace(/<\/?script( type=('|")text\/javascript('|")|)?>/g, ''); +function retrieveAd(decision, unitId, unitName) { + let oad = decision.contents && decision.contents[0] && decision.contents[0].body + utils.createTrackPixelHtml(decision.impressionUrl); + let cb = Math.round(new Date().getTime()); + let ad = '' + oad; + ad += ''; + ad += ''; + ad += '' - return ''; + return ad; } -export const spec = { - code: CONSUMABLE_BIDDER_CODE, - isBidRequestValid: function(bid) { - return bid.params && bid.params.placement - }, - buildRequests: function (bids) { - return bids.map(formatBidRequest); - }, - interpretResponse: function ({body}, bidRequest) { - if (!body) { - utils.logError('Empty bid response', bidRequest.bidderCode, body); - } else { - let bid = _parseBidResponse(body, bidRequest); - if (bid) { - return bid; - } - } - }, - getUserSyncs: function(options, bidResponses) { - let bidResponse = bidResponses[0]; - - if (config.getConfig('consumable.userSyncOn') === EVENTS.BID_RESPONSE) { - if (!$$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped && bidResponse.ext && bidResponse.ext.pixels) { - $$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped = true; - - return parsePixelItems(bidResponse.ext.pixels); - } - } - - return []; - } -}; - registerBidder(spec); diff --git a/modules/consumableBidAdapter.md b/modules/consumableBidAdapter.md index f6dfe8131c69..2189494ebd4d 100644 --- a/modules/consumableBidAdapter.md +++ b/modules/consumableBidAdapter.md @@ -14,19 +14,35 @@ Module that connects to Consumable's demand sources ```javascript var adUnits = [ { - code: 'test-ad-div', + code: 'test-ad-1', sizes: [[300, 250]], bids: [ { bidder: 'consumable', params: { - placement: '1234567', - unitId: '1234', - unitName: 'cnsmbl-300x250', - zoneId: '13136.52' + networkId: '9969', + siteId: '980639', + unitId: '123456', + unitName: 'cnsmbl-unit' + } + } + ] + }, + { + code: 'test-ad-2', + sizes: [[300, 250]], + bids: [ + { + bidder: 'consumable', + params: { + networkId: '9969', + siteId: '980639', + unitId: '123456', + unitName: 'cnsmbl-unit', + zoneIds: [178503] } } ] } ]; -``` +``` \ No newline at end of file diff --git a/modules/currency.js b/modules/currency.js index 25eddc5b9938..f66c33bbed80 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -154,6 +154,14 @@ export function addBidResponseHook(adUnitCode, bid, fn) { bid.currency = 'USD'; } + let fromCurrency = bid.currency; + let cpm = bid.cpm; + + // used for analytics + bid.getCpmInNewCurrency = function(toCurrency) { + return (parseFloat(cpm) * getCurrencyConversion(fromCurrency, toCurrency)).toFixed(3); + }; + // execute immediately if the bid is already in the desired currency if (bid.currency === adServerCurrency) { return fn.apply(this, arguments); @@ -178,16 +186,12 @@ function wrapFunction(fn, context, params) { let fromCurrency = bid.currency; try { let conversion = getCurrencyConversion(fromCurrency); - let cpm = bid.originalCpm = bid.cpm; + bid.originalCpm = bid.cpm; bid.originalCurrency = bid.currency; if (conversion !== 1) { bid.cpm = (parseFloat(bid.cpm) * conversion).toFixed(4); bid.currency = adServerCurrency; } - // used for analytics - bid.getCpmInNewCurrency = function(toCurrency) { - return (parseFloat(cpm) * getCurrencyConversion(fromCurrency, toCurrency)).toFixed(3); - }; } catch (e) { utils.logWarn('Returning NO_BID, getCurrencyConversion threw error: ', e); params[1] = bidfactory.createBid(STATUS.NO_BID, { diff --git a/modules/divreachBidAdapter.js b/modules/divreachBidAdapter.js new file mode 100644 index 000000000000..e597e32f4be9 --- /dev/null +++ b/modules/divreachBidAdapter.js @@ -0,0 +1,74 @@ +import * as utils from 'src/utils'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'divreach'; +const ENDPOINT_URL = '//ads.divreach.com/prebid.1.0.aspx'; +export const spec = { + code: BIDDER_CODE, + aliases: [], + supportedMediaTypes: ['banner', 'video'], + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + return !!bid.params.zone; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {bidderRequest} - bidderRequest.bids[] is an array of AdUnits and bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (bidderRequest) { + const payload = { + imps: [], + referrer: encodeURIComponent(utils.getTopWindowUrl()), + }; + bidderRequest.forEach((bid) => { + if (bid.bidder === BIDDER_CODE) { + payload.imps.push(bid); + } + }); + const payloadString = JSON.stringify(payload); + return { + method: 'GET', + url: ENDPOINT_URL, + data: `data=${payloadString}`, + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + // loop through serverResponses { + try { + serverResponse = serverResponse.body; + serverResponse.forEach((bidResponse) => { + const bidResp = { + requestId: bidResponse.bidId, + cpm: bidResponse.cpm, + width: bidResponse.width, + height: bidResponse.height, + ad: bidResponse.ad, + ttl: bidResponse.ttl, + creativeId: bidResponse.creativeId, + netRevenue: bidResponse.netRevenue, + currency: bidResponse.currency, + vastUrl: bidResponse.vastUrl, + }; + bidResponses.push(bidResp); + }); + } catch (e) { + utils.logError(e); + } + return bidResponses; + } +}; +registerBidder(spec); diff --git a/modules/divreachBidAdapter.md b/modules/divreachBidAdapter.md new file mode 100644 index 000000000000..da2ebee97cf0 --- /dev/null +++ b/modules/divreachBidAdapter.md @@ -0,0 +1,52 @@ +# Overview + +Module Name: DivReach Bidder Adapter +Module Type: Bidder Adapter +Maintainer: Zeke@divreach.com + +# Description + +Connects to DivReach demand source to fetch bids. +Banner and Video formats are supported. +Please use ```divreach``` as the bidder code. + +# Test Parameters +``` + var adUnits = [ + { + code: 'desktop-banner-ad-div', + sizes: [[300, 250]], // a display size + bids: [ + { + bidder: "divreach", + params: { + zone: '261eae83-0508-4e1a-8c9b-19561fa9279e' + } + } + ] + },{ + code: 'mobile-banner-ad-div', + sizes: [[300, 50]], // a mobile size + bids: [ + { + bidder: "divreach", + params: { + zone: '561e26ea-1999-4fb6-ad0b-9d72929e545e' + } + } + ] + },{ + code: 'video-ad', + sizes: [[300, 50]], + mediaType: 'video', + bids: [ + { + bidder: "divreach", + params: { + zone: 'e784ecbe-720f-46f7-8388-aff8c2c4ed86' + } + } + ] + }, + ]; +``` diff --git a/modules/fidelityBidAdapter.js b/modules/fidelityBidAdapter.js index ca7dc9d6fd62..08a032bcba93 100644 --- a/modules/fidelityBidAdapter.js +++ b/modules/fidelityBidAdapter.js @@ -1,13 +1,14 @@ -import * as utils from 'src/utils'; -import {registerBidder} from 'src/adapters/bidderFactory'; - -const BIDDER_CODE = 'fidelity'; -const BIDDER_SERVER = 'x.fidelity-media.com'; -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function(bid) { - return !!(bid && bid.params && bid.params.zoneid); - }, +import * as utils from 'src/utils'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'fidelity'; +const BIDDER_SERVER = 'x.fidelity-media.com'; +const FIDELITY_VENDOR_ID = 408; +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.zoneid); + }, buildRequests: function(validBidRequests, bidderRequest) { return validBidRequests.map(bidRequest => { var server = bidRequest.params.server || BIDDER_SERVER; @@ -20,15 +21,13 @@ export const spec = { zoneid: bidRequest.params.zoneid, floor: parseFloat(bidRequest.params.floor) > 0 ? bidRequest.params.floor : 0, charset: document.charSet || document.characterSet, - defloc: utils.getTopWindowUrl(), - altloc: window.location.href, subid: 'hb', flashver: getFlashVersion(), tmax: bidderRequest.timeout, + defloc: utils.getTopWindowUrl(), + referrer: utils.getTopWindowReferrer(), }; - if (document.referrer) { - payload.referrer = document.referrer; - } + setConsentParams(bidderRequest.gdprConsent, payload); return { method: 'GET', @@ -37,13 +36,13 @@ export const spec = { }; }); }, - interpretResponse: function(serverResponse) { - serverResponse = serverResponse.body; + interpretResponse: function(serverResponse) { + serverResponse = serverResponse.body; const bidResponses = []; if (serverResponse && serverResponse.seatbid) { serverResponse.seatbid.forEach(seatBid => seatBid.bid.forEach(bid => { const bidResponse = { - requestId: bid.impid, + requestId: bid.impid, creativeId: bid.impid, cpm: bid.price, width: bid.width, @@ -51,24 +50,30 @@ export const spec = { ad: bid.adm, netRevenue: bid.netRevenue, currency: bid.cur, - ttl: bid.ttl, + ttl: bid.ttl, }; bidResponses.push(bidResponse); })); } - return bidResponses; + return bidResponses; }, - getUserSyncs: function getUserSyncs(syncOptions) { + getUserSyncs: function getUserSyncs(syncOptions, serverResponses, gdprConsent) { if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: '//' + BIDDER_SERVER + '/delivery/matches.php?type=iframe', + var url = '//' + BIDDER_SERVER + '/delivery/matches.php'; + var payload = { + type: 'iframe' + }; + setConsentParams(gdprConsent, payload); + + return [{ + type: 'iframe', + url: url + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, '') }]; } - } -} - + } +} + function getFlashVersion() { var plugins, plugin, result; @@ -82,6 +87,23 @@ function getFlashVersion() { } } return result || ''; -} - -registerBidder(spec); +} + +function setConsentParams(gdprConsent, payload) { + if (gdprConsent) { + payload.gdpr = 0; + payload.consent_str = ''; + payload.consent_given = 0; + if (typeof gdprConsent.gdprApplies !== 'undefined') { + payload.gdpr = gdprConsent.gdprApplies ? 1 : 0; + } + if (typeof gdprConsent.consentString !== 'undefined') { + payload.consent_str = gdprConsent.consentString; + } + if (gdprConsent.vendorData && gdprConsent.vendorData.vendorConsents && typeof gdprConsent.vendorData.vendorConsents[FIDELITY_VENDOR_ID.toString(10)] !== 'undefined') { + payload.consent_given = gdprConsent.vendorData.vendorConsents[FIDELITY_VENDOR_ID.toString(10)] ? 1 : 0; + } + } +} + +registerBidder(spec); diff --git a/modules/justpremiumBidAdapter.js b/modules/justpremiumBidAdapter.js index 406fc166483a..0f38f586a4d8 100644 --- a/modules/justpremiumBidAdapter.js +++ b/modules/justpremiumBidAdapter.js @@ -3,8 +3,11 @@ import { getTopWindowLocation } from 'src/utils' const BIDDER_CODE = 'justpremium' const ENDPOINT_URL = getTopWindowLocation().protocol + '//pre.ads.justpremium.com/v/2.0/t/xhr' -const JP_ADAPTER_VERSION = '1.1' +const JP_ADAPTER_VERSION = '1.2' const pixels = [] +const TRACK_START_TIME = Date.now() +let LAST_PAYLOAD = {} +let AD_UNIT_IDS = [] export const spec = { code: BIDDER_CODE, @@ -17,6 +20,11 @@ export const spec = { buildRequests: (validBidRequests, bidderRequest) => { const c = preparePubCond(validBidRequests) const dim = getWebsiteDim() + AD_UNIT_IDS = validBidRequests.map(b => { + return b.adUnitCode + }).filter((value, index, self) => { + return self.indexOf(value) === index + }) const payload = { zone: validBidRequests.map(b => { return parseInt(b.params.zone) @@ -44,7 +52,7 @@ export const spec = { payload.gdpr_consent = { consent_string: bidderRequest.gdprConsent.consentString, consent_required: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - }; + } } payload.version = { @@ -54,6 +62,8 @@ export const spec = { const payloadString = JSON.stringify(payload) + LAST_PAYLOAD = payload + return { method: 'POST', url: ENDPOINT_URL + '?i=' + (+new Date()), @@ -97,10 +107,51 @@ export const spec = { pixels.push({ type: 'iframe', url: url - }); + }) } return pixels + }, + + onTimeout: (timeoutData) => { + timeoutData.forEach((data) => { + if (AD_UNIT_IDS.indexOf(data.adUnitCode) != -1) { + track(data, LAST_PAYLOAD, 'btm') + } + }) + }, + +} + +function track (data, payload, type) { + let pubUrl = '' + + let jp = { + auc: data.adUnitCode, + to: data.timeout } + + if (window.top == window) { + pubUrl = window.location.href + } else { + try { + pubUrl = window.top.location.href + } catch (e) { + pubUrl = document.referrer + } + } + + let duration = Date.now() - TRACK_START_TIME + + const pixelUrl = `${getTopWindowLocation().protocol}//emea-v3.tracking.justpremium.com/tracking.gif?rid=&sid=&uid=&vr=& +ru=${encodeURIComponent(pubUrl)}&tt=&siw=&sh=${payload.sh}&sw=${payload.sw}&wh=${payload.wh}&ww=${payload.ww}&an=&vn=& +sd=&_c=&et=&aid=&said=&ei=&fc=&sp=&at=bidder&cid=&ist=&mg=&dl=&dlt=&ev=&vt=&zid=${payload.id}&dr=${duration}&di=&pr=& +cw=&ch=&nt=&st=&jp=${encodeURIComponent(JSON.stringify(jp))}&ty=${type}` + + let img = document.createElement('img') + img.src = pixelUrl + img.id = 'jp-pixel-track' + img.style.cssText = 'display:none !important;' + document.body.appendChild(img) } function findBid (params, bids) { diff --git a/modules/kargoAnalyticsAdapter.js b/modules/kargoAnalyticsAdapter.js new file mode 100644 index 000000000000..6bb77d926b51 --- /dev/null +++ b/modules/kargoAnalyticsAdapter.js @@ -0,0 +1,14 @@ +import adapter from 'src/AnalyticsAdapter'; +import adaptermanager from 'src/adaptermanager'; + +var kargoAdapter = adapter({ + analyticsType: 'endpoint', + url: 'https://krk.kargo.com/api/v1/event/prebid' +}); + +adaptermanager.registerAnalyticsAdapter({ + adapter: kargoAdapter, + code: 'kargo' +}); + +export default kargoAdapter; diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index dfcc3057ab72..1ba0f392d087 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -12,7 +12,10 @@ export const spec = { return !!bid.params.placementId; }, buildRequests: function(validBidRequests, bidderRequest) { - const currency = config.getConfig('currency'); + const currencyObj = config.getConfig('currency'); + const currency = (currencyObj && currencyObj.adServerCurrency) || 'USD'; + const bidIds = {}; + utils._each(validBidRequests, bid => bidIds[bid.bidId] = bid.params.placementId); const transformedParams = Object.assign({}, { timeout: bidderRequest.timeout, currency: currency, @@ -22,31 +25,29 @@ export const spec = { floor: 0, ceil: 20 }, - adSlotIDs: utils._map(validBidRequests, bid => bid.params.placementId) + bidIDs: bidIds }, spec._getAllMetadata()); const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); return Object.assign({}, bidderRequest, { method: 'GET', - url: `${HOST}/api/v1/bid`, + url: `${HOST}/api/v2/bid`, data: `json=${encodedParams}`, currency: currency }); }, interpretResponse: function(response, bidRequest) { - let adUnits = response.body; - let bids = {}; - utils._each(bidRequest.bids, bid => bids[bid.params.placementId] = bid); + let bids = response.body; const bidResponses = []; - for (let adUnitId in adUnits) { - let adUnit = adUnits[adUnitId]; + for (let bidId in bids) { + let adUnit = bids[bidId]; bidResponses.push({ - requestId: bids[adUnitId].bidId, + requestId: bidId, cpm: Number(adUnit.cpm), width: adUnit.width, height: adUnit.height, ad: adUnit.adm, ttl: 300, - creativeId: adUnitId, + creativeId: adUnit.id, netRevenue: true, currency: bidRequest.currency }); diff --git a/modules/lockerdomeBidAdapter.js b/modules/lockerdomeBidAdapter.js index 43c8a15c37bd..d4b4e048b8bb 100644 --- a/modules/lockerdomeBidAdapter.js +++ b/modules/lockerdomeBidAdapter.js @@ -12,6 +12,7 @@ export const spec = { const adUnitBidRequests = bidRequests.map(function (bid) { return { requestId: bid.bidId, + adUnitCode: bid.adUnitCode, adUnitId: utils.getBidIdParameter('adUnitId', bid.params), sizes: bid.sizes } diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 4ff7bfd000ea..07c721371bd2 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -122,7 +122,8 @@ function slotParams(bidRequest) { let params = { id: bidRequest.bidId, ext: { - dfp_id: bidRequest.adUnitCode + dfp_id: bidRequest.adUnitCode, + display_count: bidRequest.bidRequestsCount }, banner: transformSizes(bidRequest.sizes), all: bidRequest.params diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index 38367837faf7..5b1fd999ee64 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -36,14 +36,17 @@ export const spec = { * Make a server request from the list of BidRequests. * * @param {validBidRequests[]} - an array of bids + * @param bidderRequest * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(bids) { + buildRequests: function(bids, bidRequest) { + let consentData = bidRequest ? bidRequest.gdprConsent : null; + return bids.map(bid => { return { method: 'POST', url: location.protocol + spec.ENDPOINT + bid.params.pubId, - data: getRequestData(bid), + data: getRequestData(bid, consentData), options: {contentType: 'application/json'}, bidRequest: bid } @@ -127,7 +130,11 @@ function getSize(sizes) { }; } -function getRequestData(bid) { +function isConsentRequired(consentData) { + return !!(consentData && consentData.gdprApplies); +} + +function getRequestData(bid, consentData) { let loc = utils.getTopWindowLocation(); let global = (window.top) ? window.top : window; let page = (bid.params.site && bid.params.site.page) ? (bid.params.site.page) : (loc.href); @@ -179,6 +186,23 @@ function getRequestData(bid) { if (bid.params.site && bid.params.site.id) { bidData.site.id = bid.params.site.id } + + if (isConsentRequired(consentData)) { + bidData.regs = { + ext: { + gdpr: 1 + } + }; + + if (consentData.consentString) { + bidData.user = { + ext: { + consent: consentData.consentString + } + }; + } + } + return bidData; } diff --git a/modules/prebidServerBidAdapter/config.js b/modules/prebidServerBidAdapter/config.js index a2b0b6bec776..74eb4a95744f 100644 --- a/modules/prebidServerBidAdapter/config.js +++ b/modules/prebidServerBidAdapter/config.js @@ -12,7 +12,7 @@ export const S2S_VENDORS = { adapter: 'prebidServer', cookieSet: false, enabled: true, - endpoint: '//prebid-server.rubiconproject.com/auction', + endpoint: '//prebid-server.rubiconproject.com/openrtb2/auction', syncEndpoint: '//prebid-server.rubiconproject.com/cookie_sync', timeout: 500 } diff --git a/modules/somoaudienceBidAdapter.js b/modules/somoaudienceBidAdapter.js index 7655eb9e2e0f..836d87417ccf 100644 --- a/modules/somoaudienceBidAdapter.js +++ b/modules/somoaudienceBidAdapter.js @@ -1,19 +1,30 @@ -import {getTopWindowReferrer, getTopWindowLocation} from 'src/utils'; +import * as utils from 'src/utils'; import { registerBidder } from 'src/adapters/bidderFactory'; +import includes from 'core-js/library/fn/array/includes'; +import {BANNER, VIDEO} from 'src/mediaTypes'; + +const VIDEO_TARGETING = ['mimes', 'minduration', 'maxduration', 'protocols', + 'startdelay', 'linearity', 'skip', 'delivery', + 'pos', 'api', 'ext', 'battr']; +const BANNER_TARGETING = ['battr', 'btype', 'pos', 'mimes', 'ext']; + +const SITE_TARGETING = ['name', 'domain', 'cat', 'keywords', 'content'] +const APP_TARGETING = ['name', 'bundle', 'domain', 'storeUrl', 'cat', 'ver', 'keywords', 'content'] export const spec = { code: 'somoaudience', + supportedMediaTypes: [BANNER, VIDEO], aliases: ['somo'], isBidRequestValid: bid => ( !!(bid && bid.params && bid.params.placementId) ), - buildRequests: function(bidRequests) { + buildRequests: function(bidRequests, bidderRequest) { return bidRequests.map(bidRequest => { - let da = openRtbRequest(bidRequest); + let da = openRtbRequest(bidRequest, bidderRequest); if (window.top1 && window.top1.realvu_aa) { let a = window.top1.realvu_aa.check({ unit_id: bidRequest.adUnitCode, @@ -48,51 +59,135 @@ export const spec = { interpretResponse(response, request) { return bidResponseAvailable(request, response); + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent) => { + const syncs = []; + var url = '//publisher-east.mobileadtrading.com/usersync'; + + if (syncOptions.pixelEnabled) { + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + // add 'gdpr' only if 'gdprApplies' is defined + if (typeof gdprConsent.gdprApplies === 'boolean') { + url += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } + } + syncs.push({ + type: 'image', + url: url + }); + } + return syncs; } }; function bidResponseAvailable(bidRequest, bidResponse) { let bidResponses = []; - let bidId = 1; - if (typeof bidRequest != 'undefined' && typeof bidRequest.bidRequest != 'undefined' && typeof bidRequest.bidRequest.bidId != 'undefined') { - bidId = bidRequest.bidRequest.bidId; - } if (bidResponse.body) { let bidData = bidResponse.body.seatbid[0].bid[0]; const bid = { - requestId: bidId, + requestId: bidData.impid, cpm: bidData.price, width: bidData.w, height: bidData.h, ad: bidData.adm, ttl: 360, creativeId: bidData.crid, - adId: bidId, + adId: bidData.impid, netRevenue: false, currency: 'USD', }; + if (isVideo(bidRequest.bidRequest)) { + bid.vastXml = bidData.adm; + bid.mediaType = 'video'; + } else { + bid.ad = bidData.adm; + bid.mediaType = 'banner'; + } bidResponses.push(bid); } return bidResponses; } -function openRtbRequest(bidRequest) { - return { +function openRtbRequest(bidRequest, bidderRequest) { + var openRtbRequest = { id: bidRequest.bidderRequestId, imp: [openRtbImpression(bidRequest)], at: 1, tmax: 400, site: openRtbSite(bidRequest), app: openRtbApp(bidRequest), - device: openRtbDevice() + device: openRtbDevice(), + bcat: openRtbBCat(bidRequest), + badv: openRtbBAdv(bidRequest), + ext: { + prebid: '$prebid.version$', + }, }; + if (bidderRequest != undefined) { + openRtbRequest = populateOpenRtbGdpr(bidderRequest.gdprConsent, openRtbRequest); + } + + return openRtbRequest; +} + +function populateOpenRtbGdpr(gdpr, bidRequest) { + if (gdpr && bidRequest && 'gdprApplies' in gdpr) { + if (!('reqs' in bidRequest)) { + bidRequest.reqs = {}; + } + if (!('ext' in bidRequest.reqs)) { + bidRequest.reqs.ext = {}; + } + bidRequest.reqs.ext.gdpr = gdpr.gdprApplies; + + if ('consentString' in gdpr) { + if (!('user' in bidRequest)) { + bidRequest.user = {}; + } + if (!('ext' in bidRequest.user)) { + bidRequest.user.ext = {}; + } + bidRequest.user.ext.consent = gdpr.consentString; + } + } + + return bidRequest; } function openRtbImpression(bidRequest) { - return { - id: bidRequest.bidId, - banner: {} + const imp = { + 'id': bidRequest.bidId, + bidfloor: bidRequest.params.bidfloor || 0, }; + if (isVideo(bidRequest)) { + imp.video = {}; + if (bidRequest.sizes) { + const sizes = getSizes(bidRequest.sizes); + imp.video.w = sizes[0]; + imp.video.h = sizes[1]; + } + if (bidRequest.params.video) { + Object.keys(bidRequest.params.video) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => imp.video[param] = bidRequest.params.video[param]); + } + } else { + imp.banner = { + topframe: 0 + }; + if (bidRequest.sizes) { + const sizes = getSizes(bidRequest.sizes); + imp.banner.w = sizes[0]; + imp.banner.h = sizes[1]; + } + if (bidRequest.params.banner) { + Object.keys(bidRequest.params.banner) + .filter(param => includes(BANNER_TARGETING, param)) + .forEach(param => imp.banner[param] = bidRequest.params.banner[param]); + } + } + return imp; } function isApp(bidRequest) { @@ -105,13 +200,20 @@ function isApp(bidRequest) { function openRtbSite(bidRequest) { if (!isApp(bidRequest)) { - const pageUrl = getTopWindowLocation().href; - const domain = getTopWindowLocation().hostname; - return { - ref: getTopWindowReferrer(), - page: pageUrl, - domain: domain + const site = { + ref: utils.getTopWindowReferrer(), + page: utils.getTopWindowLocation().href + }; + if (bidRequest.params.site) { + Object.keys(bidRequest.params.site) + .filter(param => includes(SITE_TARGETING, param)) + .forEach(param => site[param] = bidRequest.params.site[param]); + } + if (site.domain == undefined) { + site.domain = utils.getTopWindowLocation().hostname; } + + return site; } else { return null; } @@ -119,13 +221,14 @@ function openRtbSite(bidRequest) { function openRtbApp(bidRequest) { if (isApp(bidRequest)) { - const appParams = bidRequest.params.app; - return { - bundle: appParams.bundle ? appParams.bundle : null, - storeurl: appParams.storeUrl ? appParams.storeUrl : null, - domain: appParams.domain ? appParams.domain : null, - name: appParams.name ? appParams.name : null, + const app = { + } + Object.keys(bidRequest.params.app) + .filter(param => includes(APP_TARGETING, param)) + .forEach(param => app[param] = bidRequest.params.app[param]); + + return app; } else { return null; } @@ -133,9 +236,47 @@ function openRtbApp(bidRequest) { function openRtbDevice() { return { + ip: 'check', ua: navigator.userAgent, language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), }; } +function openRtbBCat(bidRequest) { + if (utils.isArray(bidRequest.params.bcat)) { + return bidRequest.params.bcat; + } + return []; +} + +function openRtbBAdv(bidRequest) { + if (utils.isArray(bidRequest.params.badv)) { + return bidRequest.params.badv; + } + return []; +} + +function isVideo (format) { + return utils.deepAccess(format, 'mediaTypes.video') || format.mediaType == 'video'; +} + +/* Turn bid request sizes into compatible format */ +function getSizes(requestSizes) { + let width = 0; + let height = 0; + if (utils.isArray(requestSizes) && requestSizes.length === 2 && + !utils.isArray(requestSizes[0])) { + width = parseInt(requestSizes[0], 10); + height = parseInt(requestSizes[1], 10); + } else if (typeof requestSizes === 'object') { + for (let i = 0; i < requestSizes.length; i++) { + let size = requestSizes[i]; + width = parseInt(size[0], 10); + height = parseInt(size[1], 10); + break; + } + } + return [width, height]; +} + registerBidder(spec); diff --git a/modules/somoaudienceBidAdapter.md b/modules/somoaudienceBidAdapter.md index a622d73d84bd..10af6023cb54 100644 --- a/modules/somoaudienceBidAdapter.md +++ b/modules/somoaudienceBidAdapter.md @@ -6,6 +6,8 @@ # Description Connects to Somo Audience demand source. Please use ```somoaudience``` as the bidder code. + +For video integration, somoAudience returns content as vastXML and requires the publisher to define the cache url in config passed to Prebid for it to be valid in the auction # Test Site Parameters ``` var adUnits = [{ diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 3d9ad2ce976b..779736520cc0 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -66,6 +66,9 @@ export const spec = { if (validBidRequests[0].params.referrer) { payload.ref = validBidRequests[0].params.referrer; } + if (validBidRequests[0].params.render) { + payload.render = validBidRequests[0].params.render; + } // Apply GDPR parameters to request. if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 3688aa3b9762..20e000e3379e 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -60,19 +60,24 @@ export const spec = { r: reqId }; - if (bidderRequest && bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + if (bidderRequest) { + if (bidderRequest.timeout) { + payload.wtimeout = bidderRequest.timeout; + } + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + payload.gdpr_applies = + (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; } return { method: 'GET', url: ENDPOINT_URL, - data: payload, + data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), bidsMap: bidsMap, }; }, @@ -84,7 +89,7 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest) { - serverResponse = serverResponse && serverResponse.body + serverResponse = serverResponse && serverResponse.body; const bidResponses = []; const bidsMap = bidRequest.bidsMap; const priceType = bidRequest.data.pt; diff --git a/modules/undertoneBidAdapter.js b/modules/undertoneBidAdapter.js index d7e063b85f55..dd99df1fc7e2 100644 --- a/modules/undertoneBidAdapter.js +++ b/modules/undertoneBidAdapter.js @@ -70,7 +70,7 @@ export const spec = { creativeId: bidRes.adId, currency: bidRes.currency, netRevenue: bidRes.netRevenue, - ttl: bidRes.ttl, + ttl: bidRes.ttl || 360, ad: bidRes.ad }; bids.push(bid); diff --git a/package.json b/package.json index 3aa37c517c75..18b21b57e867 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "1.19.0", + "version": "1.22.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { @@ -60,6 +60,7 @@ "gulp-uglify": "^3.0.0", "gulp-util": "^3.0.0", "ignore-loader": "^0.1.2", + "is-docker": "^1.1.0", "istanbul": "^0.4.5", "istanbul-instrumenter-loader": "^3.0.0", "json-loader": "^0.5.1", diff --git a/src/adUnits.js b/src/adUnits.js new file mode 100644 index 000000000000..bbdc82b60736 --- /dev/null +++ b/src/adUnits.js @@ -0,0 +1,34 @@ +import { deepAccess } from './utils'; + +let adUnits = {}; + +/** + * Increments and returns current Adunit counter + * @param {string} adunit id + * @returns {number} current adunit count + */ +function incrementCounter(adunit) { + adUnits[adunit] = adUnits[adunit] || {}; + adUnits[adunit].counter = (deepAccess(adUnits, `${adunit}.counter`) + 1) || 1; + return adUnits[adunit].counter; +} + +/** + * Returns current Adunit counter + * @param {string} adunit id + * @returns {number} current adunit count + */ +function getCounter(adunit) { + return deepAccess(adUnits, `${adunit}.counter`) || 0; +} + +/** + * A module which counts how many times an adunit was called + * @module adunitCounter + */ +let adunitCounter = { + incrementCounter, + getCounter +} + +export { adunitCounter }; diff --git a/src/adaptermanager.js b/src/adaptermanager.js index 5f0ac5b5656d..009ba18347bb 100644 --- a/src/adaptermanager.js +++ b/src/adaptermanager.js @@ -8,6 +8,7 @@ import { ajaxBuilder } from 'src/ajax'; import { config, RANDOM } from 'src/config'; import includes from 'core-js/library/fn/array/includes'; import find from 'core-js/library/fn/array/find'; +import { adunitCounter } from './adUnits'; var utils = require('./utils.js'); var CONSTANTS = require('./constants.json'); @@ -95,7 +96,8 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels}) { sizes: sizes, bidId: bid.bid_id || utils.getUniqueIdentifierStr(), bidderRequestId, - auctionId + auctionId, + bidRequestsCount: adunitCounter.getCounter(adUnit.code) })); } return bids; diff --git a/src/prebid.js b/src/prebid.js index 2539697dda13..c66dfb3d642d 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -1,16 +1,17 @@ /** @module pbjs */ import { getGlobal } from './prebidGlobal'; -import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, removeRequestId, getLatestHighestCpmBid } from './utils'; +import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, removeRequestId } from './utils'; import { listenMessagesFromCreative } from './secureCreatives'; import { userSync } from 'src/userSync.js'; import { loadScript } from './adloader'; import { config } from './config'; import { auctionManager } from './auctionManager'; -import { targeting, getHighestCpmBidsFromBidPool, RENDERED, BID_TARGETING_SET } from './targeting'; +import { targeting, RENDERED, BID_TARGETING_SET } from './targeting'; import { createHook } from 'src/hook'; import { sessionLoader } from 'src/debugging'; import includes from 'core-js/library/fn/array/includes'; +import { adunitCounter } from './adUnits'; const $$PREBID_GLOBAL$$ = getGlobal(); const CONSTANTS = require('./constants.json'); @@ -367,6 +368,7 @@ $$PREBID_GLOBAL$$.requestBids = createHook('asyncSeries', function ({ bidsBackHa adUnit.bids = adUnit.bids.filter(bid => bid.bidder !== bidder); } }); + adunitCounter.incrementCounter(adUnit.code); }); if (!adUnits || adUnits.length === 0) { @@ -598,8 +600,7 @@ $$PREBID_GLOBAL$$.getAllPrebidWinningBids = function () { * @return {Array} array containing highest cpm bid object(s) */ $$PREBID_GLOBAL$$.getHighestCpmBids = function (adUnitCode) { - let bidsReceived = getHighestCpmBidsFromBidPool(auctionManager.getBidsReceived(), getLatestHighestCpmBid); - return targeting.getWinningBids(adUnitCode, bidsReceived) + return targeting.getWinningBids(adUnitCode) .map(removeRequestId); }; diff --git a/src/secureCreatives.js b/src/secureCreatives.js index 424d1402831f..2204a6e7643b 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -69,10 +69,14 @@ function sendAdToCreative(adObject, remoteDomain, source) { } function resizeRemoteCreative({ adUnitCode, width, height }) { - const iframe = document.getElementById( - find(window.googletag.pubads().getSlots().filter(isSlotMatchingAdUnitCode(adUnitCode)), slot => slot) - .getSlotElementId()).querySelector('iframe'); - - iframe.width = '' + width; - iframe.height = '' + height; + // resize both container div + iframe + ['div', 'iframe'].forEach(elmType => { + let elementStyle = getElementByAdUnit(elmType).style; + elementStyle.width = width; + elementStyle.height = height; + }); + function getElementByAdUnit(elmType) { + return document.getElementById(find(window.googletag.pubads().getSlots().filter(isSlotMatchingAdUnitCode(adUnitCode)), slot => slot) + .getSlotElementId()).querySelector(elmType); + } } diff --git a/src/utils.js b/src/utils.js index ac7f035d652f..db50745ebcc5 100644 --- a/src/utils.js +++ b/src/utils.js @@ -14,13 +14,11 @@ var tNumb = 'Number'; var tObject = 'Object'; var tBoolean = 'Boolean'; var toString = Object.prototype.toString; -let infoLogger = null; -let warnLogger = null; -try { - infoLogger = console.info.bind(window.console); - warnLogger = console.warn.bind(window.console); -} catch (e) { -} +let consoleExists = Boolean(window.console); +let consoleLogExists = Boolean(consoleExists && window.console.log); +let consoleInfoExists = Boolean(consoleExists && window.console.info); +let consoleWarnExists = Boolean(consoleExists && window.console.warn); +let consoleErrorExists = Boolean(consoleExists && window.console.error); /* * Substitutes into a string from a given map using the token @@ -260,42 +258,43 @@ exports.getTopWindowReferrer = function() { } }; -exports.logWarn = function (msg, args) { - if (debugTurnedOn() && console.warn) { - if (warnLogger) { - if (!args || args.length === 0) { - args = ''; - } - - warnLogger('WARNING: ' + msg + ((args === '') ? '' : ' : params : '), args); - } +/** + * Wrappers to console.(log | info | warn | error). Takes N arguments, the same as the native methods + */ +exports.logMessage = function () { + if (debugTurnedOn() && consoleLogExists) { + console.log.apply(console, decorateLog(arguments, 'MESSAGE:')); } }; -exports.logInfo = function (msg, args) { - if (debugTurnedOn() && hasConsoleLogger()) { - if (infoLogger) { - if (!args || args.length === 0) { - args = ''; - } +exports.logInfo = function () { + if (debugTurnedOn() && consoleInfoExists) { + console.info.apply(console, decorateLog(arguments, 'INFO:')); + } +}; - infoLogger('INFO: ' + msg + ((args === '') ? '' : ' : params : '), args); - } +exports.logWarn = function () { + if (debugTurnedOn() && consoleWarnExists) { + console.warn.apply(console, decorateLog(arguments, 'WARNING:')); } }; -exports.logMessage = function (msg) { - if (debugTurnedOn() && hasConsoleLogger()) { - console.log('MESSAGE: ' + msg); +exports.logError = function () { + if (debugTurnedOn() && consoleErrorExists) { + console.error.apply(console, decorateLog(arguments, 'ERROR:')); } }; -function hasConsoleLogger() { - return (window.console && window.console.log); +function decorateLog(args, prefix) { + args = [].slice.call(args); + prefix && args.unshift(prefix); + args.unshift('display: inline-block; color: #fff; background: #3b88c3; padding: 1px 4px; border-radius: 3px;'); + args.unshift('%cPrebid'); + return args; } -function hasConsoleError() { - return (window.console && window.console.error); +function hasConsoleLogger() { + return consoleLogExists; } exports.hasConsoleLogger = hasConsoleLogger; @@ -312,15 +311,6 @@ var debugTurnedOn = function () { exports.debugTurnedOn = debugTurnedOn; -/** - * Wrapper to console.error. Takes N arguments to log the same as console.error. - */ -exports.logError = function () { - if (debugTurnedOn() && hasConsoleError()) { - console.error.apply(console, arguments); - } -}; - exports.createInvisibleIframe = function _createInvisibleIframe() { var f = document.createElement('iframe'); f.id = _getUniqueIdentifierStr(); diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 8ca2f3da902b..2779209c1cf3 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -40,6 +40,7 @@ describe('33acrossBidAdapter:', function () { afterEach(function() { this.sandbox.restore(); + delete this.bidRequests; }); describe('isBidRequestValid:', function () { @@ -112,150 +113,254 @@ describe('33acrossBidAdapter:', function () { }); describe('buildRequests:', function() { - it('returns corresponding server requests for each valid bidRequest', function() { - const ttxRequest = { - imp: [ { - banner: { - format: [ - { - w: 300, - h: 250, - ext: {} - }, - { - w: 728, - h: 90, - ext: {} + context('when gdpr consent data exists', function() { + beforeEach(function() { + this.bidderRequest = { + gdprConsent: { + consentString: 'foobarMyPreference', + gdprApplies: true + } + } + }); + + it('returns corresponding server requests with gdpr consent data', function() { + const ttxRequest = { + imp: [ { + banner: { + format: [ + { + w: 300, + h: 250, + ext: {} + }, + { + w: 728, + h: 90, + ext: {} + } + ] + }, + ext: { + ttx: { + prod: PRODUCT_ID } - ] + } + } ], + site: { + id: SITE_ID }, - ext: { - ttx: { - prod: PRODUCT_ID + id: 'b1', + user: { + ext: { + consent: 'foobarMyPreference' + } + }, + regs: { + ext: { + gdpr: 1 } } - } ], - site: { - id: SITE_ID - }, - id: 'b1' - }; - const serverRequest = { - 'method': 'POST', - 'url': END_POINT, - 'data': JSON.stringify(ttxRequest), - 'options': { - 'contentType': 'text/plain', - 'withCredentials': true - } - } - const builtServerRequests = buildRequests(this.bidRequests); - expect(builtServerRequests).to.deep.equal([ serverRequest ]); - expect(builtServerRequests.length).to.equal(1); - }); + }; - it('returns corresponding test server requests for each valid bidRequest', function() { - this.sandbox.stub(config, 'getConfig').callsFake(() => { - return { - 'url': 'https://foo.com/hb/' + const serverRequest = { + 'method': 'POST', + 'url': END_POINT, + 'data': JSON.stringify(ttxRequest), + 'options': { + 'contentType': 'text/plain', + 'withCredentials': true + } } + const builtServerRequests = buildRequests(this.bidRequests, this.bidderRequest); + expect(builtServerRequests).to.deep.equal([ serverRequest ]); + expect(builtServerRequests.length).to.equal(1); }); - const ttxRequest = { - imp: [ { - banner: { - format: [ - { - w: 300, - h: 250, - ext: { } - }, - { - w: 728, - h: 90, - ext: { } + it('returns corresponding test server requests with gdpr consent data', function() { + this.sandbox.stub(config, 'getConfig').callsFake(() => { + return { + 'url': 'https://foo.com/hb/' + } + }); + + const ttxRequest = { + imp: [ { + banner: { + format: [ + { + w: 300, + h: 250, + ext: { } + }, + { + w: 728, + h: 90, + ext: { } + } + ] + }, + ext: { + ttx: { + prod: PRODUCT_ID } - ] + } + } ], + site: { + id: SITE_ID }, - ext: { - ttx: { - prod: PRODUCT_ID + id: 'b1', + user: { + ext: { + consent: 'foobarMyPreference' + } + }, + regs: { + ext: { + gdpr: 1 } } - } ], - site: { - id: SITE_ID - }, - id: 'b1' - }; - const serverRequest = { - method: 'POST', - url: 'https://foo.com/hb/', - data: JSON.stringify(ttxRequest), - options: { - contentType: 'text/plain', - withCredentials: true - } - }; + }; + const serverRequest = { + method: 'POST', + url: 'https://foo.com/hb/', + data: JSON.stringify(ttxRequest), + options: { + contentType: 'text/plain', + withCredentials: true + } + }; + + const builtServerRequests = buildRequests(this.bidRequests, this.bidderRequest); + expect(builtServerRequests).to.deep.equal([ serverRequest ]); + expect(builtServerRequests.length).to.equal(1); + }); - const builtServerRequests = buildRequests(this.bidRequests); - expect(builtServerRequests).to.deep.equal([ serverRequest ]); - expect(builtServerRequests.length).to.equal(1); + afterEach(function() { + delete this.bidderRequest; + }) }); - it('returns corresponding test server requests for each valid bidRequest', function() { - this.sandbox.stub(config, 'getConfig').callsFake(() => { - return { - 'url': 'https://foo.com/hb/' - } + context('when gdpr consent data does not exist', function() { + beforeEach(function() { + this.bidderRequest = { } }); - this.bidRequests[0].params.test = 1; - const ttxRequest = { - imp: [ { - banner: { - format: [ - { - w: 300, - h: 250, - ext: { } - }, - { - w: 728, - h: 90, - ext: { } + + it('returns corresponding server requests with default gdpr consent data', function() { + const ttxRequest = { + imp: [ { + banner: { + format: [ + { + w: 300, + h: 250, + ext: {} + }, + { + w: 728, + h: 90, + ext: {} + } + ] + }, + ext: { + ttx: { + prod: PRODUCT_ID } - ] + } + } ], + site: { + id: SITE_ID }, - ext: { - ttx: { - prod: PRODUCT_ID + id: 'b1', + user: { + ext: { + consent: undefined + } + }, + regs: { + ext: { + gdpr: 0 } } - } ], - site: { - id: SITE_ID - }, - id: 'b1', - test: 1 - }; - const serverRequest = { - method: 'POST', - url: 'https://foo.com/hb/', - data: JSON.stringify(ttxRequest), - options: { - contentType: 'text/plain', - withCredentials: true + }; + + const serverRequest = { + 'method': 'POST', + 'url': END_POINT, + 'data': JSON.stringify(ttxRequest), + 'options': { + 'contentType': 'text/plain', + 'withCredentials': true + } } - }; + const builtServerRequests = buildRequests(this.bidRequests, this.bidderRequest); + expect(builtServerRequests).to.deep.equal([ serverRequest ]); + expect(builtServerRequests.length).to.equal(1); + }); - const builtServerRequests = buildRequests(this.bidRequests); - expect(builtServerRequests).to.deep.equal([ serverRequest ]); - expect(builtServerRequests.length).to.equal(1); - }); + it('returns corresponding test server requests with default gdpr consent data', function() { + this.sandbox.stub(config, 'getConfig').callsFake(() => { + return { + 'url': 'https://foo.com/hb/' + } + }); + + const ttxRequest = { + imp: [ { + banner: { + format: [ + { + w: 300, + h: 250, + ext: { } + }, + { + w: 728, + h: 90, + ext: { } + } + ] + }, + ext: { + ttx: { + prod: PRODUCT_ID + } + } + } ], + site: { + id: SITE_ID + }, + id: 'b1', + user: { + ext: { + consent: undefined + } + }, + regs: { + ext: { + gdpr: 0 + } + } + }; + const serverRequest = { + method: 'POST', + url: 'https://foo.com/hb/', + data: JSON.stringify(ttxRequest), + options: { + contentType: 'text/plain', + withCredentials: true + } + }; - afterEach(function() { - delete this.bidRequests; - }) + const builtServerRequests = buildRequests(this.bidRequests, this.bidderRequest); + expect(builtServerRequests).to.deep.equal([ serverRequest ]); + expect(builtServerRequests.length).to.equal(1); + }); + + afterEach(function() { + delete this.bidderRequest; + }) + }); }); describe('interpretResponse', function() { @@ -452,23 +557,63 @@ describe('33acrossBidAdapter:', function () { ]; }); - context('when iframe is not enabled', function() { - it('returns empty sync array', function() { - const syncOptions = {}; - buildRequests(this.bidRequests); - expect(getUserSyncs(syncOptions)).to.deep.equal([]); + context('when gdpr does not apply', function() { + beforeEach(function() { + this.gdprConsent = { + gdprApplies: false + } + }); + + context('when iframe is not enabled', function() { + it('returns empty sync array', function() { + const syncOptions = {}; + buildRequests(this.bidRequests); + expect(getUserSyncs(syncOptions, {}, this.gdprConsent)).to.deep.equal([]); + }); + }); + + context('when iframe is enabled', function() { + it('returns sync array equal to number of unique siteIDs', function() { + const syncOptions = { + iframeEnabled: true + }; + buildRequests(this.bidRequests); + const syncs = getUserSyncs(syncOptions, {}, this.gdprConsent); + expect(syncs).to.deep.equal(this.syncs); + }); }); }); - context('when iframe is enabled', function() { - it('returns sync array equal to number of unique siteIDs', function() { - const syncOptions = { - iframeEnabled: true - }; - buildRequests(this.bidRequests); - const syncs = getUserSyncs(syncOptions); - expect(syncs).to.deep.equal(this.syncs); + context('when consent data is not defined', function() { + context('when iframe is not enabled', function() { + it('returns empty sync array', function() { + const syncOptions = {}; + buildRequests(this.bidRequests); + expect(getUserSyncs(syncOptions)).to.deep.equal([]); + }); + }); + + context('when iframe is enabled', function() { + it('returns sync array equal to number of unique siteIDs', function() { + const syncOptions = { + iframeEnabled: true + }; + buildRequests(this.bidRequests); + const syncs = getUserSyncs(syncOptions); + expect(syncs).to.deep.equal(this.syncs); + }); }); }); + + context('when gdpr applies', function() { + it('returns empty sync array', function() { + const syncOptions = {}; + const gdprConsent = { + gdprApplies: true + } + buildRequests(this.bidRequests); + expect(getUserSyncs(syncOptions, {}, gdprConsent)).to.deep.equal([]); + }); + }) }); }); diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index 149b9eb4d535..39eb514752aa 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -57,23 +57,36 @@ describe('AdoceanAdapter', () => { } ]; + const bidderRequest = { + gdprConsent: { + consentString: 'BOQHk-4OSlWKFBoABBPLBd-AAAAgWAHAACAAsAPQBSACmgFTAOkA', + gdprApplies: true + } + }; + it('should add bidIdMap with slaveId => bidId mapping', () => { - const request = spec.buildRequests(bidRequests)[0]; + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.bidIdMap).to.exists; const bidIdMap = request.bidIdMap; expect(bidIdMap[bidRequests[0].params.slaveId]).to.equal(bidRequests[0].bidId); }); it('sends bid request to url via GET', () => { - const request = spec.buildRequests(bidRequests)[0]; + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.method).to.equal('GET'); expect(request.url).to.match(new RegExp(`^https://${bidRequests[0].params.emiter}/ad.json`)); }); it('should attach id to url', () => { - const request = spec.buildRequests(bidRequests)[0]; + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.url).to.include('id=' + bidRequests[0].params.masterId); }); + + it('should attach consent information to url', () => { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.url).to.include('gdpr=1'); + expect(request.url).to.include('gdpr_consent=' + bidderRequest.gdprConsent.consentString); + }); }) describe('interpretResponse', () => { diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js new file mode 100644 index 000000000000..c6a531c9f1f9 --- /dev/null +++ b/test/spec/modules/ajaBidAdapter_spec.js @@ -0,0 +1,144 @@ +import { spec } from 'modules/ajaBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//ad.as.amanad.adtdp.com/v2/prebid'; + +describe('AjaAdapter', () => { + const adapter = newBidder(spec); + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'aja', + 'params': { + 'asi': '123456' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'asi': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': 'aja', + 'params': { + 'asi': '123456' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('sends bid request to ENDPOINT via GET', () => { + const requests = spec.buildRequests(bidRequests); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].method).to.equal('GET'); + }); + }); + describe('interpretResponse', () => { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 1, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'USD', + 'creative_id': '123abc', + 'banner': { + 'w': 300, + 'h': 250, + 'tag': '
', + 'imps': [ + '//as.amanad.adtdp.com/v1/imp' + ] + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + it('should get correct banner bid response', () => { + let expectedResponse = [ + { + 'requestId': '51ef8751f9aead', + 'cpm': 12.34, + 'creativeId': '123abc', + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
', + 'mediaType': 'banner', + 'currency': 'USD', + 'ttl': 300, + 'netRevenue': true + } + ]; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles video responses', () => { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 3, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'JPY', + 'creative_id': '123abc', + 'video': { + 'w': 300, + 'h': 250, + 'vtag': '', + 'purl': 'http://cdn/player', + 'progress': true, + 'loop': false, + 'inread': false + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('renderer'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles nobid responses', () => { + let response = { + 'is_ad_return': false, + 'ad': {} + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index abfd50d17467..b65988e3ec13 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -326,6 +326,50 @@ describe('AppNexusAdapter', () => { expect(payload.gdpr_consent.consent_string).to.exist.and.to.equal(consentString); expect(payload.gdpr_consent.consent_required).to.exist.and.to.be.true; }); + + it('supports sending hybrid mobile app parameters', () => { + let appRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + app: { + id: 'B1O2W3M4AN.com.prebid.webview', + geo: { + lat: 40.0964439, + lng: -75.3009142 + }, + device_id: { + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', // Apple advertising identifier + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', // Android advertising identifier + md5udid: '5756ae9022b2ea1e47d84fead75220c8', // MD5 hash of the ANDROID_ID + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', // SHA1 hash of the ANDROID_ID + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' // Windows advertising identifier + } + } + } + } + ); + const request = spec.buildRequests([appRequest]); + const payload = JSON.parse(request.data); + expect(payload.app).to.exist; + expect(payload.app).to.deep.equal({ + appid: 'B1O2W3M4AN.com.prebid.webview' + }); + expect(payload.device.device_id).to.exist; + expect(payload.device.device_id).to.deep.equal({ + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', + md5udid: '5756ae9022b2ea1e47d84fead75220c8', + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' + }); + expect(payload.device.geo).to.exist; + expect(payload.device.geo).to.deep.equal({ + lat: 40.0964439, + lng: -75.3009142 + }); + }); }) describe('interpretResponse', () => { diff --git a/test/spec/modules/audienceNetworkBidAdapter_spec.js b/test/spec/modules/audienceNetworkBidAdapter_spec.js index c61cd04c422b..3e6a890e4dc5 100644 --- a/test/spec/modules/audienceNetworkBidAdapter_spec.js +++ b/test/spec/modules/audienceNetworkBidAdapter_spec.js @@ -19,7 +19,7 @@ const placementId = 'test-placement-id'; const playerwidth = 320; const playerheight = 180; const requestId = 'test-request-id'; -const debug = 'adapterver=1.0.0&platform=241394079772386&platver=$prebid.version$'; +const debug = 'adapterver=1.0.1&platform=241394079772386&platver=$prebid.version$'; const pageUrl = encodeURIComponent(utils.getTopWindowUrl()); describe('AudienceNetwork adapter', () => { @@ -182,13 +182,17 @@ describe('AudienceNetwork adapter', () => { }]); }); - it('can build URL for fullwidth 300x250 unit', () => { + it('can build URL for fullwidth 300x250 unit, overriding platform', () => { + const platform = 'test-platform'; + const debugPlatform = debug.replace('241394079772386', platform); + expect(buildRequests([{ bidder, bidId: requestId, sizes: [[300, 250]], params: { placementId, + platform, format: 'fullwidth' } }])).to.deep.equal([{ @@ -197,7 +201,7 @@ 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=${pageUrl}&sdk[]=5.5.web&${debug}` + data: `placementids[]=test-placement-id&adformats[]=fullwidth&testmode=false&pageurl=${pageUrl}&sdk[]=5.5.web&${debugPlatform}` }]); }); @@ -249,6 +253,7 @@ describe('AudienceNetwork adapter', () => { .to.contain(`placementid:'${placementId}',format:'native',bidid:'test-bid-id'`, 'ad missing parameters') .and.to.contain('getElementsByTagName("style")', 'ad missing native styles') .and.to.contain('
', 'ad missing native container'); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); @@ -287,6 +292,7 @@ describe('AudienceNetwork adapter', () => { .to.contain(`placementid:'${placementId}',format:'300x250',bidid:'test-bid-id'`, 'ad missing parameters') .and.not.to.contain('getElementsByTagName("style")', 'ad should not contain native styles') .and.not.to.contain('
', 'ad should not contain native container'); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); @@ -320,6 +326,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.requestId).to.equal(requestId); expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); @@ -364,6 +371,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseNative.width).to.equal(300); expect(bidResponseNative.height).to.equal(250); expect(bidResponseNative.ad).to.contain(`placementid:'${placementIdNative}',format:'native',bidid:'test-bid-id-native'`, 'ad missing parameters'); + expect(bidResponseNative.ttl).to.equal(600); expect(bidResponseNative.creativeId).to.equal(placementIdNative); expect(bidResponseNative.netRevenue).to.equal(true); expect(bidResponseNative.currency).to.equal('USD'); @@ -377,6 +385,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseIab.width).to.equal(300); expect(bidResponseIab.height).to.equal(250); expect(bidResponseIab.ad).to.contain(`placementid:'${placementIdIab}',format:'300x250',bidid:'test-bid-id-iab'`, 'ad missing parameters'); + expect(bidResponseIab.ttl).to.equal(600); expect(bidResponseIab.creativeId).to.equal(placementIdIab); expect(bidResponseIab.netRevenue).to.equal(true); expect(bidResponseIab.currency).to.equal('USD'); @@ -410,6 +419,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.cpm).to.equal(1.23); expect(bidResponse.requestId).to.equal(requestId); + expect(bidResponse.ttl).to.equal(3600); expect(bidResponse.mediaType).to.equal('video'); expect(bidResponse.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${placementId}&pageurl=${pageUrl}&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${bidId}`); expect(bidResponse.width).to.equal(playerwidth); @@ -450,6 +460,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseVideo.cpm).to.equal(1.23); expect(bidResponseVideo.requestId).to.equal(requestId); + expect(bidResponseVideo.ttl).to.equal(3600); expect(bidResponseVideo.mediaType).to.equal('video'); expect(bidResponseVideo.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${videoPlacementId}&pageurl=${pageUrl}&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${videoBidId}`); expect(bidResponseVideo.width).to.equal(playerwidth); @@ -457,6 +468,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseNative.cpm).to.equal(4.56); expect(bidResponseNative.requestId).to.equal(requestId); + expect(bidResponseNative.ttl).to.equal(600); expect(bidResponseNative.width).to.equal(300); expect(bidResponseNative.height).to.equal(250); expect(bidResponseNative.ad).to.contain(`placementid:'${nativePlacementId}',format:'native',bidid:'${nativeBidId}'`); @@ -490,6 +502,7 @@ describe('AudienceNetwork adapter', () => { .to.contain(`placementid:'${placementId}',format:'native',bidid:'test-bid-id'`, 'ad missing parameters') .and.to.contain('getElementsByTagName("style")', 'ad missing native styles') .and.to.contain('
', 'ad missing native container'); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index b87ce6634f65..45c56885c034 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -1,215 +1,269 @@ -import {expect} from 'chai'; -import * as utils from 'src/utils'; -import {spec} from 'modules/consumableBidAdapter'; -import {config} from 'src/config'; - -const DEFAULT_OAD_CONTENT = ''; -const DEFAULT_AD_CONTENT = '' - -let getDefaultBidResponse = () => { - return { - id: '245730051428950632', - cur: 'USD', - seatbid: [{ - bid: [{ - id: 1, - impid: '245730051428950632', - price: 0.09, - adm: DEFAULT_OAD_CONTENT, - crid: 'creative-id', - h: 90, - w: 728, - dealid: 'deal-id', - ext: {sizeid: 225} - }] - }] - }; -}; +import { expect } from 'chai'; +import { spec } from 'modules/consumableBidAdapter'; -let getBidParams = () => { - return { - placement: 1234567, - network: '9599.1', - unitId: '987654', - unitName: 'unitname', - zoneId: '9599.1' - }; -}; +var bidFactory = require('src/bidfactory.js'); -let getDefaultBidRequest = () => { - return { - bidderCode: 'consumable', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - bidderRequestId: '7101db09af0db2', - start: new Date().getTime(), - bids: [{ - bidder: 'consumable', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - placementCode: 'foo', - params: getBidParams() - }] - }; -}; +const ENDPOINT = 'https://e.serverbid.com/api/v2'; +const SMARTSYNC_CALLBACK = 'serverbidCallBids'; -let getPixels = () => { - return ''; +const REQUEST = { + 'bidderCode': 'consumable', + 'auctionId': 'a4713c32-3762-4798-b342-4ab810ca770d', + 'bidderRequestId': '109f2a181342a9', + 'bidRequest': [{ + 'bidder': 'consumable', + 'params': { + 'networkId': 9969, + 'siteId': 730181, + 'unitId': 123456, + 'unitName': 'cnsmbl-unit' + }, + 'placementCode': 'div-gpt-ad-1487778092495-0', + 'sizes': [ + [728, 90], + [970, 90] + ], + 'bidId': '2b0f82502298c9', + 'bidderRequestId': '109f2a181342a9', + 'auctionId': 'a4713c32-3762-4798-b342-4ab810ca770d' + }, + { + 'bidder': 'consumable', + 'params': { + 'networkId': 9969, + 'siteId': 730181, + 'unitId': 123456, + 'unitName': 'cnsmbl-unit' + }, + 'placementCode': 'div-gpt-ad-1487778092495-0', + 'sizes': [ + [728, 90], + [970, 90] + ], + 'bidId': '123', + 'bidderRequestId': '109f2a181342a9', + 'auctionId': 'a4713c32-3762-4798-b342-4ab810ca770d' + }], + 'start': 1487883186070, + 'auctionStart': 1487883186069, + 'timeout': 3000 }; -describe('ConsumableAdapter', () => { - const CONSUMABLE_URL = '//adserver-us.adtech.advertising.com/pubapi/3.0/'; - const CONSUMABLE_TTL = 60; - - function createCustomBidRequest({bids, params} = {}) { - var bidderRequest = getDefaultBidRequest(); - if (bids && Array.isArray(bids)) { - bidderRequest.bids = bids; - } - if (params) { - bidderRequest.bids.forEach(bid => bid.params = params); +const RESPONSE = { + 'headers': null, + 'body': { + 'user': { 'key': 'ue1-2d33e91b71e74929b4aeecc23f4376f1' }, + 'decisions': { + '2b0f82502298c9': { + 'adId': 2364764, + 'creativeId': 1950991, + 'flightId': 2788300, + 'campaignId': 542982, + 'clickUrl': 'https://e.serverbid.com/r', + 'impressionUrl': 'https://e.serverbid.com/i.gif', + 'contents': [{ + 'type': 'html', + 'body': '', + 'data': { + 'height': 90, + 'width': 728, + 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif', + 'fileName': 'b0ab77db8a7848c8b78931aed022a5ef.gif' + }, + 'template': 'image' + }], + 'height': 90, + 'width': 728, + 'events': [], + 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} + }, + '123': { + 'adId': 2364764, + 'creativeId': 1950991, + 'flightId': 2788300, + 'campaignId': 542982, + 'clickUrl': 'https://e.serverbid.com/r', + 'impressionUrl': 'https://e.serverbid.com/i.gif', + 'contents': [{ + 'type': 'html', + 'body': '', + 'data': { + 'height': 90, + 'width': 728, + 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif', + 'fileName': 'b0ab77db8a7848c8b78931aed022a5ef.gif' + }, + 'template': 'image' + }], + 'height': 90, + 'width': 728, + 'events': [], + 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} + } } - return bidderRequest; } +}; - describe('interpretResponse()', () => { - let bidderSettingsBackup; - let bidResponse; - let bidRequest; - let logWarnSpy; - - beforeEach(() => { - bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; - bidRequest = { - bidderCode: 'test-bidder-code', - bidId: 'bid-id', - unitName: 'unitname', - unitId: '987654', - zoneId: '9599.1', - network: '9599.1' - }; - bidResponse = { - body: getDefaultBidResponse() +describe('Consumable BidAdapter', () => { + let bidRequests; + let adapter = spec; + + beforeEach(() => { + bidRequests = [ + { + bidder: 'consumable', + params: { + networkId: '9969', + siteId: '730181', + unitId: '123456', + unitName: 'cnsmbl-unit' + }, + placementCode: 'header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + }); + + describe('bid request validation', () => { + it('should accept valid bid requests', () => { + let bid = { + bidder: 'consumable', + params: { + networkId: '9969', + siteId: '123', + unitId: '123456', + unitName: 'cnsmbl-unit' + } }; - logWarnSpy = sinon.spy(utils, 'logWarn'); + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - afterEach(() => { - $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; - logWarnSpy.restore(); + it('should accept valid bid requests with extra fields', () => { + let bid = { + bidder: 'consumable', + params: { + networkId: '9969', + siteId: '123', + unitId: '123456', + unitName: 'cnsmbl-unit', + zoneId: '123' + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return formatted bid response with required properties', () => { - let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); - expect(formattedBidResponse).to.deep.equal({ - bidderCode: bidRequest.bidderCode, - requestId: 'bid-id', - ad: DEFAULT_AD_CONTENT, - cpm: 0.09, - width: 728, - height: 90, - creativeId: 'creative-id', - pubapiId: '245730051428950632', - currency: 'USD', - dealId: 'deal-id', - netRevenue: true, - ttl: 60 - }); + it('should reject bid requests without siteId', () => { + let bid = { + bidder: 'consumable', + params: { + networkId: '9969', + unitId: '123456', + unitName: 'cnsmbl-unit' + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should add formatted pixels to ad content when pixels are present in the response', () => { - bidResponse.body.ext = { - pixels: 'pixels-content' + it('should reject bid requests without networkId', () => { + let bid = { + bidder: 'consumable', + params: { + siteId: '9969', + unitId: '123456', + unitName: 'cnsmbl-unit' + } }; - - let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); - - expect(formattedBidResponse.ad).to.equal(DEFAULT_AD_CONTENT + ''); - return true; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests()', () => { - it('method exists and is a function', () => { - expect(spec.buildRequests).to.exist.and.to.be.a('function'); + describe('buildRequests validation', () => { + it('creates request data', () => { + let request = spec.buildRequests(bidRequests); + + expect(request).to.exist.and.to.be.a('object'); }); - describe('Consumable', () => { - it('should not return request when no bids are present', () => { - let [request] = spec.buildRequests([]); - expect(request).to.be.empty; - }); + it('request to consumable should contain a url', () => { + let request = spec.buildRequests(bidRequests); - it('should return request for endpoint', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(CONSUMABLE_URL); - }); + expect(request.url).to.have.string('serverbid.com'); + }); - it('should return url with pubapi bid option', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('cmd=bid;'); - }); + it('requires valid bids to make request', () => { + let request = spec.buildRequests([]); + expect(request.bidRequest).to.be.empty; + }); - it('should return url with version 2 of pubapi', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('v=2;'); - }); + it('sends bid request to ENDPOINT via POST', () => { + let request = spec.buildRequests(bidRequests); - it('should return url with cache busting option', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.match(/misc=\d+/); - }); + expect(request.method).to.have.string('POST'); }); }); + describe('interpretResponse validation', () => { + it('response should have valid bidderCode', () => { + let bidRequest = spec.buildRequests(REQUEST.bidRequest); + let bid = bidFactory.createBid(1, bidRequest.bidRequest[0]); + + expect(bid.bidderCode).to.equal('consumable'); + }); - describe('getUserSyncs()', () => { - let bidResponse; - let bidRequest; + it('response should include objects for all bids', () => { + let bids = spec.interpretResponse(RESPONSE, REQUEST); + expect(bids.length).to.equal(2); + }); - beforeEach(() => { - $$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped = false; - config.setConfig({ - consumable: { - userSyncOn: 'bidResponse' - }, + it('registers bids', () => { + let bids = spec.interpretResponse(RESPONSE, REQUEST); + bids.forEach(b => { + expect(b).to.have.property('cpm'); + expect(b.cpm).to.be.above(0); + expect(b).to.have.property('requestId'); + expect(b).to.have.property('unitId'); + expect(b).to.have.property('unitName'); + expect(b).to.have.property('cpm'); + expect(b).to.have.property('width'); + expect(b).to.have.property('height'); + expect(b).to.have.property('ad'); + expect(b).to.have.property('currency', 'USD'); + expect(b).to.have.property('creativeId'); + expect(b).to.have.property('ttl', 360); + expect(b).to.have.property('netRevenue', true); + expect(b).to.have.property('referrer'); }); - bidResponse = getDefaultBidResponse(); - bidResponse.ext = { - pixels: getPixels() - }; }); - it('should return user syncs only if userSyncOn equals to "bidResponse"', () => { - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + it('handles nobid responses', () => { + let EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {'decisions': null}}) + let bids = spec.interpretResponse(EMPTY_RESP, REQUEST); - expect($$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped).to.be.true; - expect(userSyncs).to.deep.equal([ - {type: 'image', url: 'img.org'}, - {type: 'iframe', url: 'pixels1.org'} - ]); + expect(bids).to.be.empty; }); - it('should not return user syncs if it has already been returned', () => { - $$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped = true; + it('handles no server response', () => { + let bids = spec.interpretResponse(null, REQUEST); - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); - - expect($$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped).to.be.true; - expect(userSyncs).to.deep.equal([]); + expect(bids).to.be.empty; }); + }); + describe('getUserSyncs', () => { + let syncOptions = {'iframeEnabled': true}; - it('should not return user syncs if pixels are not present', () => { - bidResponse.ext.pixels = null; + it('handles empty sync options', () => { + let opts = spec.getUserSyncs({}); + + expect(opts).to.be.empty; + }); - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + it('should return a sync url if iframe syncs are enabled', () => { + let opts = spec.getUserSyncs(syncOptions); - expect($$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped).to.be.false; - expect(userSyncs).to.deep.equal([]); + expect(opts.length).to.equal(1); }); }); }); diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index a4ef643feced..74e2b8a3c384 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -62,7 +62,9 @@ describe('currency', function () { innerBid = bid; }); - expect(innerBid.currency).to.equal('GBP') + expect(innerBid.currency).to.equal('GBP'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('GBP')).to.equal('1.000'); }); it('uses adapter currency over currency override if specified', () => { @@ -82,7 +84,9 @@ describe('currency', function () { innerBid = bid; }); - expect(innerBid.currency).to.equal('JPY') + expect(innerBid.currency).to.equal('JPY'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('1.000'); }); it('uses rates specified in json when provided', () => { @@ -103,6 +107,8 @@ describe('currency', function () { }); expect(innerBid.cpm).to.equal('1.0000'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); }); it('uses default rates when currency file fails to load', () => { @@ -128,6 +134,8 @@ describe('currency', function () { }); expect(innerBid.cpm).to.equal('1.0000'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); }); }); diff --git a/test/spec/modules/divreachBidAdapter_spec.js b/test/spec/modules/divreachBidAdapter_spec.js new file mode 100644 index 000000000000..8beb0830385e --- /dev/null +++ b/test/spec/modules/divreachBidAdapter_spec.js @@ -0,0 +1,117 @@ +import {expect} from 'chai'; +import {spec} from 'modules/divreachBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'divreach'; +const ENDPOINT_URL = '//ads.divreach.com/prebid.1.0.aspx'; +const ZONE_ID = '2eb6bd58-865c-47ce-af7f-a918108c3fd2'; + +describe('DivReachAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.be.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': BIDDER_CODE, + 'params': { + 'zone': ZONE_ID + }, + '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); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': BIDDER_CODE, + 'params': { + 'zone': ZONE_ID + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should add referrer and imp to be equal bidRequest', () => { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data.substr(5)); + expect(payload.referrer).to.not.be.undefined; + expect(payload.imps[0]).to.deep.equal(bidRequests[0]); + }); + + it('sends bid request to ENDPOINT via GET', () => { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT_URL); + expect(request.method).to.equal('GET'); + }); + }); + + describe('interpretResponse', () => { + let response = { + body: [{ + 'currency': 'USD', + 'cpm': 6.210000, + 'ad': '
ad
', + 'width': 300, + 'height': 600, + 'creativeId': 'ccca3e5e-0c54-4761-9667-771322fbdffc', + 'ttl': 360, + 'netRevenue': false, + 'bidId': '5e4e763b6bc60b' + }] + }; + + it('should get correct bid response', () => { + const body = response.body; + let expectedResponse = [ + { + 'requestId': body[0].bidId, + 'cpm': body[0].cpm, + 'creativeId': body[0].creativeId, + 'width': body[0].width, + 'height': body[0].height, + 'ad': body[0].ad, + 'vastUrl': undefined, + 'currency': body[0].currency, + 'netRevenue': body[0].netRevenue, + 'ttl': body[0].ttl, + } + ]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('handles nobid responses', () => { + let response = []; + + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/fidelityBidAdapter_spec.js b/test/spec/modules/fidelityBidAdapter_spec.js index 28ea18aacacc..007f4e6b4805 100644 --- a/test/spec/modules/fidelityBidAdapter_spec.js +++ b/test/spec/modules/fidelityBidAdapter_spec.js @@ -52,7 +52,7 @@ describe('FidelityAdapter', () => { describe('buildRequests', () => { let bidderRequest = { bidderCode: 'fidelity', - auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', bidderRequestId: '178e34bad3658f', bids: [ { @@ -66,7 +66,7 @@ describe('FidelityAdapter', () => { sizes: [[300, 250], [320, 50]], bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', - auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' } ], @@ -85,11 +85,31 @@ describe('FidelityAdapter', () => { expect(payload.zoneid).to.exist; expect(payload.floor).to.exist; expect(payload.charset).to.exist; - expect(payload.defloc).to.exist; - expect(payload.altloc).to.exist; expect(payload.subid).to.exist; expect(payload.flashver).to.exist; expect(payload.tmax).to.exist; + expect(payload.defloc).to.exist; + }); + + it('should add gdpr consent information to the request', () => { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: consentString, + vendorData: { + vendorConsents: { + '408': 1 + }, + }, + }; + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const payload = request.data; + expect(payload.gdpr).to.exist.and.to.be.a('number'); + expect(payload.gdpr).to.equal(1); + expect(payload.consent_str).to.exist.and.to.be.a('string'); + expect(payload.consent_str).to.equal(consentString); + expect(payload.consent_given).to.exist.and.to.be.a('number'); + expect(payload.consent_given).to.equal(1); }); it('sends bid request to ENDPOINT via GET', () => { diff --git a/test/spec/modules/justpremiumBidAdapter_spec.js b/test/spec/modules/justpremiumBidAdapter_spec.js index 3b74b7300443..eb6d17d7a328 100644 --- a/test/spec/modules/justpremiumBidAdapter_spec.js +++ b/test/spec/modules/justpremiumBidAdapter_spec.js @@ -4,6 +4,7 @@ import { spec } from 'modules/justpremiumBidAdapter' describe('justpremium adapter', () => { let adUnits = [ { + adUnitCode: 'div-gpt-ad-1471513102552-1', bidder: 'justpremium', params: { zone: 28313, @@ -11,6 +12,7 @@ describe('justpremium adapter', () => { } }, { + adUnitCode: 'div-gpt-ad-1471513102552-2', bidder: 'justpremium', params: { zone: 32831, @@ -51,7 +53,7 @@ describe('justpremium adapter', () => { expect(jpxRequest.id).to.equal(adUnits[0].params.zone) expect(jpxRequest.sizes).to.not.equal('undefined') expect(jpxRequest.version.prebid).to.equal('$prebid.version$') - expect(jpxRequest.version.jp_adapter).to.equal('1.1') + expect(jpxRequest.version.jp_adapter).to.equal('1.2') }) }) @@ -128,4 +130,32 @@ describe('justpremium adapter', () => { expect(options[0].url).to.match(/\/\/pre.ads.justpremium.com\/v\/1.0\/t\/sync/) }) }) + + describe('onTimeout', () => { + it('onTimeout', (done) => { + spec.onTimeout([{ + 'bidId': '25cd3ec3fd6ed7', + 'bidder': 'justpremium', + 'adUnitCode': 'div-gpt-ad-1471513102552-1', + 'auctionId': '6fbd0562-f613-4151-a6df-6cb446fc717b', + 'params': [{ + 'adType': 'iab', + 'zone': 21521 + }], + 'timeout': 1 + }, { + 'bidId': '3b51df1f254e32', + 'bidder': 'justpremium', + 'adUnitCode': 'div-gpt-ad-1471513102552-3', + 'auctionId': '6fbd0562-f613-4151-a6df-6cb446fc717b', + 'params': [{ + 'adType': 'iab', + 'zone': 21521 + }], + 'timeout': 1 + }]) + + done() + }) + }) }) diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index bb0feeba069d..432ae086511c 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -50,13 +50,19 @@ describe('kargo adapter tests', function () { params: { placementId: 'foo' }, - placementCode: 1 + bidId: 1 }, { params: { placementId: 'bar' }, - placementCode: 2 + bidId: 2 + }, + { + params: { + placementId: 'bar' + }, + bidId: 3 } ]; }); @@ -173,10 +179,11 @@ describe('kargo adapter tests', function () { floor: 0, ceil: 20 }, - adSlotIDs: [ - 'foo', - 'bar' - ], + bidIDs: { + 1: 'foo', + 2: 'bar', + 3: 'bar' + }, userIDs: { kargoID: '5f108831-302d-11e7-bf6b-4595acd3bf6c', clientID: '2410d8f2-c111-4811-88a5-7b5e190e475f', @@ -235,7 +242,7 @@ describe('kargo adapter tests', function () { var request = spec.buildRequests(bids, {timeout: 200, foo: 'bar'}); var krakenParams = JSON.parse(decodeURIComponent(request.data.slice(5))); expect(request.data.slice(0, 5)).to.equal('json='); - expect(request.url).to.equal('https://krk.kargo.com/api/v1/bid'); + expect(request.url).to.equal('https://krk.kargo.com/api/v2/bid'); expect(request.method).to.equal('GET'); expect(request.currency).to.equal('USD'); expect(request.timeout).to.equal(200); @@ -306,13 +313,22 @@ describe('kargo adapter tests', function () { describe('response handler', function() { it('handles bid responses', function() { var resp = spec.interpretResponse({body: { - foo: { + 1: { + id: 'foo', cpm: 3, adm: '
', width: 320, height: 50 }, - bar: { + 2: { + id: 'bar', + cpm: 2.5, + adm: '
', + width: 300, + height: 250 + }, + 3: { + id: 'bar', cpm: 2.5, adm: '
', width: 300, @@ -321,19 +337,24 @@ describe('kargo adapter tests', function () { }}, { currency: 'USD', bids: [{ - bidId: 'fake bid id 1', + bidId: 1, params: { placementId: 'foo' } }, { - bidId: 'fake bid id 2', + bidId: 2, + params: { + placementId: 'bar' + } + }, { + bidId: 3, params: { placementId: 'bar' } }] }); var expectation = [{ - requestId: 'fake bid id 1', + requestId: '1', cpm: 3, width: 320, height: 50, @@ -343,7 +364,17 @@ describe('kargo adapter tests', function () { netRevenue: true, currency: 'USD' }, { - requestId: 'fake bid id 2', + requestId: '2', + cpm: 2.5, + width: 300, + height: 250, + ad: '
', + ttl: 300, + creativeId: 'bar', + netRevenue: true, + currency: 'USD' + }, { + requestId: '3', cpm: 2.5, width: 300, height: 250, diff --git a/test/spec/modules/lockerdomeBidAdapter_spec.js b/test/spec/modules/lockerdomeBidAdapter_spec.js index 84f0dd43e72f..19884065597e 100644 --- a/test/spec/modules/lockerdomeBidAdapter_spec.js +++ b/test/spec/modules/lockerdomeBidAdapter_spec.js @@ -65,12 +65,14 @@ describe('LockerDomeAdapter', () => { expect(bids).to.have.lengthOf(2); expect(bids[0].requestId).to.equal('2652ca954bce9'); + expect(bids[0].adUnitCode).to.equal('ad-1'); expect(bids[0].adUnitId).to.equal(10809467961050726); expect(bids[0].sizes).to.have.lengthOf(1); expect(bids[0].sizes[0][0]).to.equal(300); expect(bids[0].sizes[0][1]).to.equal(250); expect(bids[1].requestId).to.equal('4510f2834773ce'); + expect(bids[1].adUnitCode).to.equal('ad-2'); expect(bids[1].adUnitId).to.equal(9434769725128806); expect(bids[1].sizes).to.have.lengthOf(1); expect(bids[1].sizes[0][0]).to.equal(300); diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index d94cb64c1457..027b23f54bbd 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -17,7 +17,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 250]], 'bidId': '28f8f8130a583e', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }, { 'bidder': 'medianet', 'params': { @@ -33,7 +34,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 251]], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }], VALID_BID_REQUEST_INVALID_BIDFLOOR = [{ 'bidder': 'medianet', @@ -51,7 +53,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 250]], 'bidId': '28f8f8130a583e', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }, { 'bidder': 'medianet', 'params': { @@ -67,7 +70,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 251]], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }], VALID_AUCTIONDATA = { 'timeout': config.getConfig('bidderTimeout'), @@ -103,7 +107,8 @@ let VALID_BID_REQUEST = [{ x: 100, y: 100 } - } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -133,7 +138,8 @@ let VALID_BID_REQUEST = [{ x: 100, y: 100 } - } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -181,7 +187,8 @@ let VALID_BID_REQUEST = [{ x: 100, y: 100 } - } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -210,7 +217,8 @@ let VALID_BID_REQUEST = [{ x: 100, y: 100 } - } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -372,7 +380,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [300, 250], 'bidId': '28f8f8130a583e', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }, { 'bidder': 'medianet', 'params': { @@ -388,7 +397,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [300, 251], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }], VALID_BIDDER_REQUEST_WITH_GDPR = { 'gdprConsent': { @@ -429,7 +439,8 @@ let VALID_BID_REQUEST = [{ x: 100, y: 100 } - } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -458,7 +469,8 @@ let VALID_BID_REQUEST = [{ x: 100, y: 100 } - } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 3d7bba417f9f..278b39fd0794 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -1,9 +1,12 @@ import { expect } from 'chai'; import { spec } from 'modules/oneVideoBidAdapter'; import * as utils from 'src/utils'; +import {config} from 'src/config'; describe('OneVideoBidAdapter', () => { let bidRequest; + let bidderRequest; + let mockConfig; beforeEach(() => { bidRequest = { @@ -132,4 +135,33 @@ describe('OneVideoBidAdapter', () => { expect(bidResponse).to.deep.equal(o); }); }); + + describe('when GDPR applies', function () { + beforeEach(function () { + bidderRequest = { + gdprConsent: { + consentString: 'test-gdpr-consent-string', + gdprApplies: true + } + }; + + mockConfig = { + consentManagement: { + cmpApi: 'iab', + timeout: 1111, + allowAuctionWithoutConsent: 'cancel' + } + }; + }); + + it('should send a signal to specify that GDPR applies to this request', function () { + const request = spec.buildRequests([ bidRequest ], bidderRequest); + expect(request[0].data.regs.ext.gdpr).to.equal(1); + }); + + it('should send the consent string', function () { + const request = spec.buildRequests([ bidRequest ], bidderRequest); + expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); + }); + }); }); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index f3cc150ab6e4..556755316eb3 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1092,7 +1092,7 @@ describe('S2S Adapter', () => { expect(vendorConfig.cookieSet).to.be.false; expect(vendorConfig.cookieSetUrl).to.be.undefined; expect(vendorConfig.enabled).to.be.true; - expect(vendorConfig).to.have.property('endpoint', '//prebid-server.rubiconproject.com/auction'); + expect(vendorConfig).to.have.property('endpoint', '//prebid-server.rubiconproject.com/openrtb2/auction'); expect(vendorConfig).to.have.property('syncEndpoint', '//prebid-server.rubiconproject.com/cookie_sync'); expect(vendorConfig).to.have.property('timeout', 750); }); diff --git a/test/spec/modules/somoaudienceBidAdapter_spec.js b/test/spec/modules/somoaudienceBidAdapter_spec.js index 2189cacb0ec5..79ece7ffcf65 100644 --- a/test/spec/modules/somoaudienceBidAdapter_spec.js +++ b/test/spec/modules/somoaudienceBidAdapter_spec.js @@ -1,97 +1,481 @@ import {expect} from 'chai'; import {spec} from 'modules/somoaudienceBidAdapter'; -import {getTopWindowLocation, getTopWindowReferrer} from 'src/utils'; -import {newBidder} from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; describe('Somo Audience Adapter Tests', () => { - const bidderSet = [ - { - bidder: 'somoaudience', - params: { - placementId: 'test' - } - }, - ]; - - const bidderAppSet = [ - { - bidder: 'somoaudience', - params: { - placementId: 'test', - app: { - bundle: 'com.somoaudience.apps', - storeUrl: 'http://somoaudience.com/apps', - domain: 'somoaudience.com', + describe('isBidRequestValid', () => { + it('should return false when given an invalid bid', () => { + const bid = { + bidder: 'somoaudience', + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }); + it('should return true when given a placementId bid', () => { + const bid = { + bidder: 'somoaudience', + params: { + placementId: 'test' } - } - }, - ]; - - const bidderBadSet = [ - { - bidder: 'somoaudience', - params: { - placement_id: 'test' - } - }, - ]; - - it('Verifies bidder code', () => { - expect(spec.code).to.equal('somoaudience'); + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); }); - it('Verifies bidder aliases', () => { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.equal('somo'); - }); + describe('buildRequests', () => { + describe('buildBannerRequests', () => { + it('should properly build a banner request with type not defined and sizes not defined', () => { + const bidRequests = [{ + bidder: 'somoaudience', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + expect(request[0].url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); + expect(request[0].method).to.equal('POST'); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.device).to.not.equal(null); + expect(ortbRequest.device.ua).to.equal(navigator.userAgent); + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + }); - it('Verifies if bid request valid', () => { - expect(spec.isBidRequestValid(bidderSet[0])).to.equal(true); - expect(spec.isBidRequestValid(bidderAppSet[0])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid(bidderBadSet[0])).to.equal(false); - expect(spec.isBidRequestValid({ params: {} })).to.equal(false); - expect(spec.isBidRequestValid({ params: { placementId: '12345' } })).to.equal(true); - }); + it('should properly build a banner request with sizes defined in 2d array', () => { + const bidRequests = [{ + bidder: 'somoaudience', + sizes: [[300, 250]], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + expect(request[0].url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); + expect(request[0].method).to.equal('POST'); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); + }); + it('should properly build a banner request with sizes defined in 1d array', () => { + const bidRequests = [{ + bidder: 'somoaudience', + sizes: [300, 250], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + expect(request[0].url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); + expect(request[0].method).to.equal('POST'); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); + expect(ortbRequest.imp[0].banner.mimes).to.equal(undefined); + expect(ortbRequest.imp[0].banner.btype).to.equal(undefined); + expect(ortbRequest.imp[0].banner.pos).to.equal(undefined); + expect(ortbRequest.imp[0].banner.battr).to.equal(undefined); + }); + + it('should populate optional banner parameters', () => { + const bidRequests = [ + { + bidder: 'somoaudience', + sizes: [[300, 200]], + mediaType: 'banner', + params: { + placementId: 'test', + banner: { + mimes: 'video/mp4', + btype: '4', + pos: '1', + battr: 'ibv', + } + } + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(200); + expect(ortbRequest.imp[0].banner.mimes).to.equal('video/mp4'); + expect(ortbRequest.imp[0].banner.btype).to.equal('4'); + expect(ortbRequest.imp[0].banner.pos).to.equal('1'); + expect(ortbRequest.imp[0].banner.battr).to.equal('ibv'); + }); + }); + + describe('buildVideoRequests', () => { + it('should properly build a video request with sizes defined', () => { + const bidRequests = [{ + bidder: 'somoaudience', + mediaTypes: { + video: {} + }, + sizes: [200, 300], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.w).to.equal(200); + expect(ortbRequest.imp[0].video.h).to.equal(300); + }); + + it('should properly build a video request with sizes defined in 2d array', () => { + const bidRequests = [{ + bidder: 'somoaudience', + mediaTypes: { + video: {} + }, + sizes: [[200, 300]], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.w).to.equal(200); + expect(ortbRequest.imp[0].video.h).to.equal(300); + }); + it('should properly build a video request with sizes not defined', () => { + const bidRequests = [{ + bidder: 'somoaudience', + mediaType: 'video', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.mimes).to.equal(undefined); + expect(ortbRequest.imp[0].video.minduration).to.equal(undefined); + expect(ortbRequest.imp[0].video.maxduration).to.equal(undefined); + expect(ortbRequest.imp[0].video.protocols).to.equal(undefined); + expect(ortbRequest.imp[0].video.startdelay).to.equal(undefined); + expect(ortbRequest.imp[0].video.linearity).to.equal(undefined); + expect(ortbRequest.imp[0].video.skip).to.equal(undefined); + expect(ortbRequest.imp[0].video.delivery).to.equal(undefined); + expect(ortbRequest.imp[0].video.pos).to.equal(undefined); + expect(ortbRequest.imp[0].video.api).to.equal(undefined); + expect(ortbRequest.imp[0].video.battr).to.equal(undefined); + }); + + it('should populate optional video parameters', () => { + const bidRequests = [ + { + bidder: 'somoaudience', + sizes: [[200, 300]], + mediaType: 'video', + params: { + placementId: 'test', + video: { + mimes: 'video/mp4', + minduration: '15', + maxduration: '30', + protocols: 'mp4', + startdelay: '0', + linearity: 'linear', + skip: '1', + delivery: 'web', + pos: '1', + api: 'VPAID 1.0', + battr: 'ibv', + } + } + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.w).to.equal(200); + expect(ortbRequest.imp[0].video.h).to.equal(300); + expect(ortbRequest.imp[0].video.mimes).to.equal('video/mp4'); + expect(ortbRequest.imp[0].video.minduration).to.equal('15'); + expect(ortbRequest.imp[0].video.maxduration).to.equal('30'); + expect(ortbRequest.imp[0].video.protocols).to.equal('mp4'); + expect(ortbRequest.imp[0].video.startdelay).to.equal('0'); + expect(ortbRequest.imp[0].video.linearity).to.equal('linear'); + expect(ortbRequest.imp[0].video.skip).to.equal('1'); + expect(ortbRequest.imp[0].video.delivery).to.equal('web'); + expect(ortbRequest.imp[0].video.pos).to.equal('1'); + expect(ortbRequest.imp[0].video.api).to.equal('VPAID 1.0'); + expect(ortbRequest.imp[0].video.battr).to.equal('ibv'); + }); + }); + + describe('buildSiteRequests', () => { + it('should fill in basic site parameters', () => { + const bidRequests = [{ + bidder: 'somoaudience', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.app).to.equal(null); + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + }); + + it('should fill in optional site parameters', () => { + const bidRequests = [{ + bidder: 'somoaudience', + params: { + placementId: 'test', + site: { + domain: 'somoaudience.com', + name: 'Somo Audience', + cat: 'IAB-25', + keywords: 'unit testing', + content: 'Unit Testing' + } + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.app).to.equal(null); + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.name).to.equal('Somo Audience'); + expect(ortbRequest.site.domain).to.equal('somoaudience.com'); + expect(ortbRequest.site.cat).to.equal('IAB-25'); + expect(ortbRequest.site.keywords).to.equal('unit testing'); + expect(ortbRequest.site.content).to.equal('Unit Testing'); + }) + }); + + describe('buildAppRequests', () => { + it('should fill in app parameters', () => { + const bidRequests = [{ + bidder: 'somoaudience', + params: { + placementId: 'test', + app: { + bundle: 'com.somoaudience.apps', + storeUrl: 'http://somoaudience.com/apps', + domain: 'somoaudience.com', + name: 'Generic SomoAudience App 5', + cat: 'IAB-25', + keywords: 'unit testing', + content: 'Unit Testing', + ver: '5.423-s', + } + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.equal(null); + expect(ortbRequest.app).to.not.be.null; + expect(ortbRequest.app.bundle).to.equal('com.somoaudience.apps'); + expect(ortbRequest.app.storeUrl).to.equal('http://somoaudience.com/apps'); + expect(ortbRequest.app.domain).to.equal('somoaudience.com'); + expect(ortbRequest.app.name).to.equal('Generic SomoAudience App 5'); + expect(ortbRequest.app.ver).to.equal('5.423-s'); + expect(ortbRequest.app.cat).to.equal('IAB-25'); + expect(ortbRequest.app.keywords).to.equal('unit testing'); + expect(ortbRequest.app.content).to.equal('Unit Testing'); + }); + }); - it('Verifies buildRequests', () => { - const request = spec.buildRequests(bidderSet); - let br = request[0]; - expect(br.url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); - expect(br.method).to.equal('POST'); - const ortbRequest = br.data; - expect(ortbRequest.site).to.not.equal(null); - expect(ortbRequest.site.ref).to.equal(getTopWindowReferrer()); - expect(ortbRequest.site.page).to.equal(getTopWindowLocation().href); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.device).to.not.equal(null); - expect(ortbRequest.device.ua).to.equal(navigator.userAgent); + describe('buildGDPRRequests', () => { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'test' + }, + }; + + it('should properly build request with gdpr consent', () => { + const bidRequests = [{ + bidder: 'somoaudience', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request[0].data; + expect(ortbRequest.reqs).to.not.equal(undefined); + expect(ortbRequest.reqs.ext).to.not.equal(undefined); + expect(ortbRequest.reqs.ext.gdpr).to.equal(true); + expect(ortbRequest.user).to.not.equal(undefined); + expect(ortbRequest.user.ext).to.not.equal(undefined); + expect(ortbRequest.user.ext.consent).to.equal('test'); + }); + it('should properly build request with gdpr not applies', () => { + bidderRequest.gdprConsent.gdprApplies = false; + const bidRequests = [{ + bidder: 'somoaudience', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request[0].data; + expect(ortbRequest.reqs).to.not.equal(undefined); + expect(ortbRequest.reqs.ext).to.not.equal(undefined); + expect(ortbRequest.reqs.ext.gdpr).to.equal(false); + expect(ortbRequest.user).to.not.equal(undefined); + expect(ortbRequest.user.ext).to.not.equal(undefined); + expect(ortbRequest.user.ext.consent).to.equal('test'); + }); + }); + + describe('buildExtraArgsRequests', () => { + it('should populate optional parameters', () => { + const bidRequests = [ + { + bidder: 'somoaudience', + params: { + placementId: 'test', + bcat: ['IAB-2', 'IAB-7'], + badv: ['somoaudience.com', 'mobileadtrading.com'], + bidfloor: '0.05', + }, + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].bidfloor).to.be.equal('0.05'); + expect(ortbRequest.bcat).to.not.be.null; + expect(ortbRequest.bcat).to.have.lengthOf(2); + expect(ortbRequest.bcat).to.contain('IAB-2'); + expect(ortbRequest.badv).to.not.be.null; + expect(ortbRequest.badv).to.have.lengthOf(2); + expect(ortbRequest.badv).to.contain('somoaudience.com'); + }); + }); }); - it('Verify parse response', () => { - const request = spec.buildRequests(bidderSet); - const ortbRequest = request[0].data; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'Somo Test Ad' + describe('interpretResponse', () => { + it('Verify banner parse response', () => { + const bidRequests = [ + { + bidder: 'somoaudience', + params: { + placementId: 'test', + }, + bidId: '234234234', + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'Somo Test Ad' + }], + bidId: '234234234' }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.equal('Somo Test Ad'); + }; + const bids = spec.interpretResponse({ body: ortbResponse }, {bidRequest: bidRequests[0]}); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.equal('Somo Test Ad'); + }); + + it('Verify video parse response', () => { + const bidRequests = [ + { + bidder: 'somoaudience', + mediaTypes: { + video: { + } + }, + params: { + placementId: 'test', + }, + bidId: '234234234', + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'Somo Test Ad' + }], + bidId: '234234234' + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, {bidRequest: bidRequests[0]}); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.vastXml).to.equal('Somo Test Ad'); + }); }); - it('Verify app requests', () => { - const request = spec.buildRequests(bidderAppSet); - const ortbRequest = request[0].data; - expect(ortbRequest.site).to.equal(null); - expect(ortbRequest.app).to.not.be.null; - expect(ortbRequest.app.bundle).to.equal('com.somoaudience.apps'); - expect(ortbRequest.app.storeurl).to.equal('http://somoaudience.com/apps'); - expect(ortbRequest.app.domain).to.equal('somoaudience.com'); + + describe('user sync', () => { + it('should register the pixel sync url', () => { + let syncs = spec.getUserSyncs({ + pixelEnabled: true + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + }); + + it('should pass gdpr params', () => { + let syncs = spec.getUserSyncs({ pixelEnabled: true }, {}, { + gdprApplies: false, consentString: 'test' + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.contains('gdpr=0'); + }); + + it('should pass gdpr applies params', () => { + let syncs = spec.getUserSyncs({ pixelEnabled: true }, {}, { + gdprApplies: true, consentString: 'test' + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.contains('gdpr=1'); + expect(syncs[0].url).to.contains('gdpr_consent=test'); + }); }); }); diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 1b0986cb8c1c..63fb52b2dbd9 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -207,6 +207,11 @@ describe('SonobiBidAdapter', () => { expect(bidRequests.data.s).not.to.be.empty expect(bidRequests.data.hfa).to.equal('hfakey') }) + it('should return a properly formatted request with render', () => { + bidRequest[0].params.render = 'safeframe' + const bidRequests = spec.buildRequests(bidRequest) + expect(bidRequests.data.render).to.equal('safeframe') + }) it('should return null if there is nothing to bid on', () => { const bidRequests = spec.buildRequests([{params: {}}]) diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 75405850d7a8..2e0997725931 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -39,6 +39,14 @@ describe('TrustXAdapter', function () { }); describe('buildRequests', () => { + function parseRequest(url) { + const res = {}; + url.split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } let bidRequests = [ { 'bidder': 'trustx', @@ -77,8 +85,8 @@ describe('TrustXAdapter', function () { it('should attach valid params to the tag', () => { const request = spec.buildRequests([bidRequests[0]]); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); @@ -87,8 +95,8 @@ describe('TrustXAdapter', function () { it('auids must not be duplicated', () => { const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); @@ -98,8 +106,8 @@ describe('TrustXAdapter', function () { it('pt parameter must be "gross" if params.priceType === "gross"', () => { bidRequests[1].params.priceType = 'gross'; const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'gross'); expect(payload).to.have.property('auids', '43,45'); @@ -110,8 +118,8 @@ describe('TrustXAdapter', function () { it('pt parameter must be "net" or "gross"', () => { bidRequests[1].params.priceType = 'some'; const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); @@ -121,26 +129,26 @@ describe('TrustXAdapter', function () { it('if gdprConsent is present payload must have gdpr params', () => { const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 1); + expect(payload).to.have.property('gdpr_applies', '1'); }); it('if gdprApplies is false gdpr_applies must be 0', () => { const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 0); + expect(payload).to.have.property('gdpr_applies', '0'); }); it('if gdprApplies is undefined gdpr_applies must be 1', () => { const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 1); + expect(payload).to.have.property('gdpr_applies', '1'); }); }); diff --git a/test/spec/unit/adUnits_spec.js b/test/spec/unit/adUnits_spec.js new file mode 100644 index 000000000000..ff77315ba4ad --- /dev/null +++ b/test/spec/unit/adUnits_spec.js @@ -0,0 +1,23 @@ +import { expect } from 'chai'; +import { adunitCounter } from 'src/adUnits'; + +describe('Adunit Counter', function () { + const ADUNIT_ID_1 = 'test1'; + const ADUNIT_ID_2 = 'test2'; + + it('increments and checks counter of adunit 1', function () { + adunitCounter.incrementCounter(ADUNIT_ID_1); + expect(adunitCounter.getCounter(ADUNIT_ID_1)).to.be.equal(1); + }); + it('checks counter of adunit 2', function () { + expect(adunitCounter.getCounter(ADUNIT_ID_2)).to.be.equal(0); + }); + it('increments and checks counter of adunit 1', function () { + adunitCounter.incrementCounter(ADUNIT_ID_1); + expect(adunitCounter.getCounter(ADUNIT_ID_1)).to.be.equal(2); + }); + it('increments and checks counter of adunit 2', function () { + adunitCounter.incrementCounter(ADUNIT_ID_2); + expect(adunitCounter.getCounter(ADUNIT_ID_2)).to.be.equal(1); + }); +}); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index d46a8d740a56..385adc321d0a 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1809,12 +1809,9 @@ describe('Unit: Prebid Module', function () { }); describe('getHighestCpm', () => { - // it('returns an array of winning bid objects for each adUnit', () => { - // const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids(); - // expect(highestCpmBids.length).to.equal(2); - // expect(highestCpmBids[0]).to.deep.equal(auctionManager.getBidsReceived()[1]); - // expect(highestCpmBids[1]).to.deep.equal(auctionManager.getBidsReceived()[2]); - // }); + after(() => { + resetAuction(); + }); it('returns an array containing the highest bid object for the given adUnitCode', () => { const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids('/19968336/header-bid-tag-0'); @@ -1834,7 +1831,23 @@ describe('Unit: Prebid Module', function () { const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids('/19968336/header-bid-tag-0'); expect(highestCpmBids.length).to.equal(0); - resetAuction(); + }); + + it('should not return rendered bid', function() { + let _bidsReceived = getBidResponses().slice(0, 3); + _bidsReceived[0].cpm = 12; + _bidsReceived[0].status = 'rendered'; + _bidsReceived[1].cpm = 9; + _bidsReceived[2].cpm = 11; + + _bidsReceived.forEach((bid) => { + bid.adUnitCode = '/19968336/header-bid-tag-0'; + }); + + auction.getBidsReceived = function() { return _bidsReceived }; + + const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids('/19968336/header-bid-tag-0'); + expect(highestCpmBids[0]).to.deep.equal(auctionManager.getBidsReceived()[2]); }); });