diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fd757dce1f5..1dc04a9c2b7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,7 +6,7 @@ Thank you for your pull request. Please make sure this PR is scoped to one chang - [ ] Bugfix - [ ] Feature -- [ ] New bidder adapter +- [ ] New bidder adapter - [ ] Code style update (formatting, local variables) - [ ] Refactoring (no functional changes, no api changes) - [ ] Build related changes diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 00000000000..dc3a5df5ff7 --- /dev/null +++ b/.jshintignore @@ -0,0 +1 @@ +# Ingnored files diff --git a/CHANGELOG b/CHANGELOG index d03338efd6e..26feedc6151 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,24 @@ +AOL Prebid 1.19.0 +---------------- +Updated to Prebid 0.21.0 + + +AOL Prebid 1.18.0 +---------------- +Updated to Prebid 0.20.0 + + +AOL Prebid 1.17.0 +---------------- +Added functionality for rendering pixels once. + + +AOL Prebid 1.16.0 +---------------- +Added Audience Network adapter. +Added creative key field in bid response for Sharethrough adapter. + + AOL Prebid 1.15.0 ---------------- Nexage API implemented. diff --git a/README.md b/README.md index c56023b7459..52b6fd33b56 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ This URL is designed specifically for the Container Tag library and it supports [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/prebid/Prebid.js.svg)](http://isitmaintained.com/project/prebid/Prebid.js "Average time to resolve an issue") [![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) # Prebid.js @@ -181,6 +182,8 @@ Many SSPs, bidders, and publishers have contributed to this project. [20+ Bidder To add a bidder adapter, see the instructions in [How to add a bidder adaptor](http://prebid.org/dev-docs/bidder-adaptor.html). +Please **do NOT load Prebid.js inside your adapter**. If you do this, we will reject or remove your adapter as appropriate. + ### Code Quality Code quality is defined by `.jscs` and `.jshint` files and errors are reported in the terminal. diff --git a/adapters.json b/adapters.json index 41948e5920d..bf65a6d033d 100644 --- a/adapters.json +++ b/adapters.json @@ -1,23 +1,27 @@ [ "aardvark", "adblade", + "adbund", "adbutler", "adequant", "adform", "adkernel", "admedia", + "bidfluence", "vertamedia", "aol", "appnexus", "appnexusAst", + "audienceNetwork", "conversant", "districtmDMX", "fidelity", - "getintent", "gumgum", "hiromedia", "indexExchange", "kruxlink", + "getintent", + "inneractive", "komoona", "lifestreet", "mantis", @@ -25,14 +29,17 @@ "piximedia", "pubmatic", "pulsepoint", + "pulsepointLite", "rhythmone", "rubicon", "smartyads", - "smartadserver", + "smartadserver", "sekindoUM", + "serverbid", "sonobi", "sovrn", "springserve", + "thoughtleadr", "stickyadstv", "triplelift", "twenga", @@ -51,6 +58,8 @@ "vertoz", "widespace", "admixer", + "atomx", + "tapsense", { "appnexus": { "alias": "brealtime" @@ -66,6 +75,11 @@ "alias": "defymedia" } }, + { + "appnexus": { + "alias": "gourmetads" + } + }, { "appnexusAst": { "supportedMediaTypes": ["video"] @@ -100,6 +114,20 @@ "adkernel": { "alias": "headbidding" } + }, + { + "getintent": { + "supportedMediaTypes" : ["video"] + } + }, + { + "stickyadstv": { + "alias": "freewheel-ssp" + } + }, + { + "rhythmone": { + "supportedMediaTypes": ["video"] + } } - ] diff --git a/analytics.json b/analytics.json new file mode 100644 index 00000000000..36506bd9a4a --- /dev/null +++ b/analytics.json @@ -0,0 +1 @@ +["aol"] diff --git a/gulpHelpers.js b/gulpHelpers.js index 342cd04dbb8..6091dc4c495 100644 --- a/gulpHelpers.js +++ b/gulpHelpers.js @@ -53,10 +53,10 @@ module.exports = { createEnd2EndTestReport : function(targetDestinationDir) { var browsers = require('./browsers.json'); - var env = ['default']; + var env = []; var input = 'bs'; for(var key in browsers) { - if(key.substring(0, input.length) === input) { + if(key.substring(0, input.length) === input && browsers[key].browser !== 'iphone') { env.push(key); } } diff --git a/gulpfile.js b/gulpfile.js index ef7c459f6f1..1caafb647f1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -21,6 +21,7 @@ var header = require('gulp-header'); var zip = require('gulp-zip'); var replace = require('gulp-replace'); var shell = require('gulp-shell'); +var optimizejs = require('gulp-optimize-js'); var CI_MODE = process.env.NODE_ENV === 'ci'; var prebid = require('./package.json'); @@ -90,6 +91,7 @@ gulp.task('webpack', function () { } })) .pipe(header(banner, { prebid: prebid })) + .pipe(optimizejs()) // Remove window=window that was used to go around Uglify bug .pipe(replace(/,?(\/\*!ADAPTER BEGIN \w+\*\/)\s*window\s*=\s*window/g, '$1')) .pipe(replace(/,?(\/\*!ADAPTER END \w+\*\/)\s*window\s*=\s*window/g, '$1')) @@ -239,45 +241,46 @@ gulp.task('docs', ['clean-docs'], function () { }); gulp.task('e2etest', function() { - var cmd = '--env default'; + var cmdQueue = []; if(argv.browserstack) { var browsers = require('./browsers.json'); - var env = []; - var input = 'bs'; - for(var key in browsers) { - if(key.substring(0, input.length) === input) { - env.push(key); - } + delete browsers['bs_ie_9_windows_7']; + + var cmdStr = ' --config nightwatch.conf.js'; + if (argv.group) { + cmdStr = cmdStr + ' --group ' + argv.group; } - cmd = '--env default,' + env.join(','); - } + cmdStr = cmdStr + ' --reporter ./test/spec/e2e/custom-reporter/pbjs-html-reporter.js'; - if(argv.browserstack) { - cmd = cmd + ' --config nightwatch.conf.js'; - } else { - cmd = cmd + ' --config nightwatch.json'; - } + var startWith = 'bs'; - if (argv.group) { - cmd = cmd + ' --group ' + argv.group; + 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) { + var cmd = 'nightwatch --env ' + newArr.join(',') + cmdStr; + cmdQueue.push(cmd); + } + }); } - cmd = cmd + ' --reporter ./test/spec/e2e/custom-reporter/pbjs-html-reporter.js'; return gulp.src('') - .pipe(shell('nightwatch ' + cmd)); + .pipe(shell(cmdQueue.join(';'))); }); gulp.task('e2etest-report', function() { + var reportPort = 9010; var targetDestinationDir = './e2etest-report'; helpers.createEnd2EndTestReport(targetDestinationDir); connect.server({ - port: port, + port: reportPort, root: './', livereload: true }); setTimeout(function() { - opens('http://localhost:' + port + '/' + targetDestinationDir.slice(2) + '/results.html'); + opens('http://localhost:' + reportPort + '/' + targetDestinationDir.slice(2) + '/results.html'); }, 5000); }); diff --git a/integrationExamples/gpt/audienceNetwork_dfp.html b/integrationExamples/gpt/audienceNetwork_dfp.html new file mode 100644 index 00000000000..b30df31b276 --- /dev/null +++ b/integrationExamples/gpt/audienceNetwork_dfp.html @@ -0,0 +1,83 @@ + + + + + + +

Prebid.js Test

+
+ +
+
+

Audience Network quick start

+
    +
  1. Create a new App at https://developers.facebook.com/apps
  2. +
  3. Add the Audience Network product to it
  4. +
  5. Create a new Placement to generate your placementId
  6. +
  7. To test, ensure the User-Agent request header represents a mobile device
  8. +
+
+ + diff --git a/integrationExamples/gpt/pbjs_example_gpt.html b/integrationExamples/gpt/pbjs_example_gpt.html index f38d6dfc13b..5f3b25e1303 100644 --- a/integrationExamples/gpt/pbjs_example_gpt.html +++ b/integrationExamples/gpt/pbjs_example_gpt.html @@ -201,14 +201,14 @@ { bidder: 'memeglobal', params: { - tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com + tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com } }, { bidder: 'adblade', params: { partnerId: 42980, - bidfloor: 0.01 // OPTIONAL float bid floor in $ CPM + bidfloor: 0.01 // OPTIONAL float bid floor in $ CPM } }, { @@ -231,6 +231,13 @@ cur: 'EUR' } }, + { + bidder: 'adbund', + params: { + sid: '110238', // REQUIRED int. To get one, sign up http://www.adbund.com + bidfloor: 0.036 // OPTIONAL float bid floor in $ CPM + } + }, { bidder: 'smartyads', params: { @@ -242,6 +249,12 @@ params: { zone: '2eb6bd58-865c-47ce-af7f-a918108c3fd2' // REQUIRED zone oid } + }, + { + bidder: 'atomx', + params: { + id: 7395 + } } ] }, { @@ -326,22 +339,22 @@ } }, { - bidder: 'wideorbit', + bidder: 'memeglobal', params: { - pbId: 123, // REQUIRED Publisher Id, - pId: 123456 // REQUIRED Placement Id + tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com } }, { - bidder: 'memeglobal', + bidder: 'gumgum', params: { - tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com + inScreen: 'ggumtest' // REQUIRED str Tracking Id } }, { - bidder: 'gumgum', + bidder: 'wideorbit', params: { - inScreen: 'ggumtest' // REQUIRED str Tracking Id + pbId: 123, // REQUIRED Publisher Id, + pId: 123456 // REQUIRED Placement Id } } ] @@ -352,7 +365,7 @@ $$PREBID_GLOBAL$$.addAdUnits(adUnits); //register a callback handler - $$PREBID_GLOBAL$$.addCallback('adUnitBidsBack', function (adUnitCode) { + $$PREBID_GLOBAL$$.addCallback('adUnitBidsBack', function (adUnitCode) { console.debug('ad unit bids back for:', adUnitCode); }); diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html index 68120e0e863..3b0058f2ee8 100644 --- a/integrationExamples/gpt/x-domain/creative.html +++ b/integrationExamples/gpt/x-domain/creative.html @@ -5,7 +5,7 @@ var urlParser = document.createElement('a'); urlParser.href = '%%PATTERN:url%%'; var publisherDomain = urlParser.protocol + '//' + urlParser.hostname; -var adServerDomain = 'https://tpc.googlesyndication.com'; +var adServerDomain = urlParser.protocol + '//tpc.googlesyndication.com'; function renderAd(ev) { var key = ev.message ? 'message' : 'data'; diff --git a/karma.conf.js b/karma.conf.js index 91841543472..2c88288e191 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -44,7 +44,8 @@ module.exports = function (config) { // list of files to exclude exclude: [ - 'test/spec/loaders/**/*.js' + 'test/spec/loaders/**/*.js', + 'test/spec/adapters/underdogmedia_spec.js' ], // preprocess matching files before serving them to the browser diff --git a/loaders/adapterLoader.js b/loaders/adapterLoader.js index 9b136593f94..2bdf38e8eb8 100644 --- a/loaders/adapterLoader.js +++ b/loaders/adapterLoader.js @@ -8,7 +8,7 @@ const fs = require('fs'); const blockLoader = require('block-loader'); const getAdapters = require('./getAdapters'); -const adapters = getAdapters(); +const adapters = getAdapters('adapters.json', 'adapters'); const files = fs.readdirSync('src/adapters').map((file) => file.replace(/\.[^/.]+$/, '')); const adapterNames = adapters.filter(getStandardAdapters).filter(getUniques); //adapters loaded from `srcPath` @@ -46,13 +46,6 @@ function insertAdapters() { } }); - - if (!inserts.length) { - console.log('Prebid Warning: no matching adapters found for config, no adapters will be' + - ' loaded.'); - return ''; - } - return '/*!ADAPTER REGISTER DELIMITER*/' + inserts.map(name => { return `var ${adapterName(name)} = require('./adapters/${name}.js'); diff --git a/loaders/analyticsLoader.js b/loaders/analyticsLoader.js index 38c7f2f6b86..7e76a2819d6 100644 --- a/loaders/analyticsLoader.js +++ b/loaders/analyticsLoader.js @@ -2,7 +2,8 @@ const fs = require('fs'); const blockLoader = require('block-loader'); -let analyticsAdapters = require('../package.json').analytics; +const getAdapters = require('./getAdapters'); +let analyticsAdapters = getAdapters('analytics.json', 'analyticsAdapters'); var options = { start: '/** INSERT ANALYTICS - DO NOT EDIT OR REMOVE */', diff --git a/loaders/getAdapters.js b/loaders/getAdapters.js index 1aeef7dde2e..b69a534fc7f 100644 --- a/loaders/getAdapters.js +++ b/loaders/getAdapters.js @@ -4,8 +4,6 @@ const fs = require('fs'); const path = require('path'); const argv = require('yargs').argv; -const defaultAdapters = 'adapters.json'; - function load(file) { try { const buffer = fs.readFileSync(file); @@ -15,8 +13,8 @@ function load(file) { } } -module.exports = function getAdapters() { - let customAdapters = argv.adapters; +module.exports = function getAdapters(defaultAdapters, argName) { + let customAdapters = argv[argName]; if (!customAdapters) { return load(defaultAdapters); @@ -29,7 +27,7 @@ module.exports = function getAdapters() { return load(customAdapters); } catch (e) { console.log(`Prebid Warning: custom adapters config cannot be loaded from ${customAdapters}, ` + - 'using default adapters.json'); + `using default ${defaultAdapters}`); return load(defaultAdapters); } }; diff --git a/nightwatch.conf.js b/nightwatch.conf.js index c0583fee4df..072114953fe 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -1,17 +1,35 @@ module.exports = (function(settings) { var browsers = require('./browsers.json'); + delete browsers['bs_ie_9_windows_7']; + for(var browser in browsers) { + if(browsers[browser].browser === 'iphone') continue; + var desiredCapabilities = { "browserName": browsers[browser].browser, "version": browsers[browser].browser_version, - "platform": browsers[browser].os, + "platform": browsers[browser].os, "os": browsers[browser].os, - "os_version": browsers[browser].os_version, - "browser": browsers[browser].browser, - "browser_version": browsers[browser].browser_version, + "os_version": browsers[browser].os_version, + "browser": browsers[browser].browser, + "browser_version": browsers[browser].browser_version, }; - settings.test_settings[browser] = {} + settings.test_settings[browser] = { + "silent": true, + "exclude":["custom-assertions","custom-commands","common","custom-reporter"], + "screenshots" : { + "enabled" : false, + "path" : "" + }, + "javascriptEnabled": true, + "acceptSslCerts": true, + "browserstack.local": true, + "browserstack.debug": true, + "browserstack.selenium_version" : "2.53.0", + "browserstack.user": "${BROWSERSTACK_USERNAME}", + "browserstack.key": "${BROWSERSTACK_KEY}" + }; settings.test_settings[browser]['desiredCapabilities'] = desiredCapabilities; } return settings; diff --git a/package.json b/package.json index fb3037ec88b..e2d6783ee83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "0.19.0", + "version": "0.21.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { @@ -17,7 +17,6 @@ "prebid" ], "globalVarName": "pbjs", - "analytics": ["aol"], "author": "the prebid.js contributors", "contributors": [{ "name": "AOL Platforms", @@ -27,9 +26,9 @@ "devDependencies": { "babel-core": "^6.5.2", "babel-loader": "^6.2.3", - "babel-plugin-transform-object-assign": "^6.8.0", - "babel-plugin-transform-es3-property-literals": "^6.8.0", "babel-plugin-transform-es3-member-expression-literals": "^6.8.0", + "babel-plugin-transform-es3-property-literals": "^6.8.0", + "babel-plugin-transform-object-assign": "^6.8.0", "babel-preset-es2015": "^6.5.0", "block-loader": "^2.1.0", "chai": "^3.3.0", @@ -50,6 +49,7 @@ "gulp-jshint": "^1.8.4", "gulp-karma": "0.0.4", "gulp-mocha": "^2.2.0", + "gulp-optimize-js": "^1.1.0", "gulp-rename": "^1.2.0", "gulp-replace": "^0.4.0", "gulp-shell": "^0.5.2", @@ -94,7 +94,7 @@ "requirejs": "^2.1.20", "sinon": "^1.12.1", "string-replace-webpack-plugin": "0.0.3", - "uglify-js": "^2.4.15", + "uglify-js": "^2.8.10", "url-parse": "^1.0.5", "webpack": "^1.12.3", "webpack-stream": "^3.1.0", diff --git a/src/adaptermanager.js b/src/adaptermanager.js index 4c8ae95b3f7..9bb18cfec12 100644 --- a/src/adaptermanager.js +++ b/src/adaptermanager.js @@ -26,9 +26,10 @@ function getBids({bidderCode, requestId, bidderRequestId, adUnits}) { } sizes = sizeMapping; } - return Object.assign(bid, { + return Object.assign({}, bid, { placementCode: adUnit.code, mediaType: adUnit.mediaType, + transactionId : adUnit.transactionId, sizes: sizes, bidId: utils.getUniqueIdentifierStr(), bidderRequestId, diff --git a/src/adapters/adbund.js b/src/adapters/adbund.js new file mode 100644 index 00000000000..74809aa5e04 --- /dev/null +++ b/src/adapters/adbund.js @@ -0,0 +1,66 @@ +var CONSTANTS = require('../constants.json'); +var utils = require('../utils.js'); +var bidfactory = require('../bidfactory.js'); +var bidmanager = require('../bidmanager.js'); +var adloader = require('../adloader'); + +var adBundAdapter = function adBundAdapter() { + var timezone = (new Date()).getTimezoneOffset(); + var bidAPIs = [ + 'http://us-east-engine.adbund.xyz/prebid/ad/get', + 'http://us-west-engine.adbund.xyz/prebid/ad/get' + ]; + //Based on the time zone to select the interface to the server + var bidAPI = bidAPIs[timezone < 0 ? 0 : 1]; + + function _stringify(param) { + var result = []; + var key; + for (key in param) { + if (param.hasOwnProperty(key)) { + result.push(key + '=' + encodeURIComponent(param[key])); + } + } + return result.join('&'); + } + + function _createCallback(bid) { + return function (data) { + var response; + if (data && data.cpm) { + response = bidfactory.createBid(CONSTANTS.STATUS.GOOD); + response.bidderCode = 'adbund'; + Object.assign(response, data); + } else { + response = bidfactory.createBid(CONSTANTS.STATUS.NO_BID); + response.bidderCode = 'adbund'; + } + bidmanager.addBidResponse(bid.placementCode, response); + }; + } + + function _requestBids(bid) { + var info = { + referrer: utils.getTopWindowUrl(), + domain: utils.getTopWindowLocation().hostname, + ua: window.navigator.userAgent + }; + var param = Object.assign({}, bid.params, info); + param.sizes = JSON.stringify(param.sizes || bid.sizes); + param.callback = '$$PREBID_GLOBAL$$.adbundResponse'; + $$PREBID_GLOBAL$$.adbundResponse = _createCallback(bid); + adloader.loadScript(bidAPI + '?' + _stringify(param)); + } + + function _callBids(params) { + (params.bids || []).forEach(function (bid) { + _requestBids(bid); + }); + } + + return { + callBids: _callBids + }; +}; + +module.exports = adBundAdapter; \ No newline at end of file diff --git a/src/adapters/adkernel.js b/src/adapters/adkernel.js index b3383f44e9b..99653c6b482 100644 --- a/src/adapters/adkernel.js +++ b/src/adapters/adkernel.js @@ -65,6 +65,7 @@ const AdKernelAdapter = function AdKernelAdapter() { try { document.body.appendChild(iframe); } catch (error) { + /* istanbul ignore next */ utils.logError(error); } } @@ -176,7 +177,7 @@ const AdKernelAdapter = function AdKernelAdapter() { * Create bid object for the bid manager */ function createBidObject(resp, bid, width, height) { - return utils.extend(bidfactory.createBid(1, bid), { + return Object.assign(bidfactory.createBid(1, bid), { bidderCode: bid.bidder, ad: formatAdMarkup(resp), width: width, @@ -189,7 +190,7 @@ const AdKernelAdapter = function AdKernelAdapter() { * Create empty bid object for the bid manager */ function createEmptyBidObject(bid) { - return utils.extend(bidfactory.createBid(2, bid), { + return Object.assign(bidfactory.createBid(2, bid), { bidderCode: bid.bidder }); } @@ -200,7 +201,7 @@ const AdKernelAdapter = function AdKernelAdapter() { function formatAdMarkup(bid) { var adm = bid.adm; if ('nurl' in bid) { - adm += utils.createTrackPixelHtml(bid.nurl); + adm += utils.createTrackPixelHtml(`${bid.nurl}&px=1`); } return adm; } diff --git a/src/adapters/analytics/aol.js b/src/adapters/analytics/aol.js index 14c89c1f443..c1eeed8ad70 100644 --- a/src/adapters/analytics/aol.js +++ b/src/adapters/analytics/aol.js @@ -33,7 +33,7 @@ let auctionSchemaTemplate = template`;pubadid=${'pubadid'};hbauctionid=${'hbauct let winSchemaTemplate = template`;hbauctioneventts=${'hbauctioneventts'};pubadid=${'pubadid'};hbauctionid=${'hbauctionid'};hbwinner=${'hbwinner'};pubcpm=${'pubcpm'}${'hbdealid'};hbbidid=${'hbbidid'}`; let bidderSchemaTemplate = template`;hbbidder=${'hbbidder'};hbbid=${'hbbid'};hbstatus=${'hbstatus'};hbtime=${'hbtime'}${'hbdealid'};hbbidid=${'hbbidid'}`; -export default utils.extend(adapter({ +export default Object.assign(adapter({ url: '', analyticsType }), { diff --git a/src/adapters/analytics/aolPartnersIds.json b/src/adapters/analytics/aolPartnersIds.json index cd2e2115c80..ab5e74c0f51 100644 --- a/src/adapters/analytics/aolPartnersIds.json +++ b/src/adapters/analytics/aolPartnersIds.json @@ -55,5 +55,14 @@ "twenga": 54, "lifestreet": 55, "vertamedia": 56, - "stickyadstv": 57 + "stickyadstv": 57, + "audienceNetwork": 58, + "thoughtleadr": 59, + "adbund": 60, + "tapsense": 61, + "serverbid": 62, + "bidfluence": 63, + "pulsepointLite": 64, + "atomx": 65, + "inneractive": 66 } diff --git a/src/adapters/analytics/example2.js b/src/adapters/analytics/example2.js index 5e5c0dfc197..b9586b5c59a 100644 --- a/src/adapters/analytics/example2.js +++ b/src/adapters/analytics/example2.js @@ -5,12 +5,11 @@ import { ajax } from 'src/ajax'; */ import adapter from 'AnalyticsAdapter'; -const utils = require('../../utils'); const url = 'https://httpbin.org/post'; const analyticsType = 'endpoint'; -export default utils.extend(adapter( +export default Object.assign(adapter( { url, analyticsType diff --git a/src/adapters/analytics/roxot.js b/src/adapters/analytics/roxot.js index e00ef05c20f..2b71dda84db 100644 --- a/src/adapters/analytics/roxot.js +++ b/src/adapters/analytics/roxot.js @@ -6,7 +6,7 @@ const utils = require('../../utils'); const url = '//d.rxthdr.com/analytics'; const analyticsType = 'endpoint'; -export default utils.extend(adapter( +export default Object.assign(adapter( { url, analyticsType diff --git a/src/adapters/analytics/sharethrough_analytics.js b/src/adapters/analytics/sharethrough_analytics.js index 6b4f9297346..6eadcbcf60c 100644 --- a/src/adapters/analytics/sharethrough_analytics.js +++ b/src/adapters/analytics/sharethrough_analytics.js @@ -6,7 +6,7 @@ const analyticsType = 'endpoint'; const STR_BIDDER_CODE = "sharethrough"; const STR_VERSION = "0.1.0"; -export default utils.extend(adapter( +export default Object.assign(adapter( { emptyUrl, analyticsType diff --git a/src/adapters/aol.js b/src/adapters/aol.js index 687bfab4efc..febaf222c7c 100644 --- a/src/adapters/aol.js +++ b/src/adapters/aol.js @@ -3,7 +3,10 @@ const ajax = require('../ajax.js').ajax; const bidfactory = require('../bidfactory.js'); const bidmanager = require('../bidmanager.js'); const constants = require('../constants.json'); -const events = require('src/events'); + +$$PREBID_GLOBAL$$.aolGlobals = { + pixelsDropped: false +}; const AolAdapter = function AolAdapter() { @@ -23,7 +26,7 @@ const AolAdapter = function AolAdapter() { img: 'IMG' }; - let DOMReady = (() => { + let domReady = (() => { let readyEventFired = false; return fn => { let idempotentFn = () => { @@ -67,8 +70,11 @@ const AolAdapter = function AolAdapter() { })(); function dropSyncCookies(pixels) { - let pixelElements = parsePixelItems(pixels); - renderPixelElements(pixelElements); + if (!$$PREBID_GLOBAL$$.aolGlobals.pixelsDropped) { + let pixelElements = parsePixelItems(pixels); + renderPixelElements(pixelElements); + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; + } } function parsePixelItems(pixels) { @@ -122,7 +128,7 @@ const AolAdapter = function AolAdapter() { document.readyState === 'complete') { document.body.appendChild(iframe); } else { - DOMReady(() => { + domReady(() => { document.body.appendChild(iframe); }); } @@ -227,7 +233,11 @@ const AolAdapter = function AolAdapter() { if (bid.params.userSyncOn === constants.EVENTS.BID_RESPONSE) { dropSyncCookies(response.ext.pixels); } else { - ad += response.ext.pixels; + let formattedPixels = response.ext.pixels.replace(/<\/?script( type=('|")text\/javascript('|")|)?>/g, ''); + + ad += ''; } } @@ -250,9 +260,9 @@ const AolAdapter = function AolAdapter() { function _isNexageRequestPost(bid) { if (bid.params.id && bid.params.imp && bid.params.imp[0]) { let imp = bid.params.imp[0]; - return imp.id && imp.tagid - && ((imp.banner && imp.banner.w && imp.banner.h) - || (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration)); + return imp.id && imp.tagid && + ((imp.banner && imp.banner.w && imp.banner.h) || + (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration)); } } diff --git a/src/adapters/appnexus.js b/src/adapters/appnexus.js index 4e60c01826a..0a3b8f7284b 100644 --- a/src/adapters/appnexus.js +++ b/src/adapters/appnexus.js @@ -98,7 +98,7 @@ AppNexusAdapter = function AppNexusAdapter() { } //append custom attributes: - var paramsCopy = utils.extend({}, bid.params); + var paramsCopy = Object.assign({}, bid.params); //delete attributes already used delete paramsCopy.placementId; diff --git a/src/adapters/appnexusAst.js b/src/adapters/appnexusAst.js index 3258eeb7821..5888811ae9b 100644 --- a/src/adapters/appnexusAst.js +++ b/src/adapters/appnexusAst.js @@ -235,6 +235,7 @@ function AppnexusAstAdapter() { if (ad && status === STATUS.GOOD) { bid.cpm = ad.cpm; bid.creative_id = ad.creative_id; + bid.dealId = ad.deal_id; if (ad.rtb.video) { bid.width = ad.rtb.video.player_width; diff --git a/src/adapters/atomx.js b/src/adapters/atomx.js new file mode 100644 index 00000000000..4935382123a --- /dev/null +++ b/src/adapters/atomx.js @@ -0,0 +1,75 @@ +var CONSTANTS = require('../constants.json'); +var bidfactory = require('../bidfactory.js'); +var bidmanager = require('../bidmanager.js'); +var adloader = require('src/adloader.js'); +var Ajax = require('../ajax'); +var utils = require('../utils.js'); + +/** + * Adapter for requesting bids from Atomx. + * + * @returns {{callBids: _callBids, responseCallback: _responseCallback}} + */ +var AtomxAdapter = function AtomxAdapter() { + function _callBids(data) { + if (!window.atomx_prebid) { + adloader.loadScript(window.location.protocol + '//s.ato.mx/b.js', function() { _bid(data); }, true); + } else { + _bid(data); + } + } + + function _bid(data) { + var url = window.atomx_prebid(); + var bids = data.bids || []; + for (var i = 0, ln = bids.length; i < ln; i++) { + var bid = bids[i]; + if (bid.params && bid.params.id) { + Ajax.ajax(url, _responseCallback.bind(this, bid), { + id: bid.params.id, + size: utils.parseSizesInput(bid.sizes)[0], + prebid: bid.placementCode + }, {method: 'GET'}); + } else { + var bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bid); + bidObject.bidderCode = 'atomx'; + bidmanager.addBidResponse(bid.placementCode, bidObject); + } + } + } + + function _responseCallback(bid, data) { + var bidObject; + try { + data = JSON.parse(data); + + if (data.cpm && data.cpm > 0) { + bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD, bid); + bidObject.bidderCode = 'atomx'; + bidObject.cpm = data.cpm * 1000; + if (data.adm) { + bidObject.ad = data.adm; + } else { + bidObject.adUrl = data.url; + } + bidObject.width = data.width; + bidObject.height = data.height; + bidmanager.addBidResponse(bid.placementCode, bidObject); + return; + } + } catch (_error) { + utils.logError(_error); + } + + bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bid); + bidObject.bidderCode = 'atomx'; + bidmanager.addBidResponse(bid.placementCode, bidObject); + } + + return { + callBids: _callBids, + responseCallback: _responseCallback + }; +}; + +module.exports = AtomxAdapter; diff --git a/src/adapters/audienceNetwork.js b/src/adapters/audienceNetwork.js new file mode 100644 index 00000000000..8ab96430491 --- /dev/null +++ b/src/adapters/audienceNetwork.js @@ -0,0 +1,208 @@ +/** + * @file AudienceNetwork adapter. + */ +import { ajax } from '../ajax'; +import { createBid } from '../bidfactory'; +import { addBidResponse } from '../bidmanager'; +import { STATUS } from '../constants.json'; +import { format } from '../url'; +import { logError } from '../utils'; +import { createNew } from './adapter'; + +const baseAdapter = createNew('audienceNetwork'); +const setBidderCode = baseAdapter.setBidderCode; +const getBidderCode = baseAdapter.getBidderCode; + +/** + * Does this bid request contain valid parameters? + * @param {Object} bid + * @returns {Boolean} + */ +const validateBidRequest = bid => + typeof bid.params === 'object' && + typeof bid.params.placementId === 'string' && + bid.params.placementId.length > 0 && + Array.isArray(bid.sizes) && bid.sizes.length > 0; + +/** + * Does this bid request contain valid sizes? + * @param {Object} bid + * @returns {Boolean} + */ +const validateBidRequestSizes = bid => { + bid.sizes = bid.sizes.map(flattenSize); + return bid.sizes.every( size => + ['native', 'fullwidth', '300x250', '320x50'].includes(size) ); +}; + +/** + * Flattens a 2-element [W, H] array as a 'WxH' string, + * otherwise passes value through. + * @params {Array|String} size + * @returns {String} + */ +const flattenSize = size => + (Array.isArray(size) && size.length === 2) ? `${size[0]}x${size[1]}` : size; + +/** + * Does the search part of the URL contain "anhb_testmode" + * and therefore indicate testmode should be used? + * @returns {String} "true" or "false" + */ +const isTestmode = () => Boolean( + window && window.location && + typeof window.location.search === 'string' && + window.location.search.indexOf('anhb_testmode') !== -1 +).toString(); + +/** + * Parse JSON-as-string into an Object, default to empty. + * @param {String} JSON-as-string + * @returns {Object} + */ +const parseJson = jsonAsString => { + let data = {}; + try { + data = JSON.parse(jsonAsString); + } catch (err) {} + return data; +}; + +/** + * Is this a native advert size? + * @param {String} size + * @returns {Boolean} + */ +const isNative = (size) => ['native', 'fullwidth'].includes(size); + +/** + * Generate ad HTML for injection into an iframe + * @param {String} placementId + * @param {String} size + * @param {String} bidId + * @returns {String} HTML + */ +const createAdHtml = (placementId, size, bidId) => { + const nativeStyle = isNative(size) ? '' : ''; + const nativeContainer = isNative(size) ? '
' : ''; + return `${nativeStyle}
+ +${nativeContainer}
`; +}; + +/** + * Creates a "good" Bid object with the given bid ID and CPM. + * @param {String} placementId + * @param {String} bidId + * @param {String} size + * @param {Number} cpmCents + * @returns {Object} Bid + */ +const createSuccessBidResponse = (placementId, size, bidId, cpmCents) => { + const bid = createBid(STATUS.GOOD, { bidId }); + // Prebid attributes + bid.bidderCode = getBidderCode(); + bid.cpm = cpmCents / 100; + bid.ad = createAdHtml(placementId, size, bidId); + if (!isNative(size)) { + [bid.width, bid.height] = size.split('x').map(Number); + } + // Audience Network attributes + bid.hb_bidder = 'fan'; + bid.fb_bidid = bidId; + bid.fb_format = size; + bid.fb_placementid = placementId; + return bid; +}; + +/** + * Creates a "no bid" Bid object. + * @returns {Object} Bid + */ +const createFailureBidResponse = () => { + const bid = createBid(STATUS.NO_BID); + bid.bidderCode = getBidderCode(); + return bid; +}; + +/** + * Fetch bids for given parameters. + * @param {Object} bidRequest + * @param {Array} params.bids - list of bids + * @param {String} params.bids[].placementCode - Prebid placement identifier + * @param {Object} params.bids[].params + * @param {String} params.bids[].params.placementId - Audience Network placement identifier + * @param {Array} params.bids[].sizes - list of accepted advert sizes + * @param {Array|String} params.bids[].sizes[] - one of 'native', '300x250', '300x50', [300, 250], [300, 50] + * @returns {void} + */ +const callBids = bidRequest => { + // Build lists of adUnitCodes, placementids and adformats + const adUnitCodes = []; + const placementids = []; + const adformats = []; + bidRequest.bids + .filter(validateBidRequest) + .filter(validateBidRequestSizes) + .forEach( bid => bid.sizes.forEach( size => { + adUnitCodes.push(bid.placementCode); + placementids.push(bid.params.placementId); + adformats.push(size); + })); + + if (placementids.length) { + // Build URL + const testmode = isTestmode(); + const url = format({ + protocol: 'https', + host: 'an.facebook.com', + pathname: '/v2/placementbid.json', + search: { + sdk: '5.5.web', + testmode, + placementids, + adformats + } + }); + // Request + ajax(url, res => { + // Handle response + const data = parseJson(res); + if (data.errors && data.errors.length) { + const noBid = createFailureBidResponse(); + adUnitCodes.forEach( adUnitCode => addBidResponse(adUnitCode, noBid) ); + data.errors.forEach(logError); + } else { + // For each placementId in bids Object + Object.keys(data.bids) + // extract Array of bid responses + .map( placementId => data.bids[placementId] ) + // flatten + .reduce( (a, b) => a.concat(b), [] ) + // call addBidResponse + .forEach( (bid, i) => + addBidResponse(adUnitCodes[i], createSuccessBidResponse( + bid.placement_id, adformats[i], bid.bid_id, bid.bid_price_cents + )) + ); + } + }, null, { withCredentials: true }); + } else { + // No valid bids + logError('No valid bids requested'); + } +}; + +/** + * @class AudienceNetwork + * @type {Object} + * @property {Function} callBids - fetch bids for given parameters + * @property {Function} setBidderCode - used for bidder aliasing + * @property {Function} getBidderCode - unique 'audienceNetwork' identifier + */ +const AudienceNetwork = () => { + return { callBids, setBidderCode, getBidderCode }; +}; +module.exports = AudienceNetwork; diff --git a/src/adapters/bidfluence.js b/src/adapters/bidfluence.js new file mode 100644 index 00000000000..e5253e09878 --- /dev/null +++ b/src/adapters/bidfluence.js @@ -0,0 +1,57 @@ +const bidmanager = require('../bidmanager.js'); +const bidfactory = require('../bidfactory.js'); +const utils = require('../utils.js'); +const adloader = require('../adloader'); + +var BidfluenceAdapter = function BidfluenceAdapter() { + + const scriptUrl = "//bidfluence.azureedge.net/forge.js"; + + $$PREBID_GLOBAL$$.bfPbjsCB = function (bfr) { + var bidRequest = utils.getBidRequest(bfr.cbID); + var bidObject = null; + if (bfr.cpm > 0) { + bidObject = bidfactory.createBid(1, bidRequest); + bidObject.bidderCode = 'bidfluence'; + bidObject.cpm = bfr.cpm; + bidObject.ad = bfr.ad; + bidObject.width = bfr.width; + bidObject.height = bfr.height; + } else { + bidObject = bidfactory.createBid(2, bidRequest); + bidObject.bidderCode = 'bidfluence'; + } + + bidmanager.addBidResponse(bfr.placementCode, bidObject); + }; + + function _callBids(params) { + var bfbids = params.bids || []; + for (var i = 0; i < bfbids.length; i++) { + var bid = bfbids[i]; + call(bid); + } + } + function call(bid) { + + var adunitId = utils.getBidIdParameter('adunitId', bid.params); + var publisherId = utils.getBidIdParameter('pubId', bid.params); + var reservePrice = utils.getBidIdParameter('reservePrice', bid.params); + var pbjsBfobj = { + placementCode: bid.placementCode, + cbID: bid.bidId + }; + + var cb = function () { + /* globals FORGE */ + FORGE.init([adunitId, publisherId, pbjsBfobj, reservePrice]); + }; + + adloader.loadScript(scriptUrl, cb); + } + return { + callBids: _callBids + }; +}; + +module.exports = BidfluenceAdapter; diff --git a/src/adapters/centro.js b/src/adapters/centro.js index 7e7dc100c77..85303afc823 100644 --- a/src/adapters/centro.js +++ b/src/adapters/centro.js @@ -39,12 +39,10 @@ var CentroAdapter = function CentroAdapter() { utils.logError(LOG_ERROR_MESS.noUnit, bidderCode); return; } - var query = ['s=' + bid.unit];//,'url=www.abc15.com','sz=320x50']; + var query = ['s=' + bid.unit, 'adapter=prebid'];//,'url=www.abc15.com','sz=320x50']; var isDev = bid.unit.toString() === '28136'; - if (bid.page_url) { - query.push('url=' + encodeURIComponent(bid.page_url)); - } + query.push('url=' + encodeURIComponent(bid.page_url || location.href)); //check size format if ( size instanceof Array && @@ -55,8 +53,8 @@ var CentroAdapter = function CentroAdapter() { query.push('sz=' + size.join('x')); } //make handler name for JSONP request - var handlerName = handlerPrefix + bid.unit + size.join('x'); - query.push('callback=' + handlerName); + var handlerName = handlerPrefix + bid.unit + size.join('x') + encodeURIComponent(placementCode); + query.push('callback=' + encodeURIComponent('window["' + handlerName + '"]')); //maybe is needed add some random parameter to disable cache //query.push('r='+Math.round(Math.random() * 1e5)); @@ -77,7 +75,7 @@ var CentroAdapter = function CentroAdapter() { var bidObject; var bid = resp && resp.bid || resp; - if (bid && bid.adTag && bid.sectionID === unit) { + if (bid && bid.adTag && bid.sectionID && bid.sectionID.toString() === unit.toString()) { bidObject = bidfactory.createBid(1); bidObject.cpm = bid.value; bidObject.ad = bid.adTag; diff --git a/src/adapters/getintent.js b/src/adapters/getintent.js index 1293d98f903..a98fab952ae 100644 --- a/src/adapters/getintent.js +++ b/src/adapters/getintent.js @@ -1,5 +1,7 @@ /*jshint loopfunc: true */ +import { STATUS } from 'src/constants'; + var bidfactory = require('../bidfactory.js'); var bidmanager = require('../bidmanager.js'); var adloader = require('../adloader.js'); @@ -33,25 +35,34 @@ var GetIntentAdapter = function GetIntentAdapter() { pid: bidRequest.params.pid, // required tid: bidRequest.params.tid, // required known: bidRequest.params.known || 1, + is_video: bidRequest.mediaType === 'video', + video: bidRequest.params.video || {}, size: bidRequest.sizes[0].join("x"), }; addOptional(bidRequest.params, request, ['cur', 'floor']); - window.gi_hb.makeBid(request, function(bidResponse) { - if (bidResponse.no_bid === 1) { - var nobid = bidfactory.createBid(2); - nobid.bidderCode = bidRequest.bidder; - bidmanager.addBidResponse(bidRequest.placementCode, nobid); - } else { - var size = bidResponse.size.split('x'); - var bid = bidfactory.createBid(1); - bid.bidderCode = bidRequest.bidder; - bid.cpm = bidResponse.cpm; - bid.ad = bidResponse.ad; - bid.width = size[0]; - bid.height = size[1]; - bidmanager.addBidResponse(bidRequest.placementCode, bid); - } - }); + (function (r, br) { + window.gi_hb.makeBid(r, function(bidResponse) { + if (bidResponse.no_bid === 1) { + var nobid = bidfactory.createBid(STATUS.NO_BID); + nobid.bidderCode = br.bidder; + bidmanager.addBidResponse(br.placementCode, nobid); + } else { + var bid = bidfactory.createBid(STATUS.GOOD); + var size = bidResponse.size.split('x'); + bid.bidderCode = br.bidder; + bid.cpm = bidResponse.cpm; + bid.width = size[0]; + bid.height = size[1]; + if (br.mediaType === 'video') { + bid.vastUrl = bidResponse.vast_url; + bid.descriptionUrl = bidResponse.vast_url; + } else { + bid.ad = bidResponse.ad; + } + bidmanager.addBidResponse(br.placementCode, bid); + } + }); + })(request, bidRequest); } } diff --git a/src/adapters/inneractive.js b/src/adapters/inneractive.js new file mode 100644 index 00000000000..a4b24eeceec --- /dev/null +++ b/src/adapters/inneractive.js @@ -0,0 +1,462 @@ +import * as utils from '../utils'; +import Adapter from './adapter'; +import {ajax} from '../ajax'; +import bidManager from 'src/bidmanager'; +import bidFactory from 'src/bidfactory'; +import {STATUS} from 'src/constants'; +import {formatQS} from '../url'; + +/** + * @type {{IA_JS: string, ADAPTER_NAME: string, V: string, RECTANGLE_SIZE: {W: number, H: number}, SPOT_TYPES: {INTERSTITIAL: string, RECTANGLE: string, FLOATING: string, BANNER: string}, DISPLAY_AD: number, ENDPOINT_URL: string, EVENTS_ENDPOINT_URL: string, RESPONSE_HEADERS_NAME: {PRICING_VALUE: string, AD_H: string, AD_W: string}}} + */ +const CONSTANTS = { + ADAPTER_NAME: 'inneractive', + V: 'IA-JS-HB-PBJS-1.0', + RECTANGLE_SIZE:{W: 300, H: 250}, + + SPOT_TYPES: { + INTERSTITIAL: 'interstitial', + RECTANGLE: 'rectangle', + FLOATING: 'floating', + BANNER: 'banner' + }, + + DISPLAY_AD: 20, + ENDPOINT_URL: '//ad-tag.inner-active.mobi/simpleM2M/requestJsonAd', + EVENTS_ENDPOINT_URL: '//vast-events.inner-active.mobi/Event', + RESPONSE_HEADERS_NAME: { + PRICING_VALUE: 'X-IA-Pricing-Value', + AD_H: 'X-IA-Ad-Height', + AD_W: 'X-IA-Ad-Width' + } +}; + +let iaRef; +try{ + iaRef = window.top.document.referrer; +}catch(e){ + iaRef = window.document.referrer; +} + +/** + * gloable util functions + * @type {{defaultsQsParams: {v: (string|string), page: string, mw: boolean, hb: string}, stringToCamel: (function(*)), objectToCamel: (function(*=))}} + */ +const Helpers = { + defaultsQsParams: {v: CONSTANTS.V,page: encodeURIComponent(utils.getTopWindowUrl()),mw: true, hb: 'prebidjs'}, + /** + * Change string format from underscore to camelcase (e.g., APP_ID to appId) + * @param str: string + * @returns string + */ + stringToCamel(str){ + if(str.indexOf('_') === -1){ + const first = str.charAt(0); + if(first !== first.toLowerCase()){ + str = str.toLowerCase(); + } + return str; + } + + str = str.toLowerCase(); + return str.replace(/(\_[a-z])/g, $1 => $1.toUpperCase().replace('_','')); + }, + + /** + * Change all object keys string format from underscore to camelcase (e.g., {'APP_ID' : ...} to {'appId' : ...}) + * @param params: object + * @returns object + */ + objectToCamel(params){ + Object.keys(params).forEach(key => { + const keyCamelCase = this.stringToCamel(key); + if(keyCamelCase !== key){ + params[keyCamelCase] = params[key]; + delete params[key]; + } + }); + return params; + } +}; + +/** + * Tracking pixels for events + * @type {{fire: (function(*=))}} + */ +const Tracker = { + /** + * Creates a tracking pixel + * @param urls: Array + */ + fire(urls){ + urls.forEach(url => url && ((new Image(1,1)).src = encodeURI(url))); + } +}; + +/** + * Analytics + * @type {{errorEventName: string, pageProtocol: string, getPageProtocol: (function(): string), getEventUrl: (function(*, *=)), reportEvent: (function(string, Object)), defaults: {v: (string|string), page: string, mw: boolean, hb: string}, eventQueryStringParams: (function(Object): string), createTrackingPixel: (function(string))}} + */ +const Reporter = { + /** + * @private + */ + errorEventName: 'HBPreBidError', + pageProtocol: '', + + /** + * Gets the page protocol based on the document.location.protocol + * The returned string is either http:// or https:// + * @returns {string} + */ + getPageProtocol(){ + if(!this.pageProtocol){ + this.pageProtocol = ('http:' === utils.getTopWindowLocation().protocol ? 'http:' : 'https:'); + } + return this.pageProtocol; + }, + + getEventUrl(evtName, extraDetails){ + let eventsEndpoint = CONSTANTS.EVENTS_ENDPOINT_URL + '?table=' + ((evtName === this.errorEventName) ? 'mbwError' : 'mbwEvent'); + let queryStringParams = this.eventQueryStringParams(extraDetails); + const appId = extraDetails && extraDetails.appId; + let queryStringParamsWithAID = `${queryStringParams}&aid=${appId}_${evtName}_other&evtName=${evtName}`; + return eventsEndpoint + '&' + queryStringParamsWithAID; + }, + + /** + * Reports an event to IA's servers. + * @param {string} evtName - event name as string. + * @param {object} extraDetails - e.g., a JS exception JSON object. + * @param shouldSendOnlyToNewEndpoint + */ + reportEvent(evtName, extraDetails) { + const url = this.getEventUrl(evtName, extraDetails); + this.createTrackingPixel(url); + }, + defaults: Helpers.defaultsQsParams, + + /** + * Ia Event Reporting Query String Parameters, not including App Id. + * @param {object} extraDetails - e.g., a JS exception JSON object. + * @return {string} IA event contcatenated queryString parameters. + */ + eventQueryStringParams(extraDetails) { + const toQS = Object.assign({}, this.defaults, {realAppId: extraDetails && extraDetails.appId, timestamp: Date.now()}); + return formatQS(toQS); + }, + + /** + * Creates a tracking pixel by prepending the page's protocol to the URL sent as the param. + * @param {string} urlWithoutProtocol - the URL to send the tracking pixel to, without the protocol as a prefix. + */ + createTrackingPixel(urlWithoutProtocol) { + Tracker.fire([this.getPageProtocol() + urlWithoutProtocol]); + } +}; + +/** + * Url generator - generates a request URL + * @type {{defaultsParams: *, serverParamNameBySettingParamName: {referrer: string, keywords: string, appId: string, portal: string, age: string, gender: string, isSecured: (boolean|null)}, toServerParams: (function(*)), unwantedValues: *[], getUrlParams: (function(*=))}} + */ +const Url = { + defaultsParams: Object.assign({}, Helpers.defaultsQsParams, {f: CONSTANTS.DISPLAY_AD,fs: false,ref: iaRef}), + serverParamNameBySettingParamName: { + referrer: 'ref', + keywords: 'k', + appId: 'aid', + portal: 'po', + age: 'a', + gender: 'g', + }, + unwantedValues: ['', null, undefined], + + /** + * Maps publisher params to server params + * @param params: object {k:v} + * @returns object {k:v} + */ + toServerParams(params){ + const serverParams = {}; + for(const paramName in params){ + if(params.hasOwnProperty(paramName) && this.serverParamNameBySettingParamName.hasOwnProperty(paramName)){ + serverParams[this.serverParamNameBySettingParamName[paramName]] = params[paramName]; + }else{ + serverParams[paramName] = params[paramName]; + } + } + + serverParams.isSecured = Reporter.getPageProtocol() === 'https:' || null; + return serverParams; + }, + + /** + * Prepare querty string to ad server + * @param params: object {k:v} + * @returns : object {k:v} + */ + getUrlParams(params){ + const serverParams = this.toServerParams(params); + const toQueryString = Object.assign({}, this.defaultsParams, serverParams); + for(const paramName in toQueryString){ + if(toQueryString.hasOwnProperty(paramName) && this.unwantedValues.indexOf(toQueryString[paramName]) !== -1){ + delete toQueryString[paramName]; + } + } + toQueryString.fs = params.spotType === CONSTANTS.SPOT_TYPES.INTERSTITIAL; + + if(params.spotType === CONSTANTS.SPOT_TYPES.RECTANGLE){ + toQueryString.rw = CONSTANTS.RECTANGLE_SIZE.W; + toQueryString.rh = CONSTANTS.RECTANGLE_SIZE.H; + } + + if (typeof $$PREBID_GLOBAL$$ !== 'undefined') { + toQueryString.bco = $$PREBID_GLOBAL$$.cbTimeout || $$PREBID_GLOBAL$$.bidderTimeout; + } + + toQueryString.timestamp = Date.now(); + delete toQueryString.qa; + return toQueryString; + } +}; + +/** + * Http helper to extract metadata + * @type {{headers: *[], getBidHeaders: (function(*))}} + */ +const Http = { + headers: [ + CONSTANTS.RESPONSE_HEADERS_NAME.PRICING_VALUE, + CONSTANTS.RESPONSE_HEADERS_NAME.AD_H, + CONSTANTS.RESPONSE_HEADERS_NAME.AD_W + ], + + /** + * Extract headers data + * @param xhr: XMLHttpRequest + * @returns {} + */ + getBidHeaders(xhr){ + const headersData = {}; + this.headers.forEach(headerName => headersData[headerName] = xhr.getResponseHeader(headerName)); + return headersData; + } +}; + + +/** + * InnerActiveAdapter for requesting bids + * @class + */ +class InnerActiveAdapter{ + constructor(){ + this.iaAdapter = Adapter.createNew(CONSTANTS.ADAPTER_NAME); + this.bidByBidId = {}; + } + + /** + * validate if bid request is valid + * @param adSettings: object + * @returns {boolean} + * @private + */ + _isValidRequest(adSettings){ + if(adSettings && adSettings.appId && adSettings.spotType){ + return true; + } + utils.logError('bid requires appId'); + return false; + } + + /** + * Store the bids in a Map object (k: bidId, v: bid)to check later if won + * @param bid + * @returns bid object + * @private + */ + _storeBidRequestDetails(bid){ + this.bidByBidId[bid.bidId] = bid; + return bid; + } + + /** + * @param bidStatus: int ("STATUS": {"GOOD": 1,"NO_BID": 2}) + * @param bidResponse: object + * @returns {type[]} + * @private + */ + _getBidDetails(bidStatus, bidResponse, bidId){ + let bid = bidFactory.createBid(bidStatus, bidResponse); + bid.code = CONSTANTS.ADAPTER_NAME; + bid.bidderCode = bid.code; + if (bidStatus === STATUS.GOOD) { + bid = Object.assign(bid, bidResponse); + this._setBidCpm(bid, bidId); + } + return bid; + } + + _setBidCpm(bid, bidId){ + const storedBid = this.bidByBidId[bidId]; + if(storedBid){ + bid.cpm = storedBid.params && storedBid.params.qa && storedBid.params.qa.cpm || bid.cpm; + bid.cpm = (bid.cpm !== null && !isNaN(bid.cpm)) ? parseFloat(bid.cpm) : 0.0; + } + } + + /** + * Validate if response is valid + * @param responseAsJson : object + * @param headersData: {} + * @returns {boolean} + * @private + */ + _isValidBidResponse(responseAsJson, headersData){ + return (responseAsJson && responseAsJson.ad && responseAsJson.ad.html && headersData && headersData[CONSTANTS.RESPONSE_HEADERS_NAME.PRICING_VALUE] > 0); + } + + /** + * When response is received + * @param response: string(json format) + * @param xhr: XMLHttpRequest + * @param bidId: string + * @private + */ + _onResponse(response, xhr, bidId){ + const bid = this.bidByBidId[bidId]; + const [w, h] = bid.sizes[0]; + const size = {w, h}; + let responseAsJson; + const headersData = Http.getBidHeaders(xhr); + try { + responseAsJson = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + + if (!this._isValidBidResponse(responseAsJson, headersData)) { + let errorMessage = `response failed for ${CONSTANTS.ADAPTER_NAME} adapter`; + utils.logError(errorMessage); + const passback = responseAsJson && responseAsJson.config && responseAsJson.config.passback; + if(passback) { + Tracker.fire([passback]); + } + Reporter.reportEvent('HBPreBidNoAd', bid.params); + return bidManager.addBidResponse(bid.placementCode, this._getBidDetails(STATUS.NO_BID)); + } + const bidResponse = { + cpm: headersData[CONSTANTS.RESPONSE_HEADERS_NAME.PRICING_VALUE]*1000, + width: parseFloat(headersData[CONSTANTS.RESPONSE_HEADERS_NAME.AD_W]) || size.w, + ad: this._getAd(responseAsJson.ad.html, responseAsJson.config.tracking, bid.params), + height: parseFloat(headersData[CONSTANTS.RESPONSE_HEADERS_NAME.AD_H]) || size.h + }; + const auctionBid = this._getBidDetails(STATUS.GOOD, bidResponse, bidId); + bid.adId = auctionBid.adId; + this.bidByBidId[bidId] = bid; + bidManager.addBidResponse(bid.placementCode, auctionBid); + } + + /** + * Returns the ad HTML template + * @param adHtml: string {ad server creative} + * @param tracking: object {impressions, clicks} + * @param bidParams: object + * @returns {string}: create template + * @private + */ + _getAd(adHtml, tracking, bidParams){ + + let impressionsHtml = ''; + if(tracking && Array.isArray(tracking.impressions)){ + let impressions = tracking.impressions; + impressions.push(Reporter.getEventUrl('HBPreBidImpression', bidParams, false)); + impressions.forEach(impression => impression && (impressionsHtml += utils.createTrackPixelHtml(impression))); + } + adHtml = impressionsHtml + adHtml.replace(/ + + + + +
${adHtml}
+ + + `; + return adTemplate; + } + /** + * Adjust bid params to ia-ad-server params + * @param bid: object + * @private + */ + _toIaBidParams(bid){ + const bidParamsWithCustomParams = Object.assign({}, bid.params, bid.params.customParams); + delete bidParamsWithCustomParams.customParams; + bid.params = Helpers.objectToCamel(bidParamsWithCustomParams); + } + + /** + * Prebid executes for stating an auction + * @param bidRequest: object + */ + callBids(bidRequest){ + const bids = bidRequest.bids || []; + bids.forEach(bid => this._toIaBidParams(bid)); + bids + .filter(bid => this._isValidRequest(bid.params)) + .map(bid => this._storeBidRequestDetails(bid)) + .forEach(bid => ajax(this._getEndpointUrl(bid.params), (response, xhr) => this._onResponse(response, xhr, bid.bidId), Url.getUrlParams(bid.params), {method: 'GET'})); + } + + _getEndpointUrl(params){ + return params && params.qa && params.qa.url || Reporter.getPageProtocol() + CONSTANTS.ENDPOINT_URL; + } + + _getStoredBids(){ + const storedBids = []; + for(const bidId in this.bidByBidId){ + if(this.bidByBidId.hasOwnProperty(bidId)) { + storedBids.push(this.bidByBidId[bidId]); + } + } + return storedBids; + } + + /** + * Return internal object - testing + * @returns {{Reporter: {errorEventName: string, pageProtocol: string, getPageProtocol: (function(): string), getEventUrl: (function(*, *=)), reportEvent: (function(string, Object)), defaults: {v: (string|string), page: string, mw: boolean, hb: string}, eventQueryStringParams: (function(Object): string), createTrackingPixel: (function(string))}}} + * @private + */ + static _getUtils(){ + return {Reporter}; + } + + /** + * Creates new instance of InnerActiveAdapter for prebid auction + * @returns {InnerActiveAdapter} + */ + static createNew(){ + return new InnerActiveAdapter(); + } +} +module.exports = InnerActiveAdapter; diff --git a/src/adapters/lifestreet.js b/src/adapters/lifestreet.js index eceecb54608..a28b2da7e0c 100644 --- a/src/adapters/lifestreet.js +++ b/src/adapters/lifestreet.js @@ -92,12 +92,14 @@ const LifestreetAdapter = function LifestreetAdapter() { } catch (e) { return; } - if (object.message && object.message === PREBID_REQUEST_MESSAGE && object.slotName) { + if (object.message && object.message === PREBID_REQUEST_MESSAGE && object.slotName && + window.$$PREBID_GLOBAL$$[object.slotName]) { ev.source.postMessage(JSON.stringify({ message: PREBID_RESPONSE_MESSAGE, slotObject: window.$$PREBID_GLOBAL$$[object.slotName] }), '*'); window.$$PREBID_GLOBAL$$[object.slotName].destroy(); + window.$$PREBID_GLOBAL$$[object.slotName] = null; } }, false); } else { diff --git a/src/adapters/mantis.js b/src/adapters/mantis.js index 5b4b17f1638..6ca3faa0bca 100644 --- a/src/adapters/mantis.js +++ b/src/adapters/mantis.js @@ -206,6 +206,7 @@ module.exports = function () { bids: params.bids.map(function (bid) { return { bidId: bid.bidId, + config: bid.params, sizes: bid.sizes.map(function (size) { return {width: size[0], height: size[1]}; }) diff --git a/src/adapters/openx.js b/src/adapters/openx.js index 21c5ba3476d..42314e7f286 100644 --- a/src/adapters/openx.js +++ b/src/adapters/openx.js @@ -196,7 +196,8 @@ const OpenxAdapter = function OpenxAdapter() { function callBids(params) { let isIfr, bids = params.bids || [], - currentURL = window.location.href && encodeURIComponent(window.location.href); + currentURL = (window.parent !== window) ? document.referrer : window.location.href; + currentURL = currentURL && encodeURIComponent(currentURL); try { isIfr = window.self !== window.top; } diff --git a/src/adapters/pubmatic.js b/src/adapters/pubmatic.js index c039c4eac7a..3f0a54be2cb 100644 --- a/src/adapters/pubmatic.js +++ b/src/adapters/pubmatic.js @@ -17,6 +17,7 @@ var PubmaticAdapter = function PubmaticAdapter() { function _callBids(params) { bids = params.bids; + _pm_optimize_adslots = []; for (var i = 0; i < bids.length; i++) { var bid = bids[i]; //bidmanager.pbCallbackMap['' + bid.params.adSlot] = bid; diff --git a/src/adapters/pulsepoint.js b/src/adapters/pulsepoint.js index aaad60ee311..e206c32550a 100644 --- a/src/adapters/pulsepoint.js +++ b/src/adapters/pulsepoint.js @@ -5,7 +5,7 @@ var utils = require('../utils.js'); var PulsePointAdapter = function PulsePointAdapter() { - var getJsStaticUrl = window.location.protocol + '//tag.contextweb.com/getjs.static.js'; + var getJsStaticUrl = window.location.protocol + '//tag-st.contextweb.com/getjs.static.js'; var bidUrl = window.location.protocol + '//bid.contextweb.com/header/tag'; function _callBids(params) { diff --git a/src/adapters/pulsepointLite.js b/src/adapters/pulsepointLite.js new file mode 100644 index 00000000000..49900bc2c08 --- /dev/null +++ b/src/adapters/pulsepointLite.js @@ -0,0 +1,90 @@ +import {createBid} from 'src/bidfactory'; +import {addBidResponse} from 'src/bidmanager'; +import {logError,getTopWindowLocation} from 'src/utils'; +import {ajax} from 'src/ajax'; +import {STATUS} from 'src/constants'; + +function PulsePointLiteAdapter() { + + const bidUrl = window.location.protocol + '//bid.contextweb.com/header/tag?'; + const ajaxOptions = { + method: 'GET', + withCredentials: true, + contentType: 'text/plain' + }; + + function _callBids(bidderRequest) { + bidderRequest.bids.forEach(bidRequest => { + try { + var params = Object.assign({}, environment(), bidRequest.params); + var url = bidUrl + Object.keys(params).map(k => k + '=' + encodeURIComponent(params[k])).join('&'); + ajax(url, (bidResponse) => { + bidResponseAvailable(bidRequest, bidResponse); + }, null, ajaxOptions); + } catch(e) { + //register passback on any exceptions while attempting to fetch response. + logError('pulsepoint.requestBid', 'ERROR', e); + bidResponseAvailable(bidRequest); + } + }); + } + + function environment() { + return { + cn: 1, + ca: 'BID', + tl: 1, + 'if': 0, + cwu: getTopWindowLocation().href, + cwr: referrer(), + dw: document.documentElement.clientWidth, + cxy: document.documentElement.clientWidth + ',' + document.documentElement.clientHeight, + tz: new Date().getTimezoneOffset(), + ln: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage) + }; + } + + function referrer() { + try { + return window.top.document.referrer; + } catch (e) { + return document.referrer; + } + } + + function bidResponseAvailable(bidRequest, rawResponse) { + if (rawResponse) { + var bidResponse = parse(rawResponse); + if(bidResponse) { + var adSize = bidRequest.params.cf.toUpperCase().split('X'); + var bid = createBid(STATUS.GOOD, bidRequest); + bid.bidderCode = bidRequest.bidder; + bid.cpm = bidResponse.bidCpm; + bid.ad = bidResponse.html; + bid.width = adSize[0]; + bid.height = adSize[1]; + addBidResponse(bidRequest.placementCode, bid); + return; + } + } + var passback = createBid(STATUS.NO_BID, bidRequest); + passback.bidderCode = bidRequest.bidder; + addBidResponse(bidRequest.placementCode, passback); + } + + function parse(rawResponse) { + try { + return JSON.parse(rawResponse); + } catch (ex) { + logError('pulsepoint.safeParse', 'ERROR', ex); + return null; + } + } + + return { + callBids: _callBids + }; + +} + +module.exports = PulsePointLiteAdapter; diff --git a/src/adapters/rhythmone.js b/src/adapters/rhythmone.js index 77ab3e4cd13..57b6e29a217 100644 --- a/src/adapters/rhythmone.js +++ b/src/adapters/rhythmone.js @@ -1,13 +1,12 @@ var bidmanager = require('../bidmanager.js'), bidfactory = require('../bidfactory.js'), - utils = require('../utils.js'), CONSTANTS = require('../constants.json'); import {ajax as ajax} from '../ajax'; -function track(debug, p1, p2, p3) { +function track(debug) { if(debug === true){ - console.log('GA: %s %s %s', p1, p2, p3 || ''); + //console.log('GA: %s %s %s', p1, p2, p3 || ''); } } @@ -19,8 +18,6 @@ module.exports = function(bidManager, global, loader){ var version = "0.9.0.0", defaultZone = "1r", defaultPath = "mvo", - bidfloor = 0, - currency = "USD", debug = false, auctionEnded = false, requestCompleted = false, @@ -43,33 +40,45 @@ module.exports = function(bidManager, global, loader){ }); } - function load(bidParams, url, postData, callback){ - if(bidParams.method === "get"){ - loader(url, function(responseText, response){ - if(response.status === 200) - callback(200, "success", response.responseText); - else - callback(-1, "http error "+response.status, response.responseText); - }, false, {method:"GET", withCredentials: true}); - } - else{ - loader(url, function(responseText, response){ - if(response.status === 200) - callback(200, "success", response.responseText); - else - callback(-1, "http error "+response.status, response.responseText); - }, postData, {method:"POST", contentType: "application/json", withCredentials: true}); - } + function load(bidParams, url, callback){ + loader(url, function(responseText, response){ + if(response.status === 200) + callback(200, "success", response.responseText); + else + callback(-1, "http error "+response.status, response.responseText); + }, false, {method:"GET", withCredentials: true}); } + function flashInstalled(){ + var n = global.navigator, + p = n.plugins, + m = n.mimeTypes, + t = "application/x-shockwave-flash", + x = global.ActiveXObject; + + if(p && + p["Shockwave Flash"] && + m && + m[t] && + m[t].enabledPlugin) + return true; + + if(x){ + try{if((new global.ActiveXObject("ShockwaveFlash.ShockwaveFlash"))) return true;} + catch(e){} + } + + return false; + } + var bidderCode = "rhythmone", bidLostTimeout = null; - function setIfPresent(o, key, value){ + function attempt(valueFunction, defaultValue){ try{ - if(typeof value === "function") - o[key] = value(); + return valueFunction(); }catch(ex){} + return defaultValue; } function logToConsole(txt){ @@ -124,7 +133,7 @@ module.exports = function(bidManager, global, loader){ } } - function getRMPURL(bidParams, ortbJSON, bids){ + function getRMPURL(bidParams, bids){ var endpoint = "//tag.1rx.io/rmp/{placementId}/0/{path}?z={zone}", query = []; @@ -150,120 +159,77 @@ module.exports = function(bidManager, global, loader){ }); function p(k,v){ + if(v instanceof Array) + v = v.join(","); if(typeof v !== "undefined") query.push(encodeURIComponent(k)+"="+encodeURIComponent(v)); } + + p("domain", attempt(function(){ + var d = global.document.location.ancestorOrigins; + if(d && d.length > 0) + return d[d.length-1]; + return global.top.document.location.hostname; + },"")); + p("title", attempt(function(){return global.top.document.title;},"")); + p("url", attempt(function(){ + var l; + try{l = global.top.document.location.href.toString();} + catch(ex){l = global.document.location.href.toString();} + return l; + },"")); + p("dsh", (global.screen ? global.screen.height : "")); + p("dsw", (global.screen ? global.screen.width : "")); + p("tz", (new Date()).getTimezoneOffset()); + p("dtype", ((/(ios|ipod|ipad|iphone|android)/i).test(global.navigator.userAgent) ? 1 : ((/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(global.navigator.userAgent) ? 3 : 2))); + p("flash", (flashInstalled() ? 1 : 0)); - if(bidParams.method === "get"){ - - p("domain", ortbJSON.site.domain); - p("title", ortbJSON.site.name); - p("url", ortbJSON.site.page); - p("dsh", ortbJSON.device.h); - p("dsw", ortbJSON.device.w); - p("tz", (new Date()).getTimezoneOffset()); - p("dtype", ortbJSON.device.devicetype); + var placementCodes = [], + heights = [], + widths = [], + floors = [], + mediaTypes = [], + fat = /(^v|(\.0)+$)/gi, + i=0; + + p("hbv", global.$$PREBID_GLOBAL$$.version.replace(fat,"")+","+version.replace(fat,"")); + + for(; i 0 && typeof bids[i].sizes[0] === "number") + bids[i].sizes = [bids[i].sizes]; - for(var i = 0; i 0) - return d[d.length-1]; - return global.top.document.location.hostname; - }); - setIfPresent(o.site, "name", function(){return global.top.document.title;}); - - o.device.devicetype = ((/(ios|ipod|ipad|iphone|android)/i).test(global.navigator.userAgent) ? 1 : ((/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(global.navigator.userAgent) ? 3 : 2)); - - setIfPresent(o.device, "h", function(){return global.screen.height;}); - setIfPresent(o.device, "w", function(){return global.screen.width;}); - - for(var i = 0; i queryString.push(`tg_v.${key}`, item)); } @@ -290,6 +319,12 @@ function RubiconAdapter() { [bid.width, bid.height] = sizeMap[ad.size_id].split('x').map(num => Number(num)); } + // add server-side targeting + bid.rubiconTargeting = (Array.isArray(ad.targeting) ? ad.targeting : []) + .reduce((memo, item) => { + memo[item.key] = item.values[0]; + return memo; + }, {'rpfl_elemid': bidRequest.placementCode}); try { bidmanager.addBidResponse(bidRequest.placementCode, bid); diff --git a/src/adapters/serverbid.js b/src/adapters/serverbid.js new file mode 100644 index 00000000000..74b731bbe3f --- /dev/null +++ b/src/adapters/serverbid.js @@ -0,0 +1,163 @@ +import Adapter from 'src/adapters/adapter'; +import bidfactory from 'src/bidfactory'; +import bidmanager from 'src/bidmanager'; +import * as utils from 'src/utils'; +import { ajax } from 'src/ajax'; + +const ServerBidAdapter = function ServerBidAdapter() { + + const baseAdapter = Adapter.createNew('serverbid'); + + const BASE_URI = '//e.serverbid.com/api/v2'; + + 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" + ]; + + const bidIds = []; + + baseAdapter.callBids = function(params) { + + if (params && params.bids && utils.isArray(params.bids) && params.bids.length) { + + const data = { + placements: [], + time: Date.now(), + user: {}, + url: utils.getTopWindowUrl(), + referrer: document.referrer, + enableBotFiltering: true, + includePricingData: true + }; + + const bids = params.bids || []; + for (let i = 0; i < bids.length; i++) { + const bid = bids[i]; + + bidIds.push(bid.bidId); + + const bid_data = { + networkId: bid.params.networkId, + siteId: bid.params.siteId, + zoneIds: bid.params.zoneIds, + campaignId: bid.params.campaignId, + flightId: bid.params.flightId, + adId: bid.params.adId, + divName: bid.bidId, + adTypes: bid.adTypes || getSize(bid.sizes) + }; + + if (bid_data.networkId && bid_data.siteId) { + data.placements.push(bid_data); + } + + } + + if (data.placements.length) { + ajax(BASE_URI, _responseCallback, JSON.stringify(data), { method: 'POST', withCredentials: true, contentType: 'application/json' }); + } + + } + + }; + + function _responseCallback(result) { + + let bid; + let bidId; + let bidObj; + let bidCode; + let placementCode; + + try { + result = JSON.parse(result); + } catch (error) { + utils.logError(error); + } + + for (let i = 0; i < bidIds.length; i++) { + + bidId = bidIds[i]; + bidObj = utils.getBidRequest(bidId); + bidCode = bidObj.bidder; + placementCode = bidObj.placementCode; + + if (result) { + const decision = result.decisions && result.decisions[bidId]; + const price = decision && decision.pricing && decision.pricing.clearPrice; + + if (decision && price) { + bid = bidfactory.createBid(1, bidObj); + bid.bidderCode = bidCode; + bid.cpm = price; + bid.width = decision.width; + bid.height = decision.height; + bid.ad = retrieveAd(decision); + } else { + bid = bidfactory.createBid(2, bidObj); + bid.bidderCode = bidCode; + } + + } else { + bid = bidfactory.createBid(2, bidObj); + bid.bidderCode = bidCode; + } + bidmanager.addBidResponse(placementCode, bid); + } + } + + function retrieveAd(decision) { + return decision.contents && decision.contents[0] && decision.contents[0].body + utils.createTrackPixelHtml(decision.impressionUrl); + } + + function getSize(sizes) { + const result = []; + sizes.forEach(function(size) { + const index = sizeMap.indexOf(size[0] + "x" + size[1]); + if (index >= 0) { + result.push(index); + } + }); + return result; + } + + // Export the `callBids` function, so that Prebid.js can execute + // this function when the page asks to send out bid requests. + return { + callBids: baseAdapter.callBids + }; + +}; + +ServerBidAdapter.createNew = function() { + return new ServerBidAdapter(); +}; + +module.exports = ServerBidAdapter; \ No newline at end of file diff --git a/src/adapters/sharethrough.js b/src/adapters/sharethrough.js index a9d26ecf23e..0f981ca9551 100644 --- a/src/adapters/sharethrough.js +++ b/src/adapters/sharethrough.js @@ -1,9 +1,10 @@ var utils = require('../utils.js'); var bidmanager = require('../bidmanager.js'); var bidfactory = require('../bidfactory.js'); +var ajax = require('../ajax.js').ajax; const STR_BIDDER_CODE = "sharethrough"; -const STR_VERSION = "0.1.0"; //Need to check analytics too for version +const STR_VERSION = "1.2.0"; var SharethroughAdapter = function SharethroughAdapter() { @@ -11,62 +12,42 @@ var SharethroughAdapter = function SharethroughAdapter() { str.STR_BTLR_HOST = document.location.protocol + "//btlr.sharethrough.com"; str.STR_BEACON_HOST = document.location.protocol + "//b.sharethrough.com/butler?"; str.placementCodeSet = {}; + str.ajax = ajax; function _callBids(params) { const bids = params.bids; - addEventListener("message", _receiveMessage, false); - // cycle through bids for (let i = 0; i < bids.length; i += 1) { const bidRequest = bids[i]; str.placementCodeSet[bidRequest.placementCode] = bidRequest; const scriptUrl = _buildSharethroughCall(bidRequest); - str.loadIFrame(scriptUrl); + str.ajax(scriptUrl, _createCallback(bidRequest), undefined, {withCredentials: true}); } } + function _createCallback(bidRequest) { + return (bidResponse) => { + _strcallback(bidRequest, bidResponse); + }; + } + function _buildSharethroughCall(bid) { - const testPkey = 'test'; const pkey = utils.getBidIdParameter('pkey', bid.params); let host = str.STR_BTLR_HOST; let url = host + "/header-bid/v1?"; url = utils.tryAppendQueryString(url, 'bidId', bid.bidId); - - if(pkey !== testPkey) { - url = utils.tryAppendQueryString(url, 'placement_key', pkey); - url = utils.tryAppendQueryString(url, 'ijson', '$$PREBID_GLOBAL$$.strcallback'); - url = appendEnvFields(url); - } else { - url = url.substring(0, url.length - 1); - } + url = utils.tryAppendQueryString(url, 'placement_key', pkey); + url = appendEnvFields(url); return url; } - str.loadIFrame = function(url) { - const iframe = document.createElement("iframe"); - iframe.src = url; - iframe.style.cssText = 'display:none;'; - - document.body.appendChild(iframe); - }; - - function _receiveMessage(event) { - if(event.origin === str.STR_BTLR_HOST) { - try { - $$PREBID_GLOBAL$$.strcallback(JSON.parse(event.data).response); - } catch(e) { - console.log(e); - } - } - } - - $$PREBID_GLOBAL$$.strcallback = function(bidResponse) { + function _strcallback(bidObj, bidResponse) { + bidResponse = JSON.parse(bidResponse); const bidId = bidResponse.bidId; - const bidObj = utils.getBidRequest(bidId); try { const bid = bidfactory.createBid(1, bidObj); bid.bidderCode = STR_BIDDER_CODE; @@ -102,7 +83,7 @@ var SharethroughAdapter = function SharethroughAdapter() { } catch (e) { _handleInvalidBid(bidObj); } - }; + } function _handleInvalidBid(bidObj) { const bid = bidfactory.createBid(2, bidObj); @@ -120,6 +101,7 @@ var SharethroughAdapter = function SharethroughAdapter() { return { callBids: _callBids, str : str, + callback: _strcallback }; }; diff --git a/src/adapters/smartadserver.js b/src/adapters/smartadserver.js index 82498dd2787..f35923411bb 100644 --- a/src/adapters/smartadserver.js +++ b/src/adapters/smartadserver.js @@ -14,9 +14,11 @@ var SmartAdServer = function SmartAdServer() { bidObject = bidfactory.createBid(1); bidObject.bidderCode = 'smartadserver'; bidObject.cpm = adUnit.cpm; + bidObject.currency = adUnit.currency; bidObject.ad = adUnit.ad; bidObject.width = adUnit.width; bidObject.height = adUnit.height; + bidObject.dealId = adUnit.dealId; bidmanager.addBidResponse(bid.placementCode, bidObject); } else { utils.logMessage(`[SmartAdServer] no bid response for placementCode ${bid.placementCode}`); @@ -39,6 +41,7 @@ var SmartAdServer = function SmartAdServer() { "siteid": bid.params.siteId, "pgid": bid.params.pageId, "fmtid": bid.params.formatId, + "ccy": bid.params.currency || "USD", "tgt": encodeURIComponent(bid.params.target || ''), "tag": bid.placementCode, "sizes": bid.sizes.map(size => size[0] + "x" + size[1]).join(","), diff --git a/src/adapters/stickyadstv.js b/src/adapters/stickyadstv.js index 803cd626d56..221898df1f0 100644 --- a/src/adapters/stickyadstv.js +++ b/src/adapters/stickyadstv.js @@ -1,9 +1,11 @@ +var Adapter = require('./adapter.js'); var bidfactory = require('../bidfactory.js'); var bidmanager = require('../bidmanager.js'); var adloader = require('../adloader.js'); var StickyAdsTVAdapter = function StickyAdsTVAdapter() { + var STICKYADS_BIDDERCODE = 'stickyadstv'; var MUSTANG_URL = "//cdn.stickyadstv.com/mustang/mustang.min.js"; var INTEXTROLL_URL = "//cdn.stickyadstv.com/prime-time/intext-roll.min.js"; var SCREENROLL_URL = "//cdn.stickyadstv.com/prime-time/screen-roll.min.js"; @@ -225,7 +227,7 @@ var StickyAdsTVAdapter = function StickyAdsTVAdapter() { if(valid && priceData) { // valid bid response bidObject = bidfactory.createBid(1, bidRequest); - bidObject.bidderCode = 'stickyadstv'; + bidObject.bidderCode = bidRequest.bidder; bidObject.cpm = priceData.price; bidObject.currencyCode = priceData.currency; bidObject.ad = html; @@ -236,7 +238,7 @@ var StickyAdsTVAdapter = function StickyAdsTVAdapter() { else { // invalid bid response bidObject = bidfactory.createBid(2, bidRequest); - bidObject.bidderCode = 'stickyadstv'; + bidObject.bidderCode = bidRequest.bidder; } return bidObject; } @@ -277,16 +279,19 @@ var StickyAdsTVAdapter = function StickyAdsTVAdapter() { }; - // Export the callBids function, so that prebid.js can execute - // this function when the page asks to send out bid requests. - return { + return Object.assign(Adapter.createNew(STICKYADS_BIDDERCODE), { callBids: _callBids, formatBidObject: formatBidObject, formatAdHTML: formatAdHTML, getBiggerSize:getBiggerSize, getBid:getBid, - getTopMostWindow:getTopMostWindow - }; + getTopMostWindow:getTopMostWindow, + createNew: StickyAdsTVAdapter.createNew //enable alias feature (to be used for freewheel-ssp alias) + }); +}; + +StickyAdsTVAdapter.createNew = function() { + return new StickyAdsTVAdapter(); }; module.exports = StickyAdsTVAdapter; \ No newline at end of file diff --git a/src/adapters/tapsense.js b/src/adapters/tapsense.js new file mode 100644 index 00000000000..f656c580546 --- /dev/null +++ b/src/adapters/tapsense.js @@ -0,0 +1,87 @@ +//v0.0.1 + +const bidfactory = require('../bidfactory.js'); +const bidmanager = require('../bidmanager.js'); +const adloader = require('../adloader'); +const utils = require('../utils.js'); + +const TapSenseAdapter = function TapSenseAdapter() { + const version = "0.0.1"; + const creativeSizes = [ + "320x50" + ]; + const validParams = [ + "ufid", + "refer", + "ad_unit_id", //required + "device_id", + "lat", + "long", + "user", //required + "price_floor", + "test" + ]; + const SCRIPT_URL = "https://ads04.tapsense.com/ads/headerad"; + let bids; + $$PREBID_GLOBAL$$.tapsense = {}; + function _callBids(params) { + bids = params.bids || []; + for (let i = 0; i < bids.length; i++) { + let bid = bids[i]; + let isValidSize = false; + if (!bid.sizes || !bid.params.user || !bid.params.ad_unit_id) { + return; + } + let parsedSizes = utils.parseSizesInput(bid.sizes); + for (let k = 0; k < parsedSizes.length; k++) { + if (creativeSizes.indexOf(parsedSizes[k]) > -1) { + isValidSize = true; + break; + } + } + if (isValidSize) { + let queryString = `?price=true&jsonp=1&callback=$$PREBID_GLOBAL$$.tapsense.callback_with_price_${bid.bidId}&version=${version}&`; + $$PREBID_GLOBAL$$.tapsense[`callback_with_price_${bid.bidId}`] = generateCallback(bid.bidId); + let keys = Object.keys(bid.params); + for (let j = 0; j < keys.length; j++) { + if (validParams.indexOf(keys[j]) < 0) continue; + queryString += encodeURIComponent(keys[j]) + "=" + encodeURIComponent(bid.params[keys[j]]) + "&"; + } + _requestBids(SCRIPT_URL + queryString); + } + } + } + + function generateCallback(bidId){ + return function tapsenseCallback(response, price) { + let bidObj; + if (response && price) { + let bidReq = utils.getBidRequest(bidId); + if (response.status.value === "ok" && response.count_ad_units > 0) { + bidObj = bidfactory.createBid(1, bidObj); + bidObj.cpm = price; + bidObj.width = response.width; + bidObj.height = response.height; + bidObj.ad = response.ad_units[0].html; + } else { + bidObj = bidfactory.createBid(2, bidObj); + } + bidObj.bidderCode = bidReq.bidder; + bidmanager.addBidResponse(bidReq.placementCode, bidObj); + + } else { + utils.logMessage('No prebid response'); + } + }; + } + + function _requestBids(scriptURL) { + adloader.loadScript(scriptURL); + } + + return { + callBids: _callBids + }; +}; + +module.exports = TapSenseAdapter; diff --git a/src/adapters/thoughtleadr.js b/src/adapters/thoughtleadr.js new file mode 100644 index 00000000000..2237adb89f5 --- /dev/null +++ b/src/adapters/thoughtleadr.js @@ -0,0 +1,101 @@ +"use strict"; +var bidfactory = require("../bidfactory"); +var bidmanager = require("../bidmanager"); +var utils = require('../utils'); +var adloader_1 = require("../adloader"); +var ROOT_URL = "//cdn.thoughtleadr.com/v4/"; +var BID_AVAILABLE = 1; + +var ThoughtleadrAdapter = (function () { + function ThoughtleadrAdapter() { + } + + ThoughtleadrAdapter.prototype.callBids = function (params) { + if (!window.tldr || !window.tldr.requestPrebid) { + var rootUrl = ROOT_URL; + if (window.tldr && window.tldr.config && window.tldr.config.root_url) { + rootUrl = window.tldr.config.root_url; + } + adloader_1.loadScript(rootUrl + "page.js", this.handleBids.bind(this, params), true); + } + else { + this.handleBids(params); + } + }; + + ThoughtleadrAdapter.prototype.handleBids = function (params) { + var bids = (params.bids || []).filter(function (bid) { + return ThoughtleadrAdapter.valid(bid); + }); + + for (var _i = 0, bids_1 = bids; _i < bids_1.length; _i++) { + var bid = bids_1[_i]; + this.requestPlacement(bid); + } + }; + + ThoughtleadrAdapter.prototype.requestPlacement = function (bid) { + var _this = this; + var rid = utils.generateUUID(null); + var size = ThoughtleadrAdapter.getSizes(bid.sizes); + + window.tldr.requestPrebid(bid.params.placementId, rid).then(function (params) { + if (!params || !params.bid) { + utils.logError("invalid response from tldr.requestPrebid", undefined, undefined); + return; + } + + _this.receiver = function (ev) { + if (ev.origin === location.origin && + ev.data && ev.data.TLDR_REQUEST && ev.data.TLDR_REQUEST.rid === rid) { + ev.source.postMessage({TLDR_RESPONSE: {config: params.config, rid: rid}}, location.origin); + } + _this.stopListen(); + }; + window.addEventListener("message", _this.receiver, false); + setTimeout(function () { + return _this.stopListen(); + }, 5000); + + var bidObject; + if (params.bid.code === BID_AVAILABLE) { + bidObject = bidfactory.createBid(params.bid.code); + bidObject.bidderCode = 'thoughtleadr'; + bidObject.cpm = params.bid.cpm; + bidObject.ad = params.bid.ad; + bidObject.width = size.width; + bidObject.height = size.height; + } + else { + bidObject = bidfactory.createBid(params.bid.code); + bidObject.bidderCode = 'thoughtleadr'; + } + bidmanager.addBidResponse(bid.placementCode, bidObject); + }); + }; + + ThoughtleadrAdapter.prototype.stopListen = function () { + if (this.receiver) { + window.removeEventListener("message", this.receiver); + this.receiver = undefined; + } + }; + + ThoughtleadrAdapter.valid = function (bid) { + return !!(bid && bid.params && typeof bid.params.placementId === "string"); + }; + + ThoughtleadrAdapter.getSizes = function (sizes) { + var first = sizes[0]; + if (Array.isArray(first)) { + return ThoughtleadrAdapter.getSizes(first); + } + return { + width: sizes[0], + height: sizes[1] + }; + }; + return ThoughtleadrAdapter; +}()); + +module.exports = ThoughtleadrAdapter; diff --git a/src/adapters/underdogmedia.js b/src/adapters/underdogmedia.js index 79843eb2c37..c467c3ee420 100644 --- a/src/adapters/underdogmedia.js +++ b/src/adapters/underdogmedia.js @@ -5,71 +5,108 @@ var utils = require('../utils.js'); var UnderdogMediaAdapter = function UnderdogMediaAdapter() { - var getJsStaticUrl = window.location.protocol + '//bid.underdog.media/udm_header_lib.js'; + const UDM_ADAPTER_VERSION = '1.0.0'; + var getJsStaticUrl = window.location.protocol + '//udmserve.net/udm/img.fetch?tid=1;dt=9;callback=$$PREBID_GLOBAL$$.handleUnderdogMediaCB;'; + var bidParams = {}; function _callBids(params) { - if (typeof window.udm_header_lib === 'undefined') { - adloader.loadScript(getJsStaticUrl, function () { bid(params); }); - } else { - bid(params); - } - } + bidParams = params; + var sizes = []; + var siteId = 0; - function bid(params) { - var bids = params.bids; - var mapped_bids = []; - for (var i = 0; i < bids.length; i++) { - var bidRequest = bids[i]; - var callback = bidResponseCallback(bidRequest); - mapped_bids.push({ - sizes: bidRequest.sizes, - siteId: bidRequest.params.siteId, - bidfloor: bidRequest.params.bidfloor, - placementCode: bidRequest.placementCode, - divId: bidRequest.params.divId, - subId: bidRequest.params.subId, - callback: callback - }); + for (var bidParam of bidParams.bids) { + sizes = utils.flatten(sizes,utils.parseSizesInput(bidParam.sizes)); + siteId = bidParam.params.siteId; } - var udmBidRequest = new window.udm_header_lib.BidRequestArray(mapped_bids); - udmBidRequest.send(); + adloader.loadScript(getJsStaticUrl + "sid=" + siteId + ";sizes=" + sizes.join(","), null, false); } - function bidResponseCallback(bid) { - return function (bidResponse) { - bidResponseAvailable(bid, bidResponse); - }; - } + function _callback(response) { + + var mids = response.mids; + for (var bidParam of bidParams.bids) { + + var filled = false; + for (var mid of mids) { + + if (mid.useCount > 0) { + continue; + } + if (!mid.useCount) { + mid.useCount = 0; + } + var size_not_found = true; + for (var size of utils.parseSizesInput(bidParam.sizes)) { + if (size === mid.width + 'x' + mid.height) { + size_not_found = false; + } + } + if (size_not_found) { + continue; + } + + var bid = bidfactory.createBid(1, bidParam); + bid.bidderCode = bidParam.bidder; + bid.width = mid.width; + bid.height = mid.height; - function bidResponseAvailable(bidRequest, bidResponse) { - if(bidResponse.bids.length > 0){ - for(var i = 0; i < bidResponse.bids.length; i++){ - var udm_bid = bidResponse.bids[i]; - var bid = bidfactory.createBid(1); - bid.bidderCode = bidRequest.bidder; - bid.cpm = udm_bid.cpm; - bid.width = udm_bid.width; - bid.height = udm_bid.height; - - if(udm_bid.ad_url !== undefined){ - bid.adUrl = udm_bid.ad_url; + bid.cpm = parseFloat(mid.cpm); + if (bid.cpm <= 0) { + continue; } - else if(udm_bid.ad_html !== undefined){ - bid.ad = udm_bid.ad_html; - }else{ - utils.logMessage('Underdogmedia bid is lacking both ad_url and ad_html, skipping bid'); + mid.useCount++; + bid.ad = mid.ad_code_html; + bid.ad = _makeNotification(bid, mid, bidParam) + bid.ad; + if (!(bid.ad || bid.adUrl)) { continue; } + bidmanager.addBidResponse(bidParam.placementCode, bid); + filled = true; + break; + } + if (!filled) { + var nobid = bidfactory.createBid(2, bidParam); + nobid.bidderCode = bidParam.bidder; + bidmanager.addBidResponse(bidParam.placementCode, nobid); + } + } + } + + $$PREBID_GLOBAL$$.handleUnderdogMediaCB = _callback; + + function _makeNotification(bid, mid, bidParam) { - bidmanager.addBidResponse(bidRequest.placementCode, bid); + var url = mid.notification_url; + + url += UDM_ADAPTER_VERSION; + url += ";cb=" + Math.random(); + url += ";qqq=" + (1 / bid.cpm); + url += ";hbt=" + $$PREBID_GLOBAL$$.bidderTimeout; + url += ';style=adapter'; + url += ';vis=' + encodeURIComponent(document.visibilityState); + + url += ';traffic_info=' + encodeURIComponent(JSON.stringify(_getUrlVars())); + if (bidParam.params.subId) { + url += ';subid=' + encodeURIComponent(bidParam.params.subId); + } + return ""; + } + + function _getUrlVars() { + var vars = {}; + var hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + if (!hash[0].match(/^utm/)) { + continue; } - }else{ - var nobid = bidfactory.createBid(2); - nobid.bidderCode = bidRequest.bidder; - bidmanager.addBidResponse(bidRequest.placementCode, nobid); + vars[hash[0]] = hash[1].substr(0, 150); } + return vars; } + return { callBids: _callBids }; diff --git a/src/adapters/vertamedia.js b/src/adapters/vertamedia.js index ea9e5402b09..13f036764d7 100644 --- a/src/adapters/vertamedia.js +++ b/src/adapters/vertamedia.js @@ -35,17 +35,36 @@ function VertamediaAdapter() { } bidRequest = bid; - bidRequest.width = parseInt(bid.sizes[0], 10) || undefined; - bidRequest.height = parseInt(bid.sizes[1], 10) || undefined; + + let size = getSize(bid.sizes); + + bidRequest.width = size.width; + bidRequest.height = size.height; return { aid: bid.params.aid, - w: parseInt(bid.sizes[0], 10) || undefined, - h: parseInt(bid.sizes[1], 10) || undefined, + w: size.width, + h: size.height, domain: document.location.hostname }; } + function getSize(requestSizes) { + var parsed = {}, + size = utils.parseSizesInput(requestSizes)[0]; + + if (typeof size !== 'string') { + return parsed; + } + + let parsedSize = size.toUpperCase().split('X'); + + return { + width: parseInt(parsedSize[0], 10) || undefined, + height: parseInt(parsedSize[1], 10) || undefined + }; + } + /* Notify Prebid of bid responses so bids can get in the auction */ function handleResponse(response) { var parsed; diff --git a/src/adapters/wideorbit.js b/src/adapters/wideorbit.js index 8b051af934d..42356991577 100644 --- a/src/adapters/wideorbit.js +++ b/src/adapters/wideorbit.js @@ -4,7 +4,7 @@ var bidfactory = require('../bidfactory.js'), adloader = require('../adloader'); var WideOrbitAdapter = function WideOrbitAdapter() { - var pageImpression = 'JSAdservingMP.ashx?pc={pc}&pbId={pbId}&clk=&exm=&jsv=1.0&tsv=1.0&cts={cts}&arp=0&fl=0&vitp=&vit=&jscb=window.$$PREBID_GLOBAL$$.handleWideOrbitCallback&url=&fp=&oid=&exr=&mraid=&apid=&apbndl=&mpp=0&uid=&cb={cb}&hb=1', + var pageImpression = 'JSAdservingMP.ashx?pc={pc}&pbId={pbId}&clk=&exm=&jsv=1.0&tsv=1.0&cts={cts}&arp=0&fl=0&vitp=&vit=&jscb=window.$$PREBID_GLOBAL$$.handleWideOrbitCallback&url={referrer}&fp=&oid=&exr=&mraid=&apid=&apbndl=&mpp=0&uid=&cb={cb}&hb=1', pageRepeatCommonParam = '&gid{o}={gid}&pp{o}=&clk{o}=&rpos{o}={rpos}&ecpm{o}={ecpm}&ntv{o}=&ntl{o}=&adsid{o}=', pageRepeatParamId = '&pId{o}={pId}&rank{o}={rank}', pageRepeatParamNamed = '&wsName{o}={wsName}&wName{o}={wName}&rank{o}={rank}&bfDim{o}={width}x{height}&subp{o}={subp}', @@ -17,7 +17,7 @@ var WideOrbitAdapter = function WideOrbitAdapter() { return; } - var properties = ['site', 'page', 'width', 'height', 'rank', 'subPublisher', 'ecpm', 'atf', 'pId', 'pbId'], + var properties = ['site', 'page', 'width', 'height', 'rank', 'subPublisher', 'ecpm', 'atf', 'pId', 'pbId', 'referrer'], prop; utils._each(properties, function (correctName) { @@ -82,12 +82,13 @@ var WideOrbitAdapter = function WideOrbitAdapter() { ]); } - function _setupAdCall(publisherId, placementCount, placementsComponent) { + function _setupAdCall(publisherId, placementCount, placementsComponent, referrer) { return _setParams(base + pageImpression, [ ['pbId', publisherId], ['pc', placementCount], ['cts', new Date().getTime()], - ['cb', Math.floor(Math.random() * 100000000)] + ['cb', Math.floor(Math.random() * 100000000)], + ['referrer', encodeURIComponent(referrer || '')] ]) + placementsComponent; } @@ -104,7 +105,7 @@ var WideOrbitAdapter = function WideOrbitAdapter() { function _callBids(params) { var publisherId, bidUrl = '', - i; + i, referrer; bids = params.bids || []; @@ -116,10 +117,11 @@ var WideOrbitAdapter = function WideOrbitAdapter() { _fixParamNames(requestParams); publisherId = requestParams.pbId; + referrer = referrer || requestParams.referrer; bidUrl += _setupPlacementParameters(i, requestParams); } - bidUrl = _setupAdCall(publisherId, bids.length, bidUrl); + bidUrl = _setupAdCall(publisherId, bids.length, bidUrl, referrer); utils.logMessage('Calling WO: ' + bidUrl); @@ -138,7 +140,7 @@ var WideOrbitAdapter = function WideOrbitAdapter() { case 'iframe': createdElem = utils.createInvisibleIframe(); break; - case 'javascript': + case 'js': createdElem = document.createElement('script'); createdElem.type = 'text/javascript'; createdElem.async = true; diff --git a/src/adapters/xhb.js b/src/adapters/xhb.js index 722d97ebc91..2b00844e2a1 100644 --- a/src/adapters/xhb.js +++ b/src/adapters/xhb.js @@ -8,6 +8,25 @@ const bidfactory = require('../bidfactory.js'); const XhbAdapter = function XhbAdapter() { + const _defaultBidderSettings = { + alwaysUseBid: true, + adserverTargeting: [ + { + key: 'hb_xhb_deal', + val: function (bidResponse) { + return bidResponse.dealId; + } + }, + { + key: 'hb_xhb_adid', + val: function (bidResponse) { + return bidResponse.adId; + } + } + ] + }; + bidmanager.registerDefaultBidderSetting('xhb', _defaultBidderSettings); + function buildJPTCall(bid, callbackId) { //determine tag params const placementId = utils.getBidIdParameter('placementId', bid.params); @@ -50,7 +69,7 @@ const XhbAdapter = function XhbAdapter() { } //append custom attributes: - let paramsCopy = utils.extend({}, bid.params); + let paramsCopy = Object.assign({}, bid.params); //delete attributes already used delete paramsCopy.placementId; diff --git a/src/ajax.js b/src/ajax.js index 986c07a00e2..5d0f34c578d 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -9,7 +9,7 @@ const XHR_DONE = 4; * Note: x-domain requests in IE9 do not support the use of cookies * * @param url string url - * @param callback object callback + * @param callback {object | function} callback * @param data mixed data * @param options object */ @@ -20,6 +20,19 @@ export function ajax(url, callback, data, options = {}) { let useXDomainRequest = false; let method = options.method || (data ? 'POST' : 'GET'); + let callbacks = typeof callback === "object" ? callback : { + success: function() { + utils.logMessage('xhr success'); + }, + error: function(e) { + utils.logError('xhr error', null, e); + } + }; + + if(typeof callback === "function") { + callbacks.success = callback; + } + if (!window.XMLHttpRequest) { useXDomainRequest = true; } else{ @@ -32,23 +45,28 @@ export function ajax(url, callback, data, options = {}) { if (useXDomainRequest) { x = new window.XDomainRequest(); x.onload = function () { - callback(x.responseText, x); + callbacks.success(x.responseText, x); }; // http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9 x.onerror = function () { - utils.logMessage('xhr onerror'); + callbacks.error("error", x); }; x.ontimeout = function () { - utils.logMessage('xhr timeout'); + callbacks.error("timeout", x); }; x.onprogress = function() { utils.logMessage('xhr onprogress'); }; } else { x.onreadystatechange = function () { - if (x.readyState === XHR_DONE && callback) { - callback(x.responseText, x); + if (x.readyState === XHR_DONE) { + let status = x.status; + if(status >= 200 && status < 300 || status === 304) { + callbacks.success(x.responseText, x); + } else { + callbacks.error(x.statusText, x); + } } }; } diff --git a/src/bidmanager.js b/src/bidmanager.js index 9f5f68cccb6..5163d23bc31 100644 --- a/src/bidmanager.js +++ b/src/bidmanager.js @@ -138,7 +138,7 @@ exports.addBidResponse = function (adUnitCode, bid) { //if there is any key value pairs to map do here var keyValues = {}; - if (bid.bidderCode && bid.cpm > 0) { + if (bid.bidderCode && (bid.cpm > 0 || bid.dealId ) ) { keyValues = getKeyValueTargetingPairs(bid.bidderCode, bid); } @@ -392,7 +392,7 @@ function adjustBids(bid) { if (code && $$PREBID_GLOBAL$$.bidderSettings && $$PREBID_GLOBAL$$.bidderSettings[code]) { if (typeof $$PREBID_GLOBAL$$.bidderSettings[code].bidCpmAdjustment === objectType_function) { try { - bidPriceAdjusted = $$PREBID_GLOBAL$$.bidderSettings[code].bidCpmAdjustment.call(null, bid.cpm, utils.extend({}, bid)); + bidPriceAdjusted = $$PREBID_GLOBAL$$.bidderSettings[code].bidCpmAdjustment.call(null, bid.cpm, Object.assign({}, bid)); } catch (e) { utils.logError('Error during bid adjustment', 'bidmanager.js', e); diff --git a/src/events.js b/src/events.js index ec196b21c58..2b52de1c38f 100644 --- a/src/events.js +++ b/src/events.js @@ -142,7 +142,7 @@ module.exports = (function () { _public.getEvents = function () { var arrayCopy = []; utils._each(eventsFired, function (value) { - var newProp = utils.extend({}, value); + var newProp = Object.assign({}, value); arrayCopy.push(newProp); }); diff --git a/src/prebid.js b/src/prebid.js index 88ef9b2a7cd..3e4d495eea2 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -1,7 +1,7 @@ /** @module $$PREBID_GLOBAL$$ */ import { getGlobal } from './prebidGlobal'; -import {flatten, uniques, isGptPubadsDefined, adUnitsFilter} from './utils'; +import {flatten, uniques, isGptPubadsDefined, adUnitsFilter, isSrcdocSupported} from './utils'; import { videoAdUnit, hasNonVideoBidder } from './video'; import 'polyfill'; import {parse as parseURL, format as formatURL} from './url'; @@ -72,7 +72,7 @@ $$PREBID_GLOBAL$$.adUnits = $$PREBID_GLOBAL$$.adUnits || []; /** * Command queue that functions will execute once prebid.js is loaded - * @param {function} cmd Annoymous function to execute + * @param {function} cmd Anonymous function to execute * @alias module:$$PREBID_GLOBAL$$.que.push */ $$PREBID_GLOBAL$$.que.push = function (cmd) { @@ -245,11 +245,11 @@ $$PREBID_GLOBAL$$.setTargetingForGPTAsync = function () { //first reset any old targeting targeting.resetPresetTargeting(); - + //now set new targeting keys targeting.setTargeting(targeting.getAllTargeting()); - - //emit event + + //emit event events.emit(SET_TARGETING); }; @@ -261,8 +261,8 @@ $$PREBID_GLOBAL$$.setTargetingForAst = function() { } targeting.setTargetingForAst(); - - //emit event + + //emit event events.emit(SET_TARGETING); }; @@ -277,8 +277,9 @@ $$PREBID_GLOBAL$$.allBidsAvailable = function () { }; /** - * This function will render the ad (based on params) in the given iframe document passed through. Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchrounsly - * @param {object} doc document + * This function will render the ad (based on params) in the given iframe document passed through. + * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously + * @param {HTMLDocument} doc document * @param {string} id bid id to locate the ad * @alias module:$$PREBID_GLOBAL$$.renderAd */ @@ -300,14 +301,18 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { var url = adObject.adUrl; var ad = adObject.ad; - if (doc===document || adObject.mediaType === 'video') { - utils.logError('Error trying to write ad. Ad render call ad id ' + id + ' was prevented from writing to the main document.'); + if (doc === document || adObject.mediaType === 'video') { + utils.logError(`Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`); } else if (ad) { - doc.write(ad); - doc.close(); + if (isSrcdocSupported(doc)) { + doc.defaultView.frameElement.srcdoc = ad; + } else { + doc.write(ad); + doc.close(); + } setRenderSize(doc, width, height); } else if (url) { - doc.write(''); + doc.write(``); doc.close(); setRenderSize(doc, width, height); } else { @@ -358,7 +363,7 @@ $$PREBID_GLOBAL$$.clearAuction = function() { * @param adUnits * @param adUnitCodes */ -$$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, adUnitCodes }) { +$$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, adUnitCodes } = {}) { events.emit('requestBids'); const cbTimeout = $$PREBID_GLOBAL$$.cbTimeout = timeout || $$PREBID_GLOBAL$$.bidderTimeout; adUnits = adUnits || $$PREBID_GLOBAL$$.adUnits; @@ -443,9 +448,13 @@ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, a $$PREBID_GLOBAL$$.addAdUnits = function (adUnitArr) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.addAdUnits', arguments); if (utils.isArray(adUnitArr)) { - //append array to existing + // generate transactionid for each new adUnits + // Append array to existing + adUnitArr.forEach(adUnit => adUnit.transactionId = utils.generateUUID()); $$PREBID_GLOBAL$$.adUnits.push.apply($$PREBID_GLOBAL$$.adUnits, adUnitArr); } else if (typeof adUnitArr === objectType_object) { + // Generate the transaction id for the adunit + adUnitArr.transactionId = utils.generateUUID(); $$PREBID_GLOBAL$$.adUnits.push(adUnitArr); } }; @@ -497,7 +506,7 @@ $$PREBID_GLOBAL$$.offEvent = function (event, handler, id) { /** * Add a callback event * @param {String} eventStr event to attach callback to Options: "allRequestedBidsBack" | "adUnitBidsBack" - * @param {Function} func function to execute. Paramaters passed into the function: (bidResObj), [adUnitCode]); + * @param {Function} func function to execute. Parameters passed into the function: (bidResObj), [adUnitCode]); * @alias module:$$PREBID_GLOBAL$$.addCallback * @returns {String} id for callback */ @@ -600,7 +609,7 @@ $$PREBID_GLOBAL$$.loadScript = function (tagSrc, callback, useCache) { }; /** - * Will enable sendinga prebid.js to data provider specified + * Will enable sending a prebid.js to data provider specified * @param {object} config object {provider : 'string', options : {}} */ $$PREBID_GLOBAL$$.enableAnalytics = function (config) { @@ -693,7 +702,7 @@ $$PREBID_GLOBAL$$.buildMasterVideoTagFromAdserverTag = function (adserverTag, op /** * Set the order bidders are called in. If not set, the bidders are called in - * the order they are defined wihin the adUnit.bids array + * the order they are defined within the adUnit.bids array * @param {string} order - Order to call bidders in. Currently the only possible value * is 'random', which randomly shuffles the order */ diff --git a/src/targeting.js b/src/targeting.js index 2abb8a0972d..63ac2937a77 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -89,8 +89,9 @@ targeting.setTargetingForAst = function() { utils.logMessage(`Attempting to set targeting for targetId: ${targetId} key: ${key} value: ${targeting[targetId][key]}`); //setKeywords supports string and array as value if(utils.isStr(targeting[targetId][key]) || utils.isArray(targeting[targetId][key])) { - var keywordsObj = {}; - var nKey = (key === 'hb_adid') ? key.toUpperCase() : key; + let keywordsObj = {}; + let input = 'hb_adid'; + let nKey = (key.substring(0, input.length) === input) ? key.toUpperCase() : key; keywordsObj[nKey] = targeting[targetId][key]; window.apntag.setKeywords(targetId,keywordsObj); } diff --git a/src/url.js b/src/url.js index 3588d44fad5..9bd555d6f4e 100644 --- a/src/url.js +++ b/src/url.js @@ -31,7 +31,7 @@ export function parse(url) { protocol: (parsed.protocol || '').replace(/:$/, ''), hostname: parsed.hostname, port: +parsed.port, - pathname: parsed.pathname, + pathname: parsed.pathname.replace(/^(?!\/)/,'/'), search: parseQS(parsed.search || ''), hash: (parsed.hash || '').replace(/^#/, ''), host: parsed.host diff --git a/src/utils.js b/src/utils.js index 5cceb65d391..d56bd05a696 100644 --- a/src/utils.js +++ b/src/utils.js @@ -109,22 +109,6 @@ exports.transformAdServerTargetingObj = function (targeting) { } }; -//Copy all of the properties in the source objects over to the target object -//return the target object. -exports.extend = function (target, source) { - target = target || {}; - - this._each(source, function (value, prop) { - if (typeof source[prop] === objectType_object) { - target[prop] = this.extend(target[prop], source[prop]); - } else { - target[prop] = source[prop]; - } - }); - - return target; -}; - /** * Parse a GPT-Style general size Array like `[[300, 250]]` or `"300x250,970x90"` into an array of sizes `["300x250"]` or '['300x250', '970x90']' * @param {array[array|number]} sizeObj Input array or double array [300,250] or [[300,250], [728,90]] @@ -592,3 +576,16 @@ export function shuffle(array) { export function adUnitsFilter(filter, bid) { return filter.includes(bid && bid.placementCode || bid && bid.adUnitCode); } + +/** + * Check if parent iframe of passed document supports content rendering via 'srcdoc' property + * @param {HTMLDocument} doc document to check support of 'srcdoc' + */ +export function isSrcdocSupported(doc) { + //Firefox is excluded due to https://bugzilla.mozilla.org/show_bug.cgi?id=1265961 + return !!doc.defaultView && 'srcdoc' in doc.defaultView.frameElement && !/firefox/i.test(navigator.userAgent); +} + +export function cloneJson(obj) { + return JSON.parse(JSON.stringify(obj)); +} \ No newline at end of file diff --git a/test/fixtures/allAdapters.js b/test/fixtures/allAdapters.js index 7cdead341f0..e2baa12cd10 100644 --- a/test/fixtures/allAdapters.js +++ b/test/fixtures/allAdapters.js @@ -1,3 +1,3 @@ exports.getAllAdaptersString = function () { - return `var AardvarkAdapter = require(\'./adapters/aardvark.js\');\n exports.registerBidAdapter(new AardvarkAdapter(), \'aardvark\');\nvar AdbladeAdapter = require(\'./adapters/adblade.js\');\n exports.registerBidAdapter(new AdbladeAdapter(), \'adblade\');\nvar AdbutlerAdapter = require(\'./adapters/adbutler.js\');\n exports.registerBidAdapter(new AdbutlerAdapter(), \'adbutler\');\nvar AdequantAdapter = require(\'./adapters/adequant.js\');\n exports.registerBidAdapter(new AdequantAdapter(), \'adequant\');\nvar AdformAdapter = require(\'./adapters/adform.js\');\n exports.registerBidAdapter(new AdformAdapter(), \'adform\');\nvar AdmediaAdapter = require(\'./adapters/admedia.js\');\n exports.registerBidAdapter(new AdmediaAdapter(), \'admedia\');\nvar AolAdapter = require(\'./adapters/aol.js\');\n exports.registerBidAdapter(new AolAdapter(), \'aol\');\nvar AppnexusAdapter = require(\'./adapters/appnexus.js\');\n exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\nvar AppnexusAstAdapter = require(\'./adapters/appnexusAst.js\');\n exports.registerBidAdapter(new AppnexusAstAdapter(), \'appnexusAst\');\nvar GetintentAdapter = require(\'./adapters/getintent.js\');\n exports.registerBidAdapter(new GetintentAdapter(), \'getintent\');\nvar HiromediaAdapter = require(\'./adapters/hiromedia.js\');\n exports.registerBidAdapter(new HiromediaAdapter(), \'hiromedia\');\nvar IndexExchangeAdapter = require(\'./adapters/indexExchange.js\');\n exports.registerBidAdapter(new IndexExchangeAdapter(), \'indexExchange\');\nvar KruxlinkAdapter = require(\'./adapters/kruxlink.js\');\n exports.registerBidAdapter(new KruxlinkAdapter(), \'kruxlink\');\nvar KomoonaAdapter = require(\'./adapters/komoona.js\');\n exports.registerBidAdapter(new KomoonaAdapter(), \'komoona\');\nvar OpenxAdapter = require(\'./adapters/openx.js\');\n exports.registerBidAdapter(new OpenxAdapter(), \'openx\');\nvar PiximediaAdapter = require(\'./adapters/piximedia.js\');\n exports.registerBidAdapter(new PiximediaAdapter(), \'piximedia\');\nvar PubmaticAdapter = require(\'./adapters/pubmatic.js\');\n exports.registerBidAdapter(new PubmaticAdapter(), \'pubmatic\');\nvar PulsepointAdapter = require(\'./adapters/pulsepoint.js\');\n exports.registerBidAdapter(new PulsepointAdapter(), \'pulsepoint\');\nvar RubiconAdapter = require(\'./adapters/rubicon.js\');\n exports.registerBidAdapter(new RubiconAdapter(), \'rubicon\');\nvar SonobiAdapter = require(\'./adapters/sonobi.js\');\n exports.registerBidAdapter(new SonobiAdapter(), \'sonobi\');\nvar SovrnAdapter = require(\'./adapters/sovrn.js\');\n exports.registerBidAdapter(new SovrnAdapter(), \'sovrn\');\nvar SpringserveAdapter = require(\'./adapters/springserve.js\');\n exports.registerBidAdapter(new SpringserveAdapter(), \'springserve\');\nvar TripleliftAdapter = require(\'./adapters/triplelift.js\');\n exports.registerBidAdapter(new TripleliftAdapter(), \'triplelift\');\nvar TwengaAdapter = require(\'./adapters/twenga.js\');\n exports.registerBidAdapter(new TwengaAdapter(), \'twenga\');\nvar YieldbotAdapter = require(\'./adapters/yieldbot.js\');\n exports.registerBidAdapter(new YieldbotAdapter(), \'yieldbot\');\nvar NginadAdapter = require(\'./adapters/nginad.js\');\n exports.registerBidAdapter(new NginadAdapter(), \'nginad\');\nvar BrightcomAdapter = require(\'./adapters/brightcom.js\');\n exports.registerBidAdapter(new BrightcomAdapter(), \'brightcom\');\nvar WideorbitAdapter = require(\'./adapters/wideorbit.js\');\n exports.registerBidAdapter(new WideorbitAdapter(), \'wideorbit\');\nvar JcmAdapter = require(\'./adapters/jcm.js\');\n exports.registerBidAdapter(new JcmAdapter(), \'jcm\');\nvar UnderdogmediaAdapter = require(\'./adapters/underdogmedia.js\');\n exports.registerBidAdapter(new UnderdogmediaAdapter(), \'underdogmedia\');\nvar MemeglobalAdapter = require(\'./adapters/memeglobal.js\');\n exports.registerBidAdapter(new MemeglobalAdapter(), \'memeglobal\');\nvar CentroAdapter = require(\'./adapters/centro.js\');\n exports.registerBidAdapter(new CentroAdapter(), \'centro\');\nvar RoxotAdapter = require(\'./adapters/roxot.js\');\n exports.registerBidAdapter(new RoxotAdapter(), \'roxot\');\nexports.aliasBidAdapter(\'appnexus\',\'brealtime\');\nexports.aliasBidAdapter(\'appnexus\',\'pagescience\');\nexports.aliasBidAdapter(\'appnexus\',\'defymedia\');\nexports.videoAdapters = ["appnexusAst"];`; + return `var AardvarkAdapter = require(\'./adapters/aardvark.js\');\n exports.registerBidAdapter(new AardvarkAdapter(), \'aardvark\');\nvar AdbladeAdapter = require(\'./adapters/adblade.js\');\n exports.registerBidAdapter(new AdbladeAdapter(), \'adblade\');\nvar AdbutlerAdapter = require(\'./adapters/adbutler.js\');\n exports.registerBidAdapter(new AdbutlerAdapter(), \'adbutler\');\nvar AdequantAdapter = require(\'./adapters/adequant.js\');\n exports.registerBidAdapter(new AdequantAdapter(), \'adequant\');\nvar AdformAdapter = require(\'./adapters/adform.js\');\n exports.registerBidAdapter(new AdformAdapter(), \'adform\');\nvar AdmediaAdapter = require(\'./adapters/admedia.js\');\n exports.registerBidAdapter(new AdmediaAdapter(), \'admedia\');\nvar AolAdapter = require(\'./adapters/aol.js\');\n exports.registerBidAdapter(new AolAdapter(), \'aol\');\nvar AppnexusAdapter = require(\'./adapters/appnexus.js\');\n exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\nvar AppnexusAstAdapter = require(\'./adapters/appnexusAst.js\');\n exports.registerBidAdapter(new AppnexusAstAdapter(), \'appnexusAst\');\nvar GetintentAdapter = require(\'./adapters/getintent.js\');\n exports.registerBidAdapter(new GetintentAdapter(), \'getintent\');\nvar HiromediaAdapter = require(\'./adapters/hiromedia.js\');\n exports.registerBidAdapter(new HiromediaAdapter(), \'hiromedia\');\nvar IndexExchangeAdapter = require(\'./adapters/indexExchange.js\');\n exports.registerBidAdapter(new IndexExchangeAdapter(), \'indexExchange\');\nvar KruxlinkAdapter = require(\'./adapters/kruxlink.js\');\n exports.registerBidAdapter(new KruxlinkAdapter(), \'kruxlink\');\nvar KomoonaAdapter = require(\'./adapters/komoona.js\');\n exports.registerBidAdapter(new KomoonaAdapter(), \'komoona\');\nvar OpenxAdapter = require(\'./adapters/openx.js\');\n exports.registerBidAdapter(new OpenxAdapter(), \'openx\');\nvar PiximediaAdapter = require(\'./adapters/piximedia.js\');\n exports.registerBidAdapter(new PiximediaAdapter(), \'piximedia\');\nvar PubmaticAdapter = require(\'./adapters/pubmatic.js\');\n exports.registerBidAdapter(new PubmaticAdapter(), \'pubmatic\');\nvar PulsepointAdapter = require(\'./adapters/pulsepoint.js\');\n exports.registerBidAdapter(new PulsepointAdapter(), \'pulsepoint\');\nvar RubiconAdapter = require(\'./adapters/rubicon.js\');\n exports.registerBidAdapter(new RubiconAdapter(), \'rubicon\');\nvar SonobiAdapter = require(\'./adapters/sonobi.js\');\n exports.registerBidAdapter(new SonobiAdapter(), \'sonobi\');\nvar SovrnAdapter = require(\'./adapters/sovrn.js\');\n exports.registerBidAdapter(new SovrnAdapter(), \'sovrn\');\nvar SpringserveAdapter = require(\'./adapters/springserve.js\');\n exports.registerBidAdapter(new SpringserveAdapter(), \'springserve\');\nvar ThoughtleadrAdapter = require('./adapters/thoughtleadr.js');\n exports.registerBidAdapter(new ThoughtleadrAdapter(), 'thoughtleadr');\nvar TripleliftAdapter = require(\'./adapters/triplelift.js\');\n exports.registerBidAdapter(new TripleliftAdapter(), \'triplelift\');\nvar TwengaAdapter = require(\'./adapters/twenga.js\');\n exports.registerBidAdapter(new TwengaAdapter(), \'twenga\');\nvar YieldbotAdapter = require(\'./adapters/yieldbot.js\');\n exports.registerBidAdapter(new YieldbotAdapter(), \'yieldbot\');\nvar NginadAdapter = require(\'./adapters/nginad.js\');\n exports.registerBidAdapter(new NginadAdapter(), \'nginad\');\nvar BrightcomAdapter = require(\'./adapters/brightcom.js\');\n exports.registerBidAdapter(new BrightcomAdapter(), \'brightcom\');\nvar WideorbitAdapter = require(\'./adapters/wideorbit.js\');\n exports.registerBidAdapter(new WideorbitAdapter(), \'wideorbit\');\nvar JcmAdapter = require(\'./adapters/jcm.js\');\n exports.registerBidAdapter(new JcmAdapter(), \'jcm\');\nvar UnderdogmediaAdapter = require(\'./adapters/underdogmedia.js\');\n exports.registerBidAdapter(new UnderdogmediaAdapter(), \'underdogmedia\');\nvar MemeglobalAdapter = require(\'./adapters/memeglobal.js\');\n exports.registerBidAdapter(new MemeglobalAdapter(), \'memeglobal\');\nvar CentroAdapter = require(\'./adapters/centro.js\');\n exports.registerBidAdapter(new CentroAdapter(), \'centro\');\nvar RoxotAdapter = require(\'./adapters/roxot.js\');\n exports.registerBidAdapter(new RoxotAdapter(), \'roxot\');\nexports.aliasBidAdapter(\'appnexus\',\'brealtime\');\nexports.aliasBidAdapter(\'appnexus\',\'pagescience\');\nexports.aliasBidAdapter(\'appnexus\',\'defymedia\');\nexports.videoAdapters = ["appnexusAst"];`; }; diff --git a/test/fixtures/fixtures.js b/test/fixtures/fixtures.js index 75a90bb2b42..202d95b402b 100644 --- a/test/fixtures/fixtures.js +++ b/test/fixtures/fixtures.js @@ -28,7 +28,8 @@ export function getBidRequests() { "bidderRequestId": "2946b569352ef2", "requestId": "1863e370099523", "startTime": 1462918897462, - "status": 1 + "status": 1, + "transactionId": "fsafsa" }, { "bidder": "appnexus", @@ -50,7 +51,8 @@ export function getBidRequests() { "bidderRequestId": "2946b569352ef2", "requestId": "1863e370099523", "startTime": 1462918897463, - "status": 1 + "status": 1, + "transactionId": "fsafsa" } ], "start": 1462918897460 @@ -79,7 +81,8 @@ export function getBidRequests() { ], "bidId": "6d11aa2d5b3659", "bidderRequestId": "5e1525bae3eb11", - "requestId": "1863e370099523" + "requestId": "1863e370099523", + "transactionId": "fsafsa" } ], "start": 1462918897463 @@ -112,7 +115,7 @@ export function getBidRequests() { "sizes": [ 15, 10 - ] + ], }, "placementCode": "/19968336/header-bid-tag-0", "sizes": [ @@ -127,7 +130,8 @@ export function getBidRequests() { ], "bidId": "96aff279720d39", "bidderRequestId": "8778750ee15a77", - "requestId": "1863e370099523" + "requestId": "1863e370099523", + "transactionId": "fsafsa" } ], "start": 1462918897474 @@ -156,7 +160,8 @@ export function getBidRequests() { "bidId": "1144e2f0de84363", "bidderRequestId": "107f5e6e98dcf09", "requestId": "1863e370099523", - "startTime": 1462918897477 + "startTime": 1462918897477, + "transactionId": "fsafsa" } ], "start": 1462918897475 @@ -185,7 +190,8 @@ export function getBidRequests() { "bidId": "135e89c039705da", "bidderRequestId": "12eeded736650b4", "requestId": "1863e370099523", - "status": 1 + "status": 1, + "transactionId": "fsafsa" } ], "start": 1462918897477 @@ -215,7 +221,8 @@ export function getBidRequests() { "bidderRequestId": "167c4d79b615948", "requestId": "1863e370099523", "startTime": 1462918897480, - "status": 1 + "status": 1, + "transactionId": "fsafsa" } ], "start": 1462918897479 @@ -245,7 +252,8 @@ export function getBidRequests() { "bidderRequestId": "18bed198c172a69", "requestId": "1863e370099523", "startTime": 1462918897481, - "status": 1 + "status": 1, + "transactionId": "fsafsa" } ], "start": 1462918897480 @@ -273,7 +281,8 @@ export function getBidRequests() { ], "bidId": "21ae8131ec04f6e", "bidderRequestId": "20d0d30333715a7", - "requestId": "1863e370099523" + "requestId": "1863e370099523", + "transactionId": "fsafsa" } ], "start": 1462918897482 diff --git a/test/spec/adUnits_spec.js b/test/spec/adUnits_spec.js index f3e5fd67d16..280ccc1eb16 100644 --- a/test/spec/adUnits_spec.js +++ b/test/spec/adUnits_spec.js @@ -83,6 +83,13 @@ describe('Publisher API _ AdUnits', function () { assert.strictEqual(bids2[1].params.placementId, '827326', 'adUnit2 bids2 params.placementId'); }); + it ('both add unit should contains a transactionid.'), function() { + assert.exist(adUnit1.transationId) + assert.exist(adUnit2.transationId) + + assert.strictEqual(false, adUnit1.transationId === adUnit2.transationId) + } + it('the second adUnits value should be same with the adUnits that is added by $$PREBID_GLOBAL$$.addAdUnits();', function () { assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); diff --git a/test/spec/adapters/adbund_spec.js b/test/spec/adapters/adbund_spec.js new file mode 100644 index 00000000000..0714d041c83 --- /dev/null +++ b/test/spec/adapters/adbund_spec.js @@ -0,0 +1,96 @@ +import { expect } from 'chai'; +import Adapter from '../../../src/adapters/adbund'; +import bidManager from 'src/bidmanager'; +import CONSTANTS from 'src/constants.json'; + +describe('adbund adapter tests', function () { + + let sandbox; + let adapter; + let server; + + const request = { + bidderCode: 'adbund', + bids: [{ + bidder: 'adbund', + params: { + sid: '110238', + bidfloor: 0.036 + }, + placementCode: 'adbund', + sizes: [[300, 250]], + bidId: 'adbund_bidId', + bidderRequestId: 'adbund_bidderRequestId', + requestId: 'adbund_requestId' + }] + }; + + const response = { + bidderCode: 'adbund', + cpm: 1.06, + height: 250, + width: 300 + }; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('adbund callBids validation', () => { + + beforeEach(() => { + adapter = new Adapter(); + }); + + afterEach(() => { + }); + + it('Valid bid-request', () => { + let bidderRequest; + + sandbox.stub(adapter, 'callBids'); + adapter.callBids(request); + + bidderRequest = adapter.callBids.getCall(0).args[0]; + + expect(bidderRequest).to.have.property('bids') + .that.is.an('array') + .with.lengthOf(1); + + expect(bidderRequest).to.have.deep.property('bids[0]') + .to.have.property('bidder', 'adbund'); + + expect(bidderRequest).to.have.deep.property('bids[0]') + .with.property('sizes') + .that.is.an('array') + .with.lengthOf(1) + .that.deep.equals(request.bids[0].sizes); + + expect(bidderRequest).to.have.deep.property('bids[0]') + .with.property('params') + .to.have.property('bidfloor', 0.036); + }); + + it('Valid bid-response', () => { + var bidderResponse; + + sandbox.stub(bidManager, 'addBidResponse'); + adapter.callBids(request); + bidderResponse = bidManager.addBidResponse.getCall(0) || + bidManager.addBidResponse.getCall(1); + + if (bidderResponse && bidderResponse.args && bidderResponse.args[1]) { + bidderResponse = bidderResponse.args[1]; + expect(bidderResponse.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); + expect(bidderResponse.bidderCode).to.equal(response.bidderCode); + expect(bidderResponse.width).to.equal(response.width); + expect(bidderResponse.height).to.equal(response.height); + expect(bidderResponse.cpm).to.equal(response.cpm); + } + }); + }); +}); \ No newline at end of file diff --git a/test/spec/adapters/adkernel_spec.js b/test/spec/adapters/adkernel_spec.js index 4d692d32056..b2fb7fd2141 100644 --- a/test/spec/adapters/adkernel_spec.js +++ b/test/spec/adapters/adkernel_spec.js @@ -238,7 +238,8 @@ describe('Adkernel adapter', () => { expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); expect(utils.createTrackPixelHtml.calledOnce); let result = pbjs.getBidResponsesForAdUnitCode(bid1_zone1.placementCode); - expect(result.bids[0].ad).to.include(bidResponse1.seatbid[0].bid[0].nurl); + let expectedNurl = bidResponse1.seatbid[0].bid[0].nurl + '&px=1'; + expect(result.bids[0].ad).to.include(expectedNurl); }); it('should perform usersync for each unique host/zone combination', () => { diff --git a/test/spec/adapters/aol_spec.js b/test/spec/adapters/aol_spec.js index 47b434de7f2..87f0f2e428d 100644 --- a/test/spec/adapters/aol_spec.js +++ b/test/spec/adapters/aol_spec.js @@ -1,43 +1,46 @@ import {expect} from 'chai'; -import { cloneDeep } from 'lodash'; import * as utils from 'src/utils'; import AolAdapter from 'src/adapters/aol'; import bidmanager from 'src/bidmanager'; -import events from 'src/events'; -import constants from 'src/constants'; - -const DEFAULT_BIDDER_REQUEST = { - bidderCode: 'aol', - requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - bidderRequestId: '7101db09af0db2', - start: new Date().getTime(), - bids: [{ - bidder: 'aol', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - placementCode: 'foo', - params: { - placement: 1234567, - network: '9599.1' - } - }] + + +let getDefaultBidResponse = () => { + return { + "id": "245730051428950632", + "cur": "USD", + "seatbid": [{ + "bid": [{ + "id": 1, + "impid": "245730051428950632", + "price": 0.09, + "adm": "", + "crid": "0", + "h": 90, + "w": 728, + "ext": {"sizeid": 225} + }] + }] + }; }; -const DEFAULT_PUBAPI_RESPONSE = { - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "price": 0.09, - "adm": "", - "crid": "0", - "h": 90, - "w": 728, - "ext": {"sizeid": 225} + +let getDefaultBidRequest = () => { + return { + bidderCode: 'aol', + requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + bidderRequestId: '7101db09af0db2', + start: new Date().getTime(), + bids: [{ + bidder: 'aol', + bidId: '84ab500420319d', + bidderRequestId: '7101db09af0db2', + requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + placementCode: 'foo', + params: { + placement: 1234567, + network: '9599.1' + } }] - }] + }; }; describe('AolAdapter', () => { @@ -47,7 +50,7 @@ describe('AolAdapter', () => { beforeEach(() => adapter = new AolAdapter()); function createBidderRequest({bids, params} = {}) { - var bidderRequest = cloneDeep(DEFAULT_BIDDER_REQUEST); + var bidderRequest = utils.cloneJson(getDefaultBidRequest()); if (bids && Array.isArray(bids)) { bidderRequest.bids = bids; } @@ -83,7 +86,7 @@ describe('AolAdapter', () => { }); it('should hit the Marketplace api endpoint with the Marketplace config', () => { - adapter.callBids(DEFAULT_BIDDER_REQUEST); + adapter.callBids(getDefaultBidRequest()); expect(requests[0].url).to.contain('adserver-us.adtech.advertising.com/pubapi/3.0/'); }); @@ -121,17 +124,17 @@ describe('AolAdapter', () => { }); it('should be the pubapi bid request', () => { - adapter.callBids(DEFAULT_BIDDER_REQUEST); + adapter.callBids(getDefaultBidRequest()); expect(requests[0].url).to.contain('cmd=bid;'); }); it('should be the version 2 of pubapi', () => { - adapter.callBids(DEFAULT_BIDDER_REQUEST); + adapter.callBids(getDefaultBidRequest()); expect(requests[0].url).to.contain('v=2;'); }); it('should contain cache busting', () => { - adapter.callBids(DEFAULT_BIDDER_REQUEST); + adapter.callBids(getDefaultBidRequest()); expect(requests[0].url).to.match(/misc=\d+/); }); @@ -218,7 +221,6 @@ describe('AolAdapter', () => { })); expect(requests[0].url).to.contain('bidfloor=0.8'); }); - }); describe('Nexage api', () => { @@ -355,14 +357,14 @@ describe('AolAdapter', () => { }); it('should be added to bidmanager if returned from pubapi', () => { - server.respondWith(JSON.stringify(DEFAULT_PUBAPI_RESPONSE)); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + server.respondWith(JSON.stringify(getDefaultBidResponse())); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; }); it('should be added to bidmanager if returned from nexage GET bid request', () => { - server.respondWith(JSON.stringify(DEFAULT_PUBAPI_RESPONSE)); + server.respondWith(JSON.stringify(getDefaultBidResponse())); adapter.callBids(createBidderRequest({ params: { dcn: '54321123', @@ -374,7 +376,7 @@ describe('AolAdapter', () => { }); it('should be added to bidmanager if returned from nexage POST bid request', () => { - server.respondWith(JSON.stringify(DEFAULT_PUBAPI_RESPONSE)); + server.respondWith(JSON.stringify(getDefaultBidResponse())); adapter.callBids(createBidderRequest({ params: { id: 'id-1', @@ -394,25 +396,25 @@ describe('AolAdapter', () => { }); it('should be added to bidmanager with correct bidderCode', () => { - server.respondWith(JSON.stringify(DEFAULT_PUBAPI_RESPONSE)); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + server.respondWith(JSON.stringify(getDefaultBidResponse())); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(bidmanager.addBidResponse.firstCall.args[1]).to.have.property('bidderCode', 'aol'); }); it('should have adId matching the bidId from related bid request', () => { - server.respondWith(JSON.stringify(DEFAULT_PUBAPI_RESPONSE)); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + server.respondWith(JSON.stringify(getDefaultBidResponse())); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(bidmanager.addBidResponse.firstCall.args[1]) - .to.have.property('adId', DEFAULT_BIDDER_REQUEST.bids[0].bidId); + .to.have.property('adId', '84ab500420319d'); }); it('should be added to bidmanager as invalid in case of empty response', () => { server.respondWith(''); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); @@ -420,238 +422,129 @@ describe('AolAdapter', () => { it('should be added to bidmanager as invalid in case of invalid JSON response', () => { server.respondWith('{foo:{bar:{baz:'); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); }); it('should be added to bidmanager as invalid in case of no bid data', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [] - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.seatbid = []; + server.respondWith(JSON.stringify(bidResponse)); + + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); }); it('should have adId matching the bidId from bid request in case of no bid data', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [] - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.seatbid = []; + server.respondWith(JSON.stringify(bidResponse)); + + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(bidmanager.addBidResponse.firstCall.args[1]) - .to.have.property('adId', DEFAULT_BIDDER_REQUEST.bids[0].bidId); + .to.have.property('adId', '84ab500420319d'); }); it('should be added to bidmanager as invalid in case of empty price', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "adm": "", - "crid": "0", - "h": 90, - "w": 728, - "ext": {"sizeid": 225} - }] - }] - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.seatbid[0].bid[0].price = undefined; + + server.respondWith(JSON.stringify(bidResponse)); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); }); it('should be added to bidmanager with attributes from pubapi response', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "price": 0.09, - "adm": "", - "crid": "12345", - "h": 90, - "w": 728, - "ext": {"sizeid": 225} - }] - }] - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.seatbid[0].bid[0].crid = '12345'; + + server.respondWith(JSON.stringify(bidResponse)); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; - var bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.ad).to.equal(""); - expect(bidResponse.cpm).to.equal(0.09); - expect(bidResponse.width).to.equal(728); - expect(bidResponse.height).to.equal(90); - expect(bidResponse.creativeId).to.equal('12345'); - expect(bidResponse.pubapiId).to.equal('245730051428950632'); + var addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; + expect(addedBidResponse.ad).to.equal(""); + expect(addedBidResponse.cpm).to.equal(0.09); + expect(addedBidResponse.width).to.equal(728); + expect(addedBidResponse.height).to.equal(90); + expect(addedBidResponse.creativeId).to.equal('12345'); + expect(addedBidResponse.pubapiId).to.equal('245730051428950632'); }); it('should be added to bidmanager including pixels from pubapi response', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "price": 0.09, - "adm": "", - "crid": "12345", - "h": 90, - "w": 728, - "ext": {"sizeid": 225} - }] - }], - "ext": { - "pixels": "" - } - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.ext = { + pixels: "" + }; + + server.respondWith(JSON.stringify(bidResponse)); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; - var bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.ad).to.equal( + var addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; + expect(addedBidResponse.ad).to.equal( "" + - "" + "" ); }); it('should be added to bidmanager including dealid from pubapi response', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "dealid": "12345", - "price": 0.09, - "adm": "", - "crid": "12345", - "h": 90, - "w": 728, - "ext": { - "sizeid": 225 - } - }] - }] - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.seatbid[0].bid[0].dealid = '12345'; + + server.respondWith(JSON.stringify(bidResponse)); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; - var bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.dealId).to.equal('12345'); + var addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; + expect(addedBidResponse.dealId).to.equal('12345'); }); it('should be added to bidmanager including encrypted price from pubapi response', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "dealid": "12345", - "price": 0.09, - "adm": "", - "crid": "12345", - "h": 90, - "w": 728, - "ext": { - "sizeid": 225, - "encp": "a9334987" - } - }] - }] - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.seatbid[0].bid[0].ext.encp = 'a9334987'; + server.respondWith(JSON.stringify(bidResponse)); + + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; - var bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.cpm).to.equal('a9334987'); + let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; + expect(addedBidResponse.cpm).to.equal('a9334987'); }); it('should not render pixels on pubapi response when no parameter is set', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "price": 0.09, - "adm": "", - "crid": "12345", - "h": 90, - "w": 728, - "ext": {"sizeid": 225} - }] - }], - "ext": { - "pixels": "" - } - })); - adapter.callBids(DEFAULT_BIDDER_REQUEST); + let bidResponse = getDefaultBidResponse(); + bidResponse.ext = { + pixels: "" + }; + server.respondWith(JSON.stringify(bidResponse)); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; expect(document.body.querySelectorAll('iframe[src="pixels.org"]').length).to.equal(0); }); it('should render pixels from pubapi response when param userSyncOn is set with \'bidResponse\'', () => { - server.respondWith(JSON.stringify({ - "id": "245730051428950632", - "cur": "USD", - "seatbid": [{ - "bid": [{ - "id": 1, - "impid": "245730051428950632", - "price": 0.09, - "adm": "", - "crid": "12345", - "h": 90, - "w": 728, - "ext": {"sizeid": 225} - }] - }], - "ext": { - "pixels": "" - } - })); - adapter.callBids({ - bidderCode: 'aol', - requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - bidderRequestId: '7101db09af0db2', - start: new Date().getTime(), - bids: [{ - bidder: 'aol', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - placementCode: 'foo', - params: { - placement: 1234567, - network: '9599.1', - userSyncOn: 'bidResponse' - } - }] - }); + let bidResponse = getDefaultBidResponse(); + bidResponse.ext = { + pixels: "" + }; + + server.respondWith(JSON.stringify(bidResponse)); + let bidRequest = getDefaultBidRequest(); + bidRequest.bids[0].params.userSyncOn = 'bidResponse'; + adapter.callBids(bidRequest); server.respond(); expect(bidmanager.addBidResponse.calledOnce).to.be.true; @@ -666,8 +559,34 @@ describe('AolAdapter', () => { assertPixelsItem('iframe[src="pixels.org"]'); assertPixelsItem('iframe[src="pixels1.org"]'); + expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; }); + it('should not render pixels if it was rendered before', () => { + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; + let bidResponse = getDefaultBidResponse(); + bidResponse.ext = { + pixels: "" + }; + server.respondWith(JSON.stringify(bidResponse)); + + let bidRequest = getDefaultBidRequest(); + bidRequest.bids[0].params.userSyncOn = 'bidResponse'; + adapter.callBids(bidRequest); + server.respond(); + + expect(bidmanager.addBidResponse.calledOnce).to.be.true; + + let assertPixelsItem = (pixelsItemSelector) => { + let pixelsItems = document.body.querySelectorAll(pixelsItemSelector); + + expect(pixelsItems.length).to.equal(0); + }; + + assertPixelsItem('iframe[src="test.com"]'); + assertPixelsItem('iframe[src="test2.com"]'); + }); }); describe('when bidCpmAdjustment is set', () => { @@ -689,13 +608,13 @@ describe('AolAdapter', () => { it('should show warning in the console', function() { sinon.spy(utils, 'logWarn'); - server.respondWith(JSON.stringify(DEFAULT_PUBAPI_RESPONSE)); + server.respondWith(JSON.stringify(getDefaultBidResponse())); $$PREBID_GLOBAL$$.bidderSettings = { aol: { bidCpmAdjustment: function() {} } }; - adapter.callBids(DEFAULT_BIDDER_REQUEST); + adapter.callBids(getDefaultBidRequest()); server.respond(); expect(utils.logWarn.calledOnce).to.be.true; }); diff --git a/test/spec/adapters/atomx_spec.js b/test/spec/adapters/atomx_spec.js new file mode 100644 index 00000000000..fb4bdc83f1b --- /dev/null +++ b/test/spec/adapters/atomx_spec.js @@ -0,0 +1,151 @@ +window.pbjs = window.pbjs || {}; +var chai = require('chai'); +var Adapter = require('src/adapters/atomx')(); +var Ajax = require('src/ajax'); +var adLoader = require('src/adloader'); +var bidmanager = require('src/bidmanager.js'); +var CONSTANTS = require('src/constants.json'); + +describe('Atomx adapter', function () { + var validData_1 = { + bids: [ + { + bidder: 'atomx', + bidId: 'bid_id', + params: {id: 1234}, + placementCode: 'ad-unit-1', + sizes: [[300, 250],[800, 600]] + } + ] + }; + var validData_2 = { + bids: [ + { + bidder: 'adtomx', + bidId: 'bid_id', + params: {id: 5678}, + placementCode: 'ad-unit-1', + sizes: [300, 250] + } + ] + }; + + var invalidData = { + bids: [ + { + bidder: 'atomx', + bidId: 'bid_id', + params: {}, + placementCode: 'ad-unit-1', + sizes: [[300, 250]] + } + ] + }; + + var responseWithAd = JSON.stringify({ + 'cpm': 2.2, + 'url': 'http://p.ato.mx/placement?id=1234', + 'width': 300, + 'height': 250, + 'code': 'ad-unit-1' + }); + var responseWithoutAd = JSON.stringify({ + 'cpm': 0, + 'url': 'http://p.ato.mx/placement?id=1234', + 'width': 300, + 'height': 250, + 'code': 'ad-unit-1' + }); + + var responseEmpty = ''; + var validJsonParams = { + id: '1234', + prebid: 'ad-unit-1', + size: '300x250' + }; + + describe('loads the tag code', function() { + var stubLoadScript = sinon.stub(adLoader, "loadScript"); + Adapter.callBids(validData_1); + sinon.assert.calledOnce(stubLoadScript); + let url = stubLoadScript.firstCall.args[0]; + let callback = stubLoadScript.firstCall.args[1]; + expect(url).to.equal('http://s.ato.mx/b.js'); + expect(callback).to.be.a('function'); + }); + describe('bid request with valid data', function () { + var stubAjax; + beforeEach(function () { + window.atomx_prebid = function() { + return '/placement'; + }; + stubAjax = sinon.stub(Ajax, 'ajax'); + }); + afterEach(function () { + stubAjax.restore(); + }); + it('bid request should be called. sizes style -> [[],[]]', function () { + Adapter.callBids(validData_1); + sinon.assert.calledOnce(stubAjax); + }); + it('bid request should be called. sizes style -> []', function () { + Adapter.callBids(validData_2); + sinon.assert.calledOnce(stubAjax); + }); + it('ajax params should be matched', function () { + Adapter.callBids(validData_1); + sinon.assert.calledWith(stubAjax, sinon.match('/placement', function () { + }, validJsonParams, {method: "GET"})); + }); + }); + describe('bid request with invalid data', function () { + var addBidResponse, stubAjax; + beforeEach(function () { + window.atomx_prebid = function() { + return '/placement'; + }; + addBidResponse = sinon.stub(bidmanager, 'addBidResponse'); + stubAjax = sinon.stub(Ajax, 'ajax'); + }); + afterEach(function () { + addBidResponse.restore(); + stubAjax.restore(); + }); + it('ajax shouldn\'t be called', function () { + Adapter.callBids(invalidData); + sinon.assert.notCalled(stubAjax); + }); + it('bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID + '"', function () { + Adapter.callBids(invalidData); + expect(addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); + expect(addBidResponse.firstCall.args[1].bidderCode).to.equal('atomx'); + }); + }); + describe('bid response', function () { + var addBidResponse; + beforeEach(function () { + addBidResponse = sinon.stub(bidmanager, 'addBidResponse'); + }); + afterEach(function () { + addBidResponse.restore(); + }); + it('with ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.GOOD + '"', function () { + Adapter.responseCallback(validData_1.bids[0], responseWithAd); + var arg = addBidResponse.firstCall.args[1]; + expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); + expect(arg.bidderCode).to.equal('atomx'); + }); + it('without ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID, function () { + Adapter.responseCallback(validData_1.bids[0], responseWithoutAd); + var arg = addBidResponse.firstCall.args[1]; + expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); + expect(arg.bidderCode).to.equal('atomx'); + }); + it('empty. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID, function () { + Adapter.responseCallback(validData_1.bids[0], responseEmpty); + var arg = addBidResponse.firstCall.args[1]; + expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); + expect(arg.bidderCode).to.equal('atomx'); + }) + }); +}); diff --git a/test/spec/adapters/audienceNetwork_spec.js b/test/spec/adapters/audienceNetwork_spec.js new file mode 100644 index 00000000000..09553b80305 --- /dev/null +++ b/test/spec/adapters/audienceNetwork_spec.js @@ -0,0 +1,333 @@ +/** + * @file Tests for AudienceNetwork adapter. + */ +import { expect } from 'chai'; + +import bidmanager from 'src/bidmanager'; +import { STATUS } from 'src/constants.json'; +import * as utils from 'src/utils'; + +import AudienceNetwork from 'src/adapters/audienceNetwork'; + +const bidderCode = 'audienceNetwork'; +const placementId = 'test-placement-id'; +const placementCode = '/test/placement/code'; + +/** + * Expect haystack string to contain needle n times. + * @param {String} haystack + * @param {String} needle + * @param {String} [n=1] + * @throws {Error} + */ +const expectToContain = (haystack, needle, n = 1) => + expect(haystack.split(needle)).to.have.lengthOf(n + 1, + `expected ${n} occurrence(s) of '${needle}' in '${haystack}'`); + + +describe('AudienceNetwork adapter', () => { + + describe('Public API', () => { + const adapter = AudienceNetwork(); + it('getBidderCode', () => { + expect(adapter.getBidderCode).to.be.a('function'); + expect(adapter.getBidderCode()).to.equal(bidderCode); + }); + it('setBidderCode', () => { + expect(adapter.setBidderCode).to.be.a('function'); + }); + it('callBids', () => { + expect(adapter.setBidderCode).to.be.a('function'); + }); + }); + + describe('callBids parameter parsing', () => { + + let xhr; + let requests; + let addBidResponse; + let logError; + + beforeEach(() => { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + requests = []; + addBidResponse = sinon.stub(bidmanager, 'addBidResponse'); + logError = sinon.stub(utils, 'logError'); + }); + + afterEach(() => { + xhr.restore(); + bidmanager.addBidResponse.restore(); + utils.logError.restore(); + }); + + it('missing placementId parameter', () => { + // Invalid parameters + const params = { + bidderCode, + bids: [{ + bidder: bidderCode, + sizes: ['native'] + }] + }; + // Request bids + AudienceNetwork().callBids(params); + // Verify no attempt to fetch response + expect(requests).to.have.lengthOf(0); + // Verify no attempt to add a response as no placement was provided + expect(addBidResponse.calledOnce).to.equal(false); + // Verify attempt to log error + expect(logError.calledOnce).to.equal(true); + }); + + it('invalid sizes parameter', () => { + // Invalid parameters + const params = { + bidderCode, + bids: [{ + bidder: bidderCode, + params: { placementId }, + sizes: ['', undefined, null, '300x100', [300, 100], [300], {}] + }] + }; + // Request bids + AudienceNetwork().callBids(params); + // Verify no attempt to fetch response + expect(requests).to.have.lengthOf(0); + // Verify attempt to log error + expect(logError.calledOnce).to.equal(true); + }); + + it('valid parameters', () => { + // Valid parameters + const params = { + bidderCode, + bids: [{ + bidder: bidderCode, + params: { placementId }, + sizes: [[320, 50], [300, 250], '300x250', 'fullwidth', '320x50', 'native'] + }] + }; + // Request bids + AudienceNetwork().callBids(params); + // Verify attempt to fetch response + expect(requests).to.have.lengthOf(1); + expect(requests[0].method).to.equal('GET'); + expectToContain(requests[0].url, 'https://an.facebook.com/v2/placementbid.json?'); + expectToContain(requests[0].url, 'placementids[]=test-placement-id', 6); + expectToContain(requests[0].url, 'adformats[]=320x50', 2); + expectToContain(requests[0].url, 'adformats[]=300x250', 2); + expectToContain(requests[0].url, 'adformats[]=fullwidth'); + expectToContain(requests[0].url, 'adformats[]=native'); + // Verify no attempt to log error + expect(logError.called).to.equal(false); + }); + + }); + + describe('callBids response handling', () => { + + let server; + let addBidResponse; + let logError; + + beforeEach( () => { + server = sinon.fakeServer.create(); + addBidResponse = sinon.stub(bidmanager, 'addBidResponse'); + logError = sinon.stub(utils, 'logError'); + }); + + afterEach( () => { + server.restore(); + bidmanager.addBidResponse.restore(); + utils.logError.restore(); + }); + + it('error in response', () => { + // Error response + const error = 'test-error-message'; + server.respondWith(JSON.stringify({ + errors: [error] + })); + // Request bids + AudienceNetwork().callBids({ + bidderCode, + bids: [{ + bidder: bidderCode, + params: { placementId }, + sizes: ['native'] + }] + }); + server.respond(); + // Verify attempt to call addBidResponse + expect(addBidResponse.calledOnce).to.equal(true); + expect(addBidResponse.args[0]).to.have.lengthOf(2); + expect(addBidResponse.args[0][1].getStatusCode()).to.equal(STATUS.NO_BID); + expect(addBidResponse.args[0][1].bidderCode).to.equal(bidderCode); + // Verify attempt to log error + expect(logError.calledOnce).to.equal(true); + expect(logError.calledWith(error)).to.equal(true); + }); + + it('valid native bid in response', () => { + // Valid response + server.respondWith(JSON.stringify({ + errors: [], + bids: { + [placementId]: [{ + placement_id: placementId, + bid_id: 'test-bid-id', + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } + })); + // Request bids + AudienceNetwork().callBids({ + bidderCode, + bids: [{ + bidder: bidderCode, + placementCode, + params: { placementId }, + sizes: ['native'] + }] + }); + server.respond(); + // Verify attempt to call addBidResponse + expect(addBidResponse.calledOnce).to.equal(true); + expect(addBidResponse.args[0]).to.have.lengthOf(2); + expect(addBidResponse.args[0][0]).to.equal(placementCode); + // Verify Prebid attributes in bid response + const bidResponse = addBidResponse.args[0][1]; + expect(bidResponse.getStatusCode()).to.equal(STATUS.GOOD); + expect(bidResponse.cpm).to.equal(1.23); + expect(bidResponse.bidderCode).to.equal(bidderCode); + expect(bidResponse.width).to.equal(0); + expect(bidResponse.height).to.equal(0); + expect(bidResponse.ad).to.contain(`placementid:'${placementId}',format:'native',bidid:'test-bid-id'`, 'ad missing parameters'); + expect(bidResponse.ad).to.contain('getElementsByTagName("style")', 'ad missing native styles'); + expect(bidResponse.ad).to.contain('
', 'ad missing native container'); + // Verify Audience Network attributes in bid response + expect(bidResponse.hb_bidder).to.equal('fan'); + expect(bidResponse.fb_bidid).to.equal('test-bid-id'); + expect(bidResponse.fb_format).to.equal('native'); + expect(bidResponse.fb_placementid).to.equal(placementId); + // Verify no attempt to log error + expect(logError.called).to.equal(false, 'logError called'); + }); + + it('valid IAB bid in response', () => { + // Valid response + server.respondWith(JSON.stringify({ + errors: [], + bids: { + [placementId]: [{ + placement_id: placementId, + bid_id: 'test-bid-id', + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } + })); + // Request bids + AudienceNetwork().callBids({ + bidderCode, + bids: [{ + bidder: bidderCode, + placementCode, + params: { placementId }, + sizes: ['300x250'] + }] + }); + server.respond(); + // Verify attempt to call addBidResponse + expect(addBidResponse.calledOnce).to.equal(true); + expect(addBidResponse.args[0]).to.have.lengthOf(2); + expect(addBidResponse.args[0][0]).to.equal(placementCode); + // Verify bidResponse Object + const bidResponse = addBidResponse.args[0][1]; + expect(bidResponse.getStatusCode()).to.equal(STATUS.GOOD); + expect(bidResponse.cpm).to.equal(1.23); + expect(bidResponse.bidderCode).to.equal(bidderCode); + expect(bidResponse.width).to.equal(300); + expect(bidResponse.height).to.equal(250); + expect(bidResponse.ad).to.contain(`placementid:'${placementId}',format:'300x250',bidid:'test-bid-id'`, 'ad missing parameters'); + expect(bidResponse.ad).not.to.contain('getElementsByTagName("style")', 'ad should not contain native styles'); + expect(bidResponse.ad).not.to.contain('
', 'ad should not contain native container'); + // Verify no attempt to log error + expect(logError.called).to.equal(false, 'logError called'); + }); + + it('valid multiple bids in response', () => { + const placementIdNative = 'test-placement-id-native'; + const placementIdIab = 'test-placement-id-iab'; + const placementCodeNative = 'test-placement-code-native'; + const placementCodeIab = 'test-placement-code-iab'; + // Valid response + server.respondWith(JSON.stringify({ + errors: [], + bids: { + [placementIdNative]: [{ + placement_id: placementIdNative, + bid_id: 'test-bid-id-native', + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }], + [placementIdIab]: [{ + placement_id: placementIdIab, + bid_id: 'test-bid-id-iab', + bid_price_cents: 456, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } + })); + // Request bids + AudienceNetwork().callBids({ + bidderCode, + bids: [{ + bidder: bidderCode, + placementCode: placementCodeNative, + params: { placementId: placementIdNative }, + sizes: ['native'] + }, { + bidder: bidderCode, + placementCode: placementCodeIab, + params: { placementId: placementIdIab }, + sizes: ['300x250'] + }] + }); + server.respond(); + // Verify multiple attempts to call addBidResponse + expect(addBidResponse.calledTwice).to.equal(true); + // Verify native + const addBidResponseNativeCall = addBidResponse.args[0]; + expect(addBidResponseNativeCall).to.have.lengthOf(2); + expect(addBidResponseNativeCall[0]).to.equal(placementCodeNative); + expect(addBidResponseNativeCall[1].getStatusCode()).to.equal(STATUS.GOOD); + expect(addBidResponseNativeCall[1].cpm).to.equal(1.23); + expect(addBidResponseNativeCall[1].bidderCode).to.equal(bidderCode); + expect(addBidResponseNativeCall[1].width).to.equal(0); + expect(addBidResponseNativeCall[1].height).to.equal(0); + expect(addBidResponseNativeCall[1].ad).to.contain(`placementid:'${placementIdNative}',format:'native',bidid:'test-bid-id-native'`, 'ad missing parameters'); + // Verify IAB + const addBidResponseIabCall = addBidResponse.args[1]; + expect(addBidResponseIabCall).to.have.lengthOf(2); + expect(addBidResponseIabCall[0]).to.equal(placementCodeIab); + expect(addBidResponseIabCall[1].getStatusCode()).to.equal(STATUS.GOOD); + expect(addBidResponseIabCall[1].cpm).to.equal(4.56); + expect(addBidResponseIabCall[1].bidderCode).to.equal(bidderCode); + expect(addBidResponseIabCall[1].width).to.equal(300); + expect(addBidResponseIabCall[1].height).to.equal(250); + expect(addBidResponseIabCall[1].ad).to.contain(`placementid:'${placementIdIab}',format:'300x250',bidid:'test-bid-id-iab'`, 'ad missing parameters'); + // Verify no attempt to log error + expect(logError.called).to.equal(false, 'logError called'); + }); + + }); + +}); diff --git a/test/spec/adapters/bidfluence_spec.js b/test/spec/adapters/bidfluence_spec.js new file mode 100644 index 00000000000..7c414853056 --- /dev/null +++ b/test/spec/adapters/bidfluence_spec.js @@ -0,0 +1,74 @@ +describe('Bidfluence Adapter', () => { + const expect = require('chai').expect; + const adapter = require('src/adapters/bidfluence'); + const bidmanager = require('src/bidmanager'); + + var REQUEST = { + bidderCode: "bidfluence", + sizes: [[300, 250]], + placementCode: "div-1", + bids: [{ + bidder: 'bidfluence', + params: { + pubId: "747efe9c-5f8a-4b6e-872b-8e9939816298", + adunitId: "c4bbd807-7d22-485f-80f1-ba008ef1c619" + } + }] + }; + + var RESPONSE = { + ad: "ad-code", + cpm: 0.9, + width: 300, + height: 250, + placementCode: "div-1" + }; + + var NO_RESPONSE = { + ad: "ad-code", + cpm: 0, + width: 300, + height: 250, + placementCode: "div-1" + }; + + it('Should exist and be a function', function () { + expect($$PREBID_GLOBAL$$.bfPbjsCB).to.exist.and.to.be.a('function'); + }); + + it('Shoud push a valid bid', () => { + + var stubAddBidResponse = sinon.stub(bidmanager, "addBidResponse"); + pbjs._bidsRequested.push(REQUEST); + adapter(); + $$PREBID_GLOBAL$$.bfPbjsCB(RESPONSE); + + var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; + var bidObject1 = stubAddBidResponse.getCall(0).args[1]; + + expect(bidPlacementCode1).to.equal("div-1"); + expect(bidObject1.getStatusCode()).to.equal(1); + expect(bidObject1.bidderCode).to.equal('bidfluence'); + + stubAddBidResponse.restore(); + }); + + it('Shoud push an empty bid', () => { + + var stubAddBidResponse = sinon.stub(bidmanager, "addBidResponse"); + pbjs._bidsRequested.push(REQUEST); + adapter(); + + $$PREBID_GLOBAL$$.bfPbjsCB(NO_RESPONSE); + + var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; + var bidObject1 = stubAddBidResponse.getCall(0).args[1]; + + expect(bidPlacementCode1).to.equal("div-1"); + expect(bidObject1.getStatusCode()).to.equal(2); + expect(bidObject1.bidderCode).to.equal('bidfluence'); + + stubAddBidResponse.restore(); + + }); +}); diff --git a/test/spec/adapters/centro_spec.js b/test/spec/adapters/centro_spec.js index a3a8f184e92..a3f186fad65 100644 --- a/test/spec/adapters/centro_spec.js +++ b/test/spec/adapters/centro_spec.js @@ -78,7 +78,7 @@ describe('centro adapter tests', function () { var parsedBidUrl = urlParse(bidUrl1); var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - var generatedCallback = 'adCentroHandler_28136300x250'; + var generatedCallback = 'window["adCentroHandler_28136300x250div-gpt-ad-12345-1"]'; expect(parsedBidUrl.hostname).to.equal('staging.brand-server.com'); expect(parsedBidUrl.pathname).to.equal('/hb'); @@ -92,13 +92,13 @@ describe('centro adapter tests', function () { parsedBidUrl = urlParse(bidUrl2); parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - generatedCallback = 'adCentroHandler_28137728x90'; + generatedCallback = 'window["adCentroHandler_28137728x90div-gpt-ad-12345-2"]'; expect(parsedBidUrl.hostname).to.equal('t.brand-server.com'); expect(parsedBidUrl.pathname).to.equal('/hb'); expect(parsedBidUrlQueryString).to.have.property('s').and.to.equal('28137'); - expect(parsedBidUrlQueryString).to.not.have.property('url'); + expect(parsedBidUrlQueryString).to.have.property('url').and.to.equal(location.href); expect(parsedBidUrlQueryString).to.have.property('sz').and.to.equal('728x90'); expect(parsedBidUrlQueryString).to.have.property('callback').and.to.equal(generatedCallback); }); @@ -158,8 +158,10 @@ describe('centro adapter tests', function () { adapter().callBids(params); - expect(window['adCentroHandler_28136300x250']).to.exist.and.to.be.a('function'); - expect(window['adCentroHandler_111111728x90']).to.exist.and.to.be.a('function'); + expect(window['adCentroHandler_28136300x250%2F19968336%2Fheader-bid-tag-0']) + .to.exist.and.to.be.a('function'); + expect(window['adCentroHandler_111111728x90%2F19968336%2Fheader-bid-tag-1']) + .to.exist.and.to.be.a('function'); }); it('bidmanager.addBidResponse should be called with correct arguments', function () { @@ -189,10 +191,10 @@ describe('centro adapter tests', function () { var response3 = {"adTag":"","height":0,"value":0,"width":0,"sectionID":222222}; var response4 = ''; - window['adCentroHandler_28136300x250'](response); - window['adCentroHandler_111111728x90'](response2); - window['adCentroHandler_222222728x90'](response3); - window['adCentroHandler_333333728x90'](response4); + window['adCentroHandler_28136300x250%2F19968336%2Fheader-bid-tag-0'](response); + window['adCentroHandler_111111728x90%2F19968336%2Fheader-bid-tag-1'](response2); + window['adCentroHandler_222222728x90%2F19968336%2Fheader-bid-tag-2'](response3); + window['adCentroHandler_333333728x90%2F19968336%2Fheader-bid-tag-3'](response4); var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; var bidObject1 = stubAddBidResponse.getCall(0).args[1]; diff --git a/test/spec/adapters/getintent_spec.js b/test/spec/adapters/getintent_spec.js index 9560bf56c9c..64b33ba72ec 100644 --- a/test/spec/adapters/getintent_spec.js +++ b/test/spec/adapters/getintent_spec.js @@ -2,6 +2,8 @@ import Adapter from '../../../src/adapters/getintent'; import bidManager from '../../../src/bidmanager'; import {expect} from 'chai'; +var assert = require('chai').assert; + describe('getintent media adapter test', () => { let adapter; @@ -17,10 +19,16 @@ describe('getintent media adapter test', () => { cpm : 2.71, size : `${bidRequest.size}` }, bidRequest); - } else { + } else if (pid == "p3") { callback({ no_bid: 1 }, bidRequest); + } else if (pid == "p4") { + callback({ + vast_url : `http://test.com?pid=${pid}&tid=${tid}`, + cpm : 2.88, + size : `${bidRequest.size}` + }, bidRequest); } } }; @@ -58,6 +66,17 @@ describe('getintent media adapter test', () => { tid: "t2", cur: "USD" } + }, + { + bidder: "getintent", + adUnitCode: "test4", + mediaType: 'video', + sizes: [[480,352]], + params: { + pid: "p4", + tid: "t3", + cur: "USD" + } } ] }); @@ -75,6 +94,7 @@ describe('getintent media adapter test', () => { let firstBid; let secondBid; let thirdBid; + let videoBid; beforeEach(() => { sinon.stub(bidManager, 'addBidResponse'); @@ -82,14 +102,15 @@ describe('getintent media adapter test', () => { firstBid = bidManager.addBidResponse.firstCall.args[1]; secondBid = bidManager.addBidResponse.secondCall.args[1]; thirdBid = bidManager.addBidResponse.thirdCall.args[1]; + videoBid = bidManager.addBidResponse.lastCall.args[1]; }); afterEach(() => { bidManager.addBidResponse.restore(); }); - it('was called three times', () => { - sinon.assert.calledThrice(bidManager.addBidResponse); + it('was called four times', () => { + assert.strictEqual(bidManager.addBidResponse.callCount, 4); }); it('will respond to the first bid', () => { @@ -116,6 +137,13 @@ describe('getintent media adapter test', () => { expect(secondBid).to.have.property('bidderCode', 'getintent'); expect(thirdBid).to.have.property('bidderCode', 'getintent'); }); + + it('will respond to the video bid', () => { + expect(videoBid).to.have.property('vastUrl', 'http://test.com?pid=p4&tid=t3'); + expect(videoBid).to.have.property('cpm', 2.88); + expect(videoBid).to.have.property('width', '480'); + expect(videoBid).to.have.property('height', '352'); + }); }); }); diff --git a/test/spec/adapters/inneractive_spec.js b/test/spec/adapters/inneractive_spec.js new file mode 100644 index 00000000000..819e01b0139 --- /dev/null +++ b/test/spec/adapters/inneractive_spec.js @@ -0,0 +1,295 @@ +/* globals context */ + +import {expect} from 'chai'; +import {default as InneractiveAdapter} from 'src/adapters/inneractive'; +import bidmanager from 'src/bidmanager'; + + +// Using plain-old-style functions, why? see: http://mochajs.org/#arrow-functions +describe('InneractiveAdapter', function () { + let adapter, + bidRequest; + + beforeEach(function () { + adapter = InneractiveAdapter.createNew(); + bidRequest = { + bidderCode: "inneractive", + bids: [ + { + bidder: "inneractive", + params: { + appId: "", + }, + placementCode: "div-gpt-ad-1460505748561-0", + sizes: [[300, 250], [300, 600]], + bidId: "507e8db167d219", + bidderRequestId: "49acc957f92917", + requestId: "51381cd0-c29c-405b-9145-20f60abb1e76" + }, + { + bidder: "inneractive", + params: { + noappId: "...", + }, + placementCode: "div-gpt-ad-1460505661639-0", + sizes: [[728, 90], [970, 90]], + bidId: "507e8db167d220", + bidderRequestId: "49acc957f92917", + requestId: "51381cd0-c29c-405b-9145-20f60abb1e76" + }, + { + bidder: "inneractive", + params: { + APP_ID: "Inneractive_AndroidHelloWorld_Android", + spotType: "rectangle", + customParams: { + Portal: 7002, + } + }, + placementCode: "div-gpt-ad-1460505748561-0", + sizes: [[320, 50], [300, 600]], + bidId: "507e8db167d221", + bidderRequestId: "49acc957f92917", + requestId: "51381cd0-c29c-405b-9145-20f60abb1e76" + }, + { + bidder: "inneractive", + params: { + appId: "Inneractive_IosHelloWorld_iPhone", + spotType: "banner", // Just for coverage considerations, no real impact in production + customParams: { + portal: 7001, + gender: '' + } + }, + placementCode: "div-gpt-ad-1460505661639-0", + sizes: [[728, 90], [970, 90]], + bidId: "507e8db167d222", + bidderRequestId: "49acc957f92917", + requestId: "51381cd0-c29c-405b-9145-20f60abb1e76" + }] + }; + }); + + describe('Reporter', function () { + context('on HBPreBidError event', function () { + it('should contain "mbwError" the inside event report url', function () { + const Reporter = InneractiveAdapter._getUtils().Reporter; + const extraDetailsParam = { + "appId": "CrunchMind_DailyDisclosure_other", + "spotType": "rectangle", + "portal": 7002 + }; + let eventReportUrl = Reporter.getEventUrl('HBPreBidError', extraDetailsParam); + expect(eventReportUrl).to.include('mbwError'); + }); + }); + }); + + describe('.createNew()', function () { + it('should return an instance of this adapter having a "callBids" method', function () { + expect(adapter) + .to.be.instanceOf(InneractiveAdapter).and + .to.have.property('callBids').and + .to.be.a('function'); + }); + }); + + describe('when sending out bid requests to the ad server', function () { + let bidRequests, + xhr; + + beforeEach(function () { + bidRequests = []; + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = (request) => { + bidRequests.push(request); + }; + }); + + afterEach(function () { + xhr.restore(); + }); + + context('when there are no bid requests', function () { + it('should not issue a request', function () { + const Reporter = InneractiveAdapter._getUtils().Reporter; + Reporter.getEventUrl('HBPreBidError', { + "appId": "CrunchMind_DailyDisclosure_other", + "spotType": "rectangle", + "portal": 7002 + }); + + delete bidRequest.bids; + adapter.callBids(bidRequest); + + expect(bidRequests).to.be.empty; // jshint ignore:line + }); + }); + + context('when there is at least one bid request', function () { + it('should filter out invalid bids', function () { + const INVALID_BIDS_COUNT = 2; + sinon.spy(adapter, '_isValidRequest'); + adapter.callBids(bidRequest); + + + for (let id = 0; id < INVALID_BIDS_COUNT; id++) { + expect(adapter._isValidRequest.getCall(id).returned(false)).to.be.true; // jshint ignore:line + } + + adapter._isValidRequest.restore(); + }); + + it('should store all valid bids internally', function () { + adapter.callBids(bidRequest); + expect(Object.keys(adapter.bidByBidId).length).to.equal(2); + }); + + it('should issue ad requests to the ad server for every valid bid', function () { + adapter.callBids(bidRequest); + expect(bidRequests).to.have.lengthOf(2); + }); + }); + }); + + describe('when registering the bids that are returned with Prebid.js', function () { + const BID_DETAILS_ARG_INDEX = 1; + let server; + + beforeEach(function () { + sinon.stub(bidmanager, 'addBidResponse'); + server = sinon.fakeServer.create(); + }); + + afterEach(function () { + server.restore(); + bidmanager.addBidResponse.restore(); + }); + + context('when the bid is valid', function () { + let adServerResponse, + headers, + body; + + beforeEach(function () { + adServerResponse = { + headers: { + "X-IA-Ad-Height": 250, + "X-IA-Ad-Width": 300, + "X-IA-Error": "OK", + "X-IA-Pricing": "CPM", + "X-IA-Pricing-Currency": "USD", + "X-IA-Pricing-Value": 0.0005 + }, + body: { + ad: { + html: "" + }, + config: { + tracking: { + impressions: [ + "http://event.inner-active.mobi/simpleM2M/reportEvent?eventArchetype=impress…pe=3&network=Inneractive_CS&acp=&pcp=&secure=false&rtb=false&houseAd=false" + ], + clicks: [ + "http://event.inner-active.mobi/simpleM2M/reportEvent?eventArchetype=richMed…pe=3&network=Inneractive_CS&acp=&pcp=&secure=false&rtb=false&houseAd=false", + "" + ], + passback: "http://event.inner-active.mobi/simpleM2M/reportEvent?eventArchetype=passbac…pe=3&network=Inneractive_CS&acp=&pcp=&secure=false&rtb=false&houseAd=false" + }, + moat: { + countryCode: "IL" + } + } + } + }; + headers = adServerResponse.headers; + body = JSON.stringify(adServerResponse.body); + }); + + it('should register bid responses with a status code of 1', function () { + server.respondWith([200, headers, body]); + adapter.callBids(bidRequest); + server.respond(); + + let firstRegisteredBidResponse = bidmanager.addBidResponse.firstCall.args[BID_DETAILS_ARG_INDEX]; + expect(firstRegisteredBidResponse) + .to.have.property('statusMessage', 'Bid available'); + }); + + it('should use the first element inside the bid request size array when no (width,height) is returned within the headers', function () { + delete headers['X-IA-Ad-Height']; + delete headers['X-IA-Ad-Width']; + server.respondWith([200, headers, body]); + adapter.callBids(bidRequest); + server.respond(); + + let firstRegisteredBidResponse = bidmanager.addBidResponse.firstCall.args[BID_DETAILS_ARG_INDEX]; + expect(firstRegisteredBidResponse).to.have.property('width', 320); + expect(firstRegisteredBidResponse).to.have.property('height', 50); + }); + }); + + context('when the bid is invalid', function () { + let passbackAdServerResponse, + headers, + body; + + beforeEach(function () { + passbackAdServerResponse = { + headers: { + "X-IA-Error": "House Ad", + "X-IA-Content": 600145, + "X-IA-Cid": 99999, + "X-IA-Publisher": 206536, + "Content-Type": "application/json; charset=UTF-8", + "X-IA-Session": 6512147119979250840, + "X-IA-AdNetwork": "inneractive360" + }, + body: { + "ad": { + "html": "" + }, + "config": { + "passback": "http://event.inner-active.mobi/simpleM2M/reportEvent?eventArchetype=passbac…pe=3&network=Inneractive_CS&acp=&pcp=&secure=false&rtb=false&houseAd=false" + } + } + }; + headers = passbackAdServerResponse.headers; + body = JSON.stringify(passbackAdServerResponse.body); + }); + + it('should register bid responses with a status code of 2', function () { + server.respondWith([200, headers, body]); + adapter.callBids(bidRequest); + server.respond(); + + let firstRegisteredBidResponse = bidmanager.addBidResponse.firstCall.args[BID_DETAILS_ARG_INDEX]; + expect(firstRegisteredBidResponse) + .to.have.property('statusMessage', 'Bid returned empty or error response'); + }); + + it('should handle responses from our server in case we had no ad to offer', function () { + const n = bidRequest.bids.length; + bidRequest.bids[n - 1].params.appId = "Komoona_InquisitrRectangle2_other"; + server.respondWith([200, headers, body]); + adapter.callBids(bidRequest); + server.respond(); + + let secondRegisteredBidResponse = bidmanager.addBidResponse.secondCall.args[BID_DETAILS_ARG_INDEX]; + expect(secondRegisteredBidResponse) + .to.have.property('statusMessage', 'Bid returned empty or error response'); + }); + + it('should handle JSON.parse errors', function () { + server.respondWith(''); + adapter.callBids(bidRequest); + server.respond(); + + const firstRegisteredBidResponse = bidmanager.addBidResponse.firstCall.args[BID_DETAILS_ARG_INDEX]; + expect(firstRegisteredBidResponse) + .to.have.property('statusMessage', 'Bid returned empty or error response'); + }); + }); + }); +}); diff --git a/test/spec/adapters/lifestreet_spec.js b/test/spec/adapters/lifestreet_spec.js index 3ae2b6add2f..848c899fcbf 100644 --- a/test/spec/adapters/lifestreet_spec.js +++ b/test/spec/adapters/lifestreet_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; +import {cloneJson} from 'src/utils'; import adloader from 'src/adloader'; import bidmanager from 'src/bidmanager'; -import * as utils from 'src/utils'; import LifestreetAdapter from 'src/adapters/lifestreet'; const BIDDER_REQUEST = { @@ -44,7 +44,7 @@ describe ('LifestreetAdapter', () => { beforeEach(() => { tagRequests = []; - request = utils.extend(request, BIDDER_REQUEST); + request = cloneJson(BIDDER_REQUEST); sinon.stub(adloader, 'loadScript', (url, callback) => { tagRequests.push(url); callback(); diff --git a/test/spec/adapters/mantis_spec.js b/test/spec/adapters/mantis_spec.js index 616dbcdc0d4..1ed6ea089e8 100644 --- a/test/spec/adapters/mantis_spec.js +++ b/test/spec/adapters/mantis_spec.js @@ -33,7 +33,8 @@ describe('mantis adapter tests', function () { placementCode: 'foo', sizes: [[728, 90]], params: { - property: '1234' + property: '1234', + zoneId: 'zone1' } }, { @@ -42,7 +43,8 @@ describe('mantis adapter tests', function () { placementCode: 'bar', sizes: [[300, 600], [300, 250]], params: { - property: '1234' + property: '1234', + zoneId: 'zone2' } } ] @@ -103,11 +105,13 @@ describe('mantis adapter tests', function () { expect(serverCall).to.string('bids[0][bidId]=bidId1&'); expect(serverCall).to.string('bids[0][sizes][0][width]=728&'); expect(serverCall).to.string('bids[0][sizes][0][height]=90&'); + expect(serverCall).to.string('bids[0][config][zoneId]=zone1&'); expect(serverCall).to.string('bids[1][bidId]=bidId2&'); expect(serverCall).to.string('bids[1][sizes][0][width]=300&'); expect(serverCall).to.string('bids[1][sizes][0][height]=600&'); expect(serverCall).to.string('bids[1][sizes][1][width]=300&'); expect(serverCall).to.string('bids[1][sizes][1][height]=250&'); + expect(serverCall).to.string('bids[1][config][zoneId]=zone2&'); expect(serverCall).to.string('version=1'); }); diff --git a/test/spec/adapters/pulsepointLite_spec.js b/test/spec/adapters/pulsepointLite_spec.js new file mode 100644 index 00000000000..c52ef556880 --- /dev/null +++ b/test/spec/adapters/pulsepointLite_spec.js @@ -0,0 +1,108 @@ +import {expect} from 'chai'; +import PulsePointAdapter from 'src/adapters/pulsepointLite'; +import bidManager from 'src/bidmanager'; +import * as ajax from "src/ajax"; +import {parse as parseURL} from 'src/url'; + +describe("PulsePoint Lite Adapter Tests", () => { + + let pulsepointAdapter = new PulsePointAdapter(); + let slotConfigs; + let ajaxStub; + + beforeEach(() => { + sinon.stub(bidManager, 'addBidResponse'); + ajaxStub = sinon.stub(ajax, 'ajax'); + + slotConfigs = { + bids: [ + { + placementCode: "/DfpAccount1/slot1", + bidder: "pulsepoint", + bidId: 'bid12345', + params: { + cp: "p10000", + ct: "t10000", + cf: "300x250" + } + },{ + placementCode: "/DfpAccount2/slot2", + bidder: "pulsepoint", + bidId: 'bid23456', + params: { + cp: "p20000", + ct: "t20000", + cf: "728x90" + } + } + ] + }; + }); + + afterEach(() => { + bidManager.addBidResponse.restore(); + ajaxStub.restore(); + }); + + it('Verify requests sent to PulsePoint', () => { + pulsepointAdapter.callBids(slotConfigs); + var call = parseURL(ajaxStub.firstCall.args[0]).search; + //slot 1 + expect(call.cp).to.equal('p10000'); + expect(call.ct).to.equal('t10000'); + expect(call.cf).to.equal('300x250'); + expect(call.ca).to.equal('BID'); + expect(call.cn).to.equal('1'); + //slot 2 + call = parseURL(ajaxStub.secondCall.args[0]).search; + expect(call.cp).to.equal('p20000'); + expect(call.ct).to.equal('t20000'); + expect(call.cf).to.equal('728x90'); + expect(call.ca).to.equal('BID'); + expect(call.cn).to.equal('1'); + }); + + it('Verify bid', () => { + pulsepointAdapter.callBids(slotConfigs); + //trigger a mock ajax callback with bid. + ajaxStub.firstCall.args[1](JSON.stringify({ + html: 'This is an Ad', + bidCpm: 1.25 + })); + let placement = bidManager.addBidResponse.firstCall.args[0]; + let bid = bidManager.addBidResponse.firstCall.args[1]; + expect(placement).to.equal('/DfpAccount1/slot1'); + expect(bid.bidderCode).to.equal('pulsepoint'); + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.equal('This is an Ad'); + expect(bid.width).to.equal('300'); + expect(bid.height).to.equal('250'); + expect(bid.adId).to.equal('bid12345'); + }); + + it('Verify passback', () => { + pulsepointAdapter.callBids(slotConfigs); + //trigger a mock ajax callback with no bid. + ajaxStub.firstCall.args[1](null); + let placement = bidManager.addBidResponse.firstCall.args[0]; + let bid = bidManager.addBidResponse.firstCall.args[1]; + expect(placement).to.equal('/DfpAccount1/slot1'); + expect(bid.bidderCode).to.equal('pulsepoint'); + expect(bid).to.not.have.property('ad'); + expect(bid).to.not.have.property('cpm'); + expect(bid.adId).to.equal('bid12345'); + }); + + it('Verify passback when ajax call fails', () => { + ajaxStub.throws(); + pulsepointAdapter.callBids(slotConfigs); + let placement = bidManager.addBidResponse.firstCall.args[0]; + let bid = bidManager.addBidResponse.firstCall.args[1]; + expect(placement).to.equal('/DfpAccount1/slot1'); + expect(bid.bidderCode).to.equal('pulsepoint'); + expect(bid).to.not.have.property('ad'); + expect(bid).to.not.have.property('cpm'); + expect(bid.adId).to.equal('bid12345'); + }); + +}); diff --git a/test/spec/adapters/pulsepoint_spec.js b/test/spec/adapters/pulsepoint_spec.js index 0e9fb97fa4c..b3c24d38890 100644 --- a/test/spec/adapters/pulsepoint_spec.js +++ b/test/spec/adapters/pulsepoint_spec.js @@ -127,7 +127,7 @@ describe("PulsePoint Adapter Tests", () => { pulsepointAdapter.callBids(slotConfigs); let libraryLoadCall = adLoader.loadScript.firstCall.args[0]; let callback = adLoader.loadScript.firstCall.args[1]; - expect(libraryLoadCall).to.equal('http://tag.contextweb.com/getjs.static.js'); + expect(libraryLoadCall).to.equal('http://tag-st.contextweb.com/getjs.static.js'); expect(callback).to.be.a('function'); }); diff --git a/test/spec/adapters/rhythmone_spec.js b/test/spec/adapters/rhythmone_spec.js index 29c308de74b..00f19a551d3 100644 --- a/test/spec/adapters/rhythmone_spec.js +++ b/test/spec/adapters/rhythmone_spec.js @@ -39,7 +39,7 @@ describe('rhythmone adapter tests', function () { assert.equal(placementcode, "div-gpt-ad-1438287399331-0"); }); it("should have the expected ad response", function(){ - assert.equal((adResponse.ad.length > 0), true); + assert.equal((adResponse.ad === undefined || adResponse.ad.length > 0), true); assert.equal(adResponse.width, 300); assert.equal(adResponse.height, 250); assert.equal(adResponse.cpm, 1); @@ -54,7 +54,8 @@ describe('rhythmone adapter tests', function () { if(e.toLowerCase() === "auctionend") endEvent = f; if(e.toLowerCase() === "bidwon") wonEvent = f; }, - "getBidResponses":function(){return {"div-gpt-ad-1438287399331-0":{"bids":[{cpm:1,bidderCode:"rhythmone"},{cpm:2,bidderCode:"rhythmone"}]}};} + "getBidResponses":function(){return {"div-gpt-ad-1438287399331-0":{"bids":[{cpm:1,bidderCode:"rhythmone"},{cpm:2,bidderCode:"rhythmone"}]}};}, + "version": "v0.20.0-pre" } }, function(url, callback){ @@ -70,10 +71,11 @@ describe('rhythmone adapter tests', function () { "placementId":"xyz", "keywords":"", "categories":[], - "trace":true, - "method":"get", - "endpoint":"http://fakedomain.com" + "trace":true, + "method":"get", + "endpoint":"http://fakedomain.com" }, + "mediaType": "video", "placementCode":"div-gpt-ad-1438287399331-0", "sizes":[[300,250]] } diff --git a/test/spec/adapters/rubicon_spec.js b/test/spec/adapters/rubicon_spec.js index cc16579c29d..1a51f5217f6 100644 --- a/test/spec/adapters/rubicon_spec.js +++ b/test/spec/adapters/rubicon_spec.js @@ -201,7 +201,7 @@ describe('the rubicon adapter', () => { xhr.restore(); }); - describe("to fastlane", () => { + describe('to fastlane', () => { it('should make a well-formed request', () => { @@ -277,7 +277,21 @@ describe('the rubicon adapter', () => { expect(bids[0].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); }); - + + it('should not send a request and register an error if no account id is present', () => { + + var noAccountBidderRequest = clone(bidderRequest); + delete noAccountBidderRequest.bids[0].params.accountId; + + rubiconAdapter.callBids(noAccountBidderRequest); + + expect(xhr.requests.length).to.equal(0); + expect(bidManager.addBidResponse.calledOnce).to.equal(true); + expect(bids).to.be.lengthOf(1); + expect(bids[0].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); + + }); + it('should allow a floor override', () => { var floorBidderRequest = clone(bidderRequest); @@ -298,7 +312,7 @@ describe('the rubicon adapter', () => { beforeEach(() => { createVideoBidderRequest(); - sandbox.stub(Date, "now", () => + sandbox.stub(Date, 'now', () => bidderRequest.auctionStart + 100 ); }); @@ -312,7 +326,7 @@ describe('the rubicon adapter', () => { let url = request.url; let post = JSON.parse(request.requestBody); - expect(url).to.equal('//optimized-by-adv.rubiconproject.com/v1/auction/video'); + expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); expect(post).to.have.property('page_url').that.is.a('string'); expect(post.resolution).to.match(/\d+x\d+/); @@ -396,55 +410,55 @@ describe('the rubicon adapter', () => { it('should handle a success response and sort by cpm', () => { server.respondWith(JSON.stringify({ - "status": "ok", - "account_id": 14062, - "site_id": 70608, - "zone_id": 530022, - "size_id": 15, - "alt_size_ids": [ + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ 43 ], - "tracking": "", - "inventory": {}, - "ads": [ + 'tracking': '', + 'inventory': {}, + 'ads': [ { - "status": "ok", - "impression_id": "153dc240-8229-4604-b8f5-256933b9374c", - "size_id": "15", - "ad_id": "6", - "advertiser": 7, - "network": 8, - "creative_id": 9, - "type": "script", - "script": "alert('foo')", - "campaign_id": 10, - "cpm": 0.811, - "targeting": [ + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374c', + 'size_id': '15', + 'ad_id': '6', + 'advertiser': 7, + 'network': 8, + 'creative_id': 9, + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.811, + 'targeting': [ { - "key": "rpfl_14062", - "values": [ - "15_tier_all_test" + 'key': 'rpfl_14062', + 'values': [ + '15_tier_all_test' ] } ] }, { - "status": "ok", - "impression_id": "153dc240-8229-4604-b8f5-256933b9374d", - "size_id": "43", - "ad_id": "7", - "advertiser": 7, - "network": 8, - "creative_id": 9, - "type": "script", - "script": "alert('foo')", - "campaign_id": 10, - "cpm": 0.911, - "targeting": [ + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', + 'size_id': '43', + 'ad_id': '7', + 'advertiser': 7, + 'network': 8, + 'creative_id': 9, + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.911, + 'targeting': [ { - "key": "rpfl_14062", - "values": [ - "15_tier_all_test" + 'key': 'rpfl_14062', + 'values': [ + '43_tier_all_test' ] } ] @@ -461,40 +475,44 @@ describe('the rubicon adapter', () => { expect(bids).to.be.lengthOf(2); expect(bids[0].getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bids[0].bidderCode).to.equal("rubicon"); + expect(bids[0].bidderCode).to.equal('rubicon'); expect(bids[0].width).to.equal(320); expect(bids[0].height).to.equal(50); expect(bids[0].cpm).to.equal(0.911); expect(bids[0].ad).to.contain(`alert('foo')`) .and.to.contain(``) .and.to.contain(`
`); + expect(bids[0].rubiconTargeting.rpfl_elemid).to.equal('/19968336/header-bid-tag-0'); + expect(bids[0].rubiconTargeting.rpfl_14062).to.equal('43_tier_all_test'); expect(bids[1].getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bids[1].bidderCode).to.equal("rubicon"); + expect(bids[1].bidderCode).to.equal('rubicon'); expect(bids[1].width).to.equal(300); expect(bids[1].height).to.equal(250); expect(bids[1].cpm).to.equal(0.811); expect(bids[1].ad).to.contain(`alert('foo')`) .and.to.contain(``) .and.to.contain(`
`); + expect(bids[1].rubiconTargeting.rpfl_elemid).to.equal('/19968336/header-bid-tag-0'); + expect(bids[1].rubiconTargeting.rpfl_14062).to.equal('15_tier_all_test'); }); it('should be fine with a CPM of 0', () => { server.respondWith(JSON.stringify({ - "status": "ok", - "account_id": 14062, - "site_id": 70608, - "zone_id": 530022, - "size_id": 15, - "alt_size_ids": [ + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ 43 ], - "tracking": "", - "inventory": {}, - "ads": [{ - "status": "ok", - "cpm": 0, - "size_id": 15 + 'tracking': '', + 'inventory': {}, + 'ads': [{ + 'status': 'ok', + 'cpm': 0, + 'size_id': 15 }] })); @@ -509,17 +527,17 @@ describe('the rubicon adapter', () => { it('should handle an error with no ads returned', () => { server.respondWith(JSON.stringify({ - "status": "ok", - "account_id": 14062, - "site_id": 70608, - "zone_id": 530022, - "size_id": 15, - "alt_size_ids": [ + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ 43 ], - "tracking": "", - "inventory": {}, - "ads": [] + 'tracking': '', + 'inventory': {}, + 'ads': [] })); rubiconAdapter.callBids(bidderRequest); @@ -533,18 +551,18 @@ describe('the rubicon adapter', () => { it('should handle an error with bad status', () => { server.respondWith(JSON.stringify({ - "status": "ok", - "account_id": 14062, - "site_id": 70608, - "zone_id": 530022, - "size_id": 15, - "alt_size_ids": [ + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ 43 ], - "tracking": "", - "inventory": {}, - "ads": [{ - "status": "not_ok", + 'tracking': '', + 'inventory': {}, + 'ads': [{ + 'status': 'not_ok', }] })); @@ -558,7 +576,19 @@ describe('the rubicon adapter', () => { }); it('should handle an error because of malformed json response', () => { - server.respondWith("{test{"); + server.respondWith('{test{'); + + rubiconAdapter.callBids(bidderRequest); + + server.respond(); + + expect(bidManager.addBidResponse.calledOnce).to.equal(true); + expect(bids).to.be.lengthOf(1); + expect(bids[0].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); + }); + + it('should handle error contacting endpoint', () => { + server.respondWith([404, {}, ""]); rubiconAdapter.callBids(bidderRequest); @@ -572,25 +602,25 @@ describe('the rubicon adapter', () => { it('should not register an error bid when a success call to addBidResponse throws an error', () => { server.respondWith(JSON.stringify({ - "status": "ok", - "account_id": 14062, - "site_id": 70608, - "zone_id": 530022, - "size_id": 15, - "alt_size_ids": [ + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ 43 ], - "tracking": "", - "inventory": {}, - "ads": [{ - "status": "ok", - "cpm": .8, - "size_id": 15 + 'tracking': '', + 'inventory': {}, + 'ads': [{ + 'status': 'ok', + 'cpm': .8, + 'size_id': 15 }] })); addBidResponseAction = function() { - throw new Error("test error"); + throw new Error('test error'); }; rubiconAdapter.callBids(bidderRequest); @@ -614,29 +644,29 @@ describe('the rubicon adapter', () => { it('should register a successful bid', () => { server.respondWith(JSON.stringify({ - "status": "ok", - "ads": { - "/19968336/header-bid-tag-0": [ + 'status': 'ok', + 'ads': { + '/19968336/header-bid-tag-0': [ { - "status": "ok", - "cpm": 1, - "tier": "tier0200", - "targeting": { - "rpfl_8000": "201_tier0200", - "rpfl_elemid": "/19968336/header-bid-tag-0" + 'status': 'ok', + 'cpm': 1, + 'tier': 'tier0200', + 'targeting': { + 'rpfl_8000': '201_tier0200', + 'rpfl_elemid': '/19968336/header-bid-tag-0' }, - "impression_id": "a40fe16e-d08d-46a9-869d-2e1573599e0c", - "site_id": 88888, - "zone_id": 54321, - "creative_type": "video", - "creative_depot_url": "https://optimized-by-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml", - "ad_id": 999999, - "size_id": 201, - "advertiser": 12345 + 'impression_id': 'a40fe16e-d08d-46a9-869d-2e1573599e0c', + 'site_id': 88888, + 'zone_id': 54321, + 'creative_type': 'video', + 'creative_depot_url': 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml', + 'ad_id': 999999, + 'size_id': 201, + 'advertiser': 12345 } ] }, - "account_id": 7780 + 'account_id': 7780 })); rubiconAdapter.callBids(bidderRequest); @@ -654,7 +684,7 @@ describe('the rubicon adapter', () => { expect(bids[0].cpm).to.equal(1); expect(bids[0].descriptionUrl).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); expect(bids[0].vastUrl).to.equal( - 'https://optimized-by-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml' + 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml' ); expect(bids[0].impression_id).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); diff --git a/test/spec/adapters/serverbid_spec.js b/test/spec/adapters/serverbid_spec.js new file mode 100644 index 00000000000..3a7408c34e5 --- /dev/null +++ b/test/spec/adapters/serverbid_spec.js @@ -0,0 +1,178 @@ +/* jshint -W024 */ +/* jshint expr:true */ + +import { expect } from 'chai'; +import Adapter from 'src/adapters/serverbid'; +import bidmanager from 'src/bidmanager'; +import * as utils from 'src/utils'; + +const ENDPOINT = '//e.serverbid.com/api/v2'; + +const REQUEST = { + "bidderCode": "serverbid", + "requestId": "a4713c32-3762-4798-b342-4ab810ca770d", + "bidderRequestId": "109f2a181342a9", + "bids": [{ + "bidder": "serverbid", + "params": { + "networkId": 9969, + "siteId": 730181 + }, + "placementCode": "div-gpt-ad-1487778092495-0", + "sizes": [ + [728, 90], + [970, 90] + ], + "bidId": "2b0f82502298c9", + "bidderRequestId": "109f2a181342a9", + "requestId": "a4713c32-3762-4798-b342-4ab810ca770d" + }], + "start": 1487883186070, + "auctionStart": 1487883186069, + "timeout": 3000 +}; + +const RESPONSE = { + "user": { "key": "ue1-2d33e91b71e74929b4aeecc23f4376f1" }, + "decisions": { + "2b0f82502298c9": { + "adId": 2364764, + "creativeId": 1950991, + "flightId": 2788300, + "campaignId": 542982, + "clickUrl": "http://e.serverbid.com/r", + "impressionUrl": "http://e.serverbid.com/i.gif", + "contents": [{ + "type": "html", + "body": "", + "data": { + "height": 90, + "width": 728, + "imageUrl": "http://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} + }, + } +}; + +describe('serverbidAdapter', () => { + + let adapter; + + beforeEach(() => adapter = Adapter.createNew()); + + describe('request function', () => { + + let xhr; + let requests; + let pbConfig; + + + beforeEach(() => { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + pbConfig = REQUEST; + //just a single slot + pbConfig.bids = [pbConfig.bids[0]]; + }); + + afterEach(() => xhr.restore()); + + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + + it('requires paramaters to make request', () => { + adapter.callBids({}); + expect(requests).to.be.empty; + }); + + it('requires networkId and siteId', () => { + let backup = pbConfig.bids[0].params; + pbConfig.bids[0].params = { networkId: 1234 }; //no hbid + adapter.callBids(pbConfig); + expect(requests).to.be.empty; + + pbConfig.bids[0].params = { siteId: 1234 }; //no placementid + adapter.callBids(pbConfig); + expect(requests).to.be.empty; + + pbConfig.bids[0].params = backup; + }); + + it('sends bid request to ENDPOINT via POST', () => { + adapter.callBids(pbConfig); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].method).to.equal('POST'); + }); + }); + + describe('response handler', () => { + + let server; + + beforeEach(() => { + server = sinon.fakeServer.create(); + sinon.stub(bidmanager, 'addBidResponse'); + sinon.stub(utils, "getBidRequest").returns(REQUEST); + }); + + afterEach(() => { + server.restore(); + bidmanager.addBidResponse.restore(); + utils.getBidRequest.restore(); + }); + + it('registers bids', () => { + server.respondWith(JSON.stringify(RESPONSE)); + + adapter.callBids(REQUEST); + server.respond(); + sinon.assert.calledOnce(bidmanager.addBidResponse); + + const response = bidmanager.addBidResponse.firstCall.args[1]; + expect(response).to.have.property('statusMessage', 'Bid available'); + expect(response).to.have.property('cpm'); + expect(response.cpm).to.be.above(0); + }); + + it('handles nobid responses', () => { + server.respondWith(JSON.stringify({ + "decisions": [] + })); + + adapter.callBids(REQUEST); + server.respond(); + sinon.assert.calledOnce(bidmanager.addBidResponse); + + const response = bidmanager.addBidResponse.firstCall.args[1]; + expect(response).to.have.property( + 'statusMessage', + 'Bid returned empty or error response' + ); + }); + + it('handles JSON.parse errors', () => { + server.respondWith(''); + + adapter.callBids(REQUEST); + server.respond(); + sinon.assert.calledOnce(bidmanager.addBidResponse); + + const response = bidmanager.addBidResponse.firstCall.args[1]; + expect(response).to.have.property( + 'statusMessage', + 'Bid returned empty or error response' + ); + }); + + }); + +}); \ No newline at end of file diff --git a/test/spec/adapters/sharethrough_spec.js b/test/spec/adapters/sharethrough_spec.js index 0678690370e..7ede215d9aa 100644 --- a/test/spec/adapters/sharethrough_spec.js +++ b/test/spec/adapters/sharethrough_spec.js @@ -51,29 +51,20 @@ describe('sharethrough adapter', () => { let secondBidUrl; beforeEach(() => { - sandbox.spy(adapter.str, 'loadIFrame'); + sandbox.spy(adapter.str, 'ajax'); }); - it('should call loadIFrame on the adloader for each bid', () => { + it('should call ajax to make a request for each bid', () => { adapter.callBids(bidderRequest); - firstBidUrl = adapter.str.loadIFrame.firstCall.args[0]; - secondBidUrl = adapter.str.loadIFrame.secondCall.args[0]; + firstBidUrl = adapter.str.ajax.firstCall.args[0]; + secondBidUrl = adapter.str.ajax.secondCall.args[0]; - sinon.assert.calledTwice(adapter.str.loadIFrame); + sinon.assert.calledTwice(adapter.str.ajax); - expect(firstBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId1&placement_key=aaaa1111&ijson=pbjs.strcallback&hbVersion=%24prebid.version%24&strVersion=0.1.0&hbSource=prebid&'); - expect(secondBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId2&placement_key=bbbb2222&ijson=pbjs.strcallback&hbVersion=%24prebid.version%24&strVersion=0.1.0&hbSource=prebid&'); - }); - - }); - - describe('strcallback', () => { - - it('should exist and be a function', () => { - let shit = sandbox.stub(pbjs, 'strcallback'); - expect(pbjs.strcallback).to.exist.and.to.be.a('function'); + expect(firstBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId1&placement_key=aaaa1111&hbVersion=%24prebid.version%24&strVersion=1.2.0&hbSource=prebid&'); + expect(secondBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId2&placement_key=bbbb2222&hbVersion=%24prebid.version%24&strVersion=1.2.0&hbSource=prebid&'); }); }); @@ -117,8 +108,8 @@ describe('sharethrough adapter', () => { "stxUserId": "" }; - pbjs.strcallback(bidderReponse1); - pbjs.strcallback(bidderReponse2); + adapter.callback(bidderRequest.bids[0], JSON.stringify(bidderReponse1)); + adapter.callback(bidderRequest.bids[1], JSON.stringify(bidderReponse2)); firstBid = bidManager.addBidResponse.firstCall.args[1]; secondBid = bidManager.addBidResponse.secondCall.args[1]; diff --git a/test/spec/adapters/smartadserver_spec.js b/test/spec/adapters/smartadserver_spec.js index d5989d679b1..f48f10d9cb5 100644 --- a/test/spec/adapters/smartadserver_spec.js +++ b/test/spec/adapters/smartadserver_spec.js @@ -18,7 +18,8 @@ describe("smartadserver adapter tests", function () { siteId: "1234", pageId: "5678", formatId: "90", - target: "test=prebid" + target: "test=prebid", + currency: "EUR" }, requestId: "efgh5678", placementCode: "sas_42" @@ -26,6 +27,24 @@ describe("smartadserver adapter tests", function () { ] }; + var DEFAULT_PARAMS_WO_OPTIONAL = { + bidderCode: "smartadserver", + bids: [{ + bidId: "abcd1234", + sizes: [[300, 250], [300, 200]], + bidder: "smartadserver", + params: { + domain: "http://www.smartadserver.com", + siteId: "1234", + pageId: "5678", + formatId: "90" + }, + requestId: "efgh5678", + placementCode: "sas_42" + } + ] + }; + var BID_RESPONSE = { cpm: 0.42, ad: "fake ad content", @@ -58,6 +77,7 @@ describe("smartadserver adapter tests", function () { expect(parsedBidUrlQueryString).to.have.property("pgid").and.to.equal("5678"); expect(parsedBidUrlQueryString).to.have.property("fmtid").and.to.equal("90"); expect(parsedBidUrlQueryString).to.have.property("tgt").and.to.equal("test=prebid"); + expect(parsedBidUrlQueryString).to.have.property("ccy").and.to.equal("EUR"); expect(parsedBidUrlQueryString).to.have.property("tag").and.to.equal("sas_42"); expect(parsedBidUrlQueryString).to.have.property("sizes").and.to.equal("300x250,300x200"); expect(parsedBidUrlQueryString).to.have.property("async").and.to.equal("1"); @@ -65,6 +85,21 @@ describe("smartadserver adapter tests", function () { stubLoadScript.restore(); }); + it("test optional parameters default value", function () { + var stubLoadScript = sinon.stub(adLoader, "loadScript"); + + adapter().callBids(DEFAULT_PARAMS_WO_OPTIONAL); + + var bidUrl = stubLoadScript.getCall(0).args[0]; + var parsedBidUrl = urlParse(bidUrl); + var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); + + expect(parsedBidUrlQueryString).to.have.property("tgt").and.to.equal(""); + expect(parsedBidUrlQueryString).to.have.property("ccy").and.to.equal("USD"); + + stubLoadScript.restore(); + }); + it("creates an empty bid response if no bids", function() { var stubLoadScript = sinon.stub(adLoader, "loadScript", function(url) { var bidUrl = stubLoadScript.getCall(0).args[0]; diff --git a/test/spec/adapters/stickyadstv_spec.js b/test/spec/adapters/stickyadstv_spec.js index eb665ec658c..b485416f614 100644 --- a/test/spec/adapters/stickyadstv_spec.js +++ b/test/spec/adapters/stickyadstv_spec.js @@ -157,7 +157,6 @@ describe('StickyAdsTV Adapter', function () { expect(result).to.have.property('cpm', '1.2345'); expect(result).to.have.property('ad', "
sample
"); - expect(result).to.have.property('bidderCode', "stickyadstv"); expect(result).to.have.property('currencyCode', "EUR"); expect(result).to.have.property('width', 200); expect(result).to.have.property('height', 300); @@ -167,14 +166,12 @@ describe('StickyAdsTV Adapter', function () { it('should create a invalid bid object because price is not defined', function () { let result = adapter.formatBidObject("", true, null, "
sample
", 200, 300); - expect(result).to.have.property('bidderCode', "stickyadstv"); expect(result.getStatusCode()).to.equal(2); }); it('should create a invalid bid object', function () { let result = adapter.formatBidObject("", false, {currency:"EUR",price:"1.2345"}, "
sample
", 200, 300); - expect(result).to.have.property('bidderCode', "stickyadstv"); expect(result.getStatusCode()).to.equal(2); }); }); diff --git a/test/spec/adapters/tapsense_spec.js b/test/spec/adapters/tapsense_spec.js new file mode 100644 index 00000000000..72d189ef81e --- /dev/null +++ b/test/spec/adapters/tapsense_spec.js @@ -0,0 +1,260 @@ +import { expect } from 'chai'; +import Adapter from 'src/adapters/tapsense'; +import bidmanager from 'src/bidmanager'; +import adloader from "src/adloader"; +import * as utils from "src/utils"; + +window.pbjs = window.pbjs || {}; + +const DEFAULT_BIDDER_REQUEST = { + "bidderCode": "tapsense", + "bidderRequestId": "141ed07a281ca3", + "requestId": "b202e550-b0f7-4fb9-bfb4-1aa80f1795b4", + "start": new Date().getTime(), + "bids": [ + { + "sizes": undefined, //set values in tests + "bidder": "tapsense", + "bidId": "2b211418dd0575", + "bidderRequestId": "141ed07a281ca3", + "placementCode": "thisisatest", + "params": { + "ufid": "thisisaufid", + "refer": "thisisarefer", + "version": "0.0.1", + "ad_unit_id": "thisisanadunitid", + "device_id": "thisisadeviceid", + "lat": "thisislat", + "long": "thisisalong", + "user": "thisisanidfa", + "price_floor": 0.01 + } + } + ] +} + +const SUCCESSFUL_RESPONSE = { + "count_ad_units": 1, + "status": { + "value": "ok", + }, + "ad_units": [ + { + html: "", + imp_url: "https://i.tapsense.com" + } + ], + "id": "thisisanid", + "width": 320, + "height": 50, + "time": new Date().getTime() +} + +const UNSUCCESSFUL_RESPONSE = { + "count_ad_units": 0, + "status": { + "value": "nofill" //will be set in test + }, + "time": new Date().getTime() +} + +function duplicate(obj) { + return JSON.parse(JSON.stringify(obj)); +} + +function makeSuccessfulRequest(adapter){ + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + modifiedReq.bids[0].sizes = [[320,50], [500,500]]; + adapter.callBids(modifiedReq); + return modifiedReq.bids; +} + +describe ("TapSenseAdapter", () => { + let adapter, sandbox; + beforeEach(() => { + adapter = new Adapter; + sandbox = sinon.sandbox.create(); + }); + afterEach(() => { + sandbox.restore(); + }) + + describe('request function', () => { + beforeEach(() => { + sandbox.stub(adloader, 'loadScript'); + }); + afterEach(() => { + sandbox.restore(); + }); + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + it('requires parameters to make request', () => { + adapter.callBids({}); + sinon.assert.notCalled(adloader.loadScript); + }); + it('does not make a request if missing user', () => { + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + delete modifiedReq.bids.user + adapter.callBids(modifiedReq); + sinon.assert.notCalled(adloader.loadScript); + }); + it('does not make a request if missing ad_unit_id', () => { + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + delete modifiedReq.bids.ad_unit_id + adapter.callBids(modifiedReq); + sinon.assert.notCalled(adloader.loadScript); + }); + it('does not make a request if ad sizes are incorrect', () => { + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + modifiedReq.bids[0].sizes = [[500,500]]; + adapter.callBids(modifiedReq); + sinon.assert.notCalled(adloader.loadScript); + }); + it('does not make a request if ad sizes are invalid format', () => { + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + modifiedReq.bids[0].sizes = 1234; + adapter.callBids(modifiedReq); + sinon.assert.notCalled(adloader.loadScript); + }); + + describe("requesting an ad", () => { + afterEach(() => { + sandbox.restore(); + }) + it("makes a request if valid sizes are provided (nested array)", () => { + makeSuccessfulRequest(adapter); + sinon.assert.calledOnce(adloader.loadScript); + expect(adloader.loadScript.firstCall.args[0]).to.contain( + "ads04.tapsense.com" + ); + }); + it("handles a singles array for size parameter", () => { + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + modifiedReq.bids[0].sizes = [320,50]; + adapter.callBids(modifiedReq); + expect(adloader.loadScript.firstCall.args[0]).to.contain( + "ads04.tapsense.com" + ); + }); + it("handles a string for size parameter", () => { + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + modifiedReq.bids[0].sizes = "320x50"; + adapter.callBids(modifiedReq); + expect(adloader.loadScript.firstCall.args[0]).to.contain( + "ads04.tapsense.com" + ); + }); + it("handles a string with multiple sizes for size parameter", () => { + let modifiedReq = duplicate(DEFAULT_BIDDER_REQUEST); + modifiedReq.bids[0].sizes = "320x50,500x500"; + adapter.callBids(modifiedReq); + expect(adloader.loadScript.firstCall.args[0]).to.contain( + "ads04.tapsense.com" + ); + }); + it("appends bid params as a query string when requesting ad", () => { + makeSuccessfulRequest(adapter); + sinon.assert.calledOnce(adloader.loadScript); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /ufid=thisisaufid&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /refer=thisisarefer&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /version=[^&]+&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /jsonp=1&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /ad_unit_id=thisisanadunitid&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /device_id=thisisadeviceid&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /lat=thisislat&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /long=thisisalong&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /user=thisisanidfa&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /price_floor=0\.01&/ + ); + expect(adloader.loadScript.firstCall.args[0]).to.match( + /callback=pbjs\.tapsense\.callback_with_price_.+&/ + ); + }) + }) + }); + + describe("generateCallback", () => { + beforeEach(() => { + sandbox.stub(adloader, 'loadScript'); + }); + afterEach(() => { + sandbox.restore(); + }); + it("generates callback in namespaced object with correct bidder id", () => { + makeSuccessfulRequest(adapter); + expect(pbjs.tapsense.callback_with_price_2b211418dd0575).to.exist.and.to.be.a('function'); + }) + }); + + describe("response", () => { + beforeEach(() => { + sandbox.stub(bidmanager, 'addBidResponse'); + sandbox.stub(adloader, 'loadScript'); + let bids = makeSuccessfulRequest(adapter); + sandbox.stub(utils, "getBidRequest", (id) => { + return bids.find((item) => { return item.bidId === id}); + }) + }); + afterEach(() => { + sandbox.restore(); + }); + describe("successful response", () => { + beforeEach(() => { + pbjs.tapsense.callback_with_price_2b211418dd0575(SUCCESSFUL_RESPONSE, 1.2); + }); + it("called the bidmanager and registers a bid", () => { + sinon.assert.calledOnce(bidmanager.addBidResponse); + expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(1); + }); + it("should have the correct placementCode", () => { + sinon.assert.calledOnce(bidmanager.addBidResponse); + expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal("thisisatest"); + }); + }); + describe("unsuccessful response", () => { + beforeEach(() => { + pbjs.tapsense.callback_with_price_2b211418dd0575(UNSUCCESSFUL_RESPONSE, 1.2); + }) + it("should call the bidmanger and register an invalid bid", () => { + sinon.assert.calledOnce(bidmanager.addBidResponse); + expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); + }); + it("should have the correct placementCode", () => { + expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal("thisisatest"); + }) + }); + describe("no response/timeout", () => { + it("should not register any bids", () => { + sinon.assert.notCalled(bidmanager.addBidResponse); + }) + }); + describe("edge cases", () => { + it("does not register a bid if no price is supplied", () => { + sandbox.stub(utils, "logMessage"); + pbjs.tapsense.callback_with_price_2b211418dd0575(SUCCESSFUL_RESPONSE); + sinon.assert.notCalled(bidmanager.addBidResponse); + }); + }); + }); + +}) diff --git a/test/spec/adapters/thoughtleadr_spec.js b/test/spec/adapters/thoughtleadr_spec.js new file mode 100644 index 00000000000..67be54f3aef --- /dev/null +++ b/test/spec/adapters/thoughtleadr_spec.js @@ -0,0 +1,176 @@ +"use strict"; +var chai_1 = require("chai"); +var ta = require("../../../src/adapters/thoughtleadr"); +var adloader = require("../../../src/adloader"); +var bidfactory = require("../../../src/bidfactory"); +var Adapter = ta; + +function setupResponse(resp) { + var stub = sinon.stub(); + window.tldr = { + requestPrebid: stub.returns({ + then: function (cb) { + return cb(resp); + } + }) + }; + return stub; +} + +describe("thoughtleadr adapter tests", function () { + var sandbox; + var adapter; + var request; + var loadScript; + var createBid; + var tldrRequestPrebid; + + before(function () { + sandbox = sinon.sandbox.create(); + }); + + beforeEach(function () { + loadScript = sandbox.stub(adloader, "loadScript"); + createBid = sandbox.spy(bidfactory, "createBid"); + adapter = new Adapter(); + loadScript.reset(); + request = { + bidderCode: "thoughtleadr", + bids: [{ + bidder: "thoughtleadr", + placementCode: "abc-123", + sizes: [[300, 250], [400, 400]], + params: { + placementId: "test-placement" + } + }] + }; + }); + + afterEach(function () { + adapter.stopListen(); + delete window.tldr; + sandbox.restore(); + }); + + describe("callBids", function () { + + beforeEach(function () { + tldrRequestPrebid = setupResponse({}); + }); + + it("should request page.js from cdn if there wasn't before", function () { + delete window.tldr; + adapter.callBids(request); + chai_1.expect(loadScript.getCall(0).args[0]).to.be.equal("//cdn.thoughtleadr.com/v4/page.js"); + }); + + it("should use window.tldr.config.root_url", function () { + window.tldr = { + config: { + root_url: "http://example.loc/" + } + }; + adapter.callBids(request); + chai_1.expect(loadScript.getCall(0).args[0]).to.be.equal("http://example.loc/page.js"); + }); + + it("should not request page.js if api is present", function () { + adapter.callBids(request); + chai_1.expect(loadScript.notCalled).to.be.ok; + }); + }); + + describe("handleBids", function () { + + beforeEach(function () { + tldrRequestPrebid = setupResponse({}); + }); + + it("should filter invalid bids", function () { + request.bids.unshift({ + bidder: "thoughtleadr", + placementCode: "abc-123", + sizes: [[300, 250], [400, 400]], + params: {} + }); + request.bids.push({ + bidder: "thoughtleadr", + placementCode: "abc-123", + sizes: [[300, 250], [400, 400]], + params: { + incorrectParam: 123 + } + }); + var requestPlacement = sinon.spy(adapter, "requestPlacement"); + adapter.callBids(request); + chai_1.expect(requestPlacement.callCount).to.be.equal(1); + chai_1.expect(requestPlacement.getCall(0).args[0]).to.be.equal(request.bids[1]); + }); + }); + + describe("requestPlacement", function () { + + beforeEach(function () { + tldrRequestPrebid = setupResponse({ + config: { + abc: 567 + }, + bid: { + code: 1, + cpm: 12, + ad: "asd" + } + }); + }); + + it("should made request through page.js api", function () { + adapter.callBids(request); + chai_1.expect(tldrRequestPrebid.callCount).to.be.equal(1); + chai_1.expect(tldrRequestPrebid.firstCall.args[0]).to.be.equal(request.bids[0].params.placementId); + chai_1.expect(tldrRequestPrebid.firstCall.args[1]).to.be.length(36); + }); + + it("should call bidfactory.createBid with code 1 if ad is ok", function () { + var bid = request.bids[0]; + adapter.requestPlacement(bid); + chai_1.expect(createBid.callCount).to.be.equal(1); + chai_1.expect(createBid.firstCall.args[0]).to.be.equal(1); + }); + + it("should call bidfactory.createBid with different code if ad isn't ok", function () { + var bid = request.bids[0]; + tldrRequestPrebid = setupResponse({ + config: {}, + bid: { + code: 2 + } + }); + adapter.requestPlacement(bid); + chai_1.expect(createBid.callCount).to.be.equal(1); + chai_1.expect(createBid.firstCall.args[0]).to.be.equal(2); + }); + + it.skip("should response on the postMessage request", function (done) { + var bid = request.bids[0]; + adapter.requestPlacement(bid); + var rid = tldrRequestPrebid.firstCall.args[1]; + chai_1.expect(rid).to.be.ok; + + window.addEventListener("message", function (ev) { + if (ev.data && ev.data.TLDR_RESPONSE) { + chai_1.expect(ev.data.TLDR_RESPONSE.rid).to.be.equal(rid); + chai_1.expect(JSON.stringify(ev.data.TLDR_RESPONSE.config)).to.be.equal('{"abc":567}'); + done(); + } + else if (ev.data && ev.data.TLDR_REQUEST) { + chai_1.expect(ev.data.TLDR_REQUEST.rid).to.be.equal(rid); + } + else { + throw new Error("should not be any other messages"); + } + }, false); + window.postMessage({TLDR_REQUEST: {rid: rid}}, "*"); + }); + }); +}); diff --git a/test/spec/adapters/underdogmedia_spec.js b/test/spec/adapters/underdogmedia_spec.js index a1f31740d4e..3c8639fd56a 100644 --- a/test/spec/adapters/underdogmedia_spec.js +++ b/test/spec/adapters/underdogmedia_spec.js @@ -2,96 +2,76 @@ import Adapter from '../../../src/adapters/underdogmedia'; import bidManager from '../../../src/bidmanager'; -import {expect} from 'chai'; +import adloader from '../../../src/adloader'; -describe('underdog media adapter test', () => { +import { + expect +} from 'chai'; + +describe('underdogmedia adapter test', () => { let adapter; let server; - // Minimal stub implementation of underdog media header bid API - // This will prevent the need to load underdog's static library, and to make requests to underdog's server - window.udm_header_lib = { - - BidRequest: function(options){ - return { - send: function(){ - var siteId = options.siteId; - if(siteId == 10272){ - // Only bid on this particular site id - var bids = []; - options.sizes.forEach(function(size){ - bids.push({ - cpm: 3.14, - ad_html: `Ad HTML for site ID ${siteId} size ${size[0]}x${size[1]}`, - width: size[0], - height: size[1] - }); - }); - options.callback({ - bids: bids - }); - } else { - options.callback({ - bids: [] - }); - } + // The third bid here is an invalid site id and should return a 'no-bid'. + var bidderRequest = { + bidderCode: 'underdogmedia', + bids: [{ + bidder: 'underdogmedia', + adUnitCode: 'foo', + sizes: [ + [728, 90] + ], + params: { + siteId: '10272' + } + }, + { + bidder: 'underdogmedia', + adUnitCode: 'bar', + sizes: [ + [300, 250] + ], + params: { + siteId: '10272', + subId: 'TEST_SUBID' } - }; - }, - - BidRequestArray: function(arr){ - return { - send: function(){ - arr.forEach(function(bidRequest){ - var req = new window.udm_header_lib.BidRequest(bidRequest); - req.send(); - }); + }, + { + bidder: 'underdogmedia', + adUnitCode: 'nothing', + sizes: [160, 600], + params: { + siteId: '31337' } - }; - } + } + ] + }; + var response = { + "mids": [{ + "width": 728, + "notification_url": "//udmserve.net/notification_url", + "height": 90, + "cpm": 2.5, + "ad_code_html": "Ad HTML for site ID 10272 size 728x90" + }, + { + "width": 300, + "notification_url": "//udmserve.net/notification_url", + "height": 250, + "cpm": 2.0, + "ad_code_html": "Ad HTML for site ID 10272 size 300x250" + } + ] }; - // The third bid here is an invalid site id and should return a 'no-bid'. - function request() { - adapter.callBids({ - bidderCode: 'underdogmedia', - bids: [ - { - bidder: 'underdogmedia', - adUnitCode: 'foo', - sizes: [[728, 90]], - params: { - siteId: '10272' - } - }, - { - bidder: 'underdogmedia', - adUnitCode: 'bar', - sizes: [[300, 250]], - params: { - siteId: '10272' - } - }, - { - bidder: 'underdogmedia', - adUnitCode: 'nothing', - sizes: [[160, 600]], - params: { - siteId: '31337' - } - } - ] - }); - } beforeEach(() => { adapter = new Adapter(); }); - afterEach(() => { - }); + afterEach(() => {}); describe('adding bids to the manager', () => { @@ -101,7 +81,10 @@ describe('underdog media adapter test', () => { beforeEach(() => { sinon.stub(bidManager, 'addBidResponse'); - request(); + sinon.stub(adloader, 'loadScript'); + + adapter.callBids(bidderRequest); + $$PREBID_GLOBAL$$.handleUnderdogMediaCB(JSON.parse(JSON.stringify(response))); firstBid = bidManager.addBidResponse.firstCall.args[1]; secondBid = bidManager.addBidResponse.secondCall.args[1]; thirdBid = bidManager.addBidResponse.thirdCall.args[1]; @@ -109,6 +92,7 @@ describe('underdog media adapter test', () => { afterEach(() => { bidManager.addBidResponse.restore(); + adloader.loadScript.restore(); }); it('will add a bid object for each bid', () => { @@ -116,12 +100,14 @@ describe('underdog media adapter test', () => { }); it('will add the ad html to the bid object', () => { - expect(firstBid).to.have.property('ad', 'Ad HTML for site ID 10272 size 728x90'); - expect(secondBid).to.have.property('ad', 'Ad HTML for site ID 10272 size 300x250'); + + expect(firstBid).to.have.property('ad').includes('Ad HTML for site ID 10272 size 728x90'); + expect(secondBid).to.have.property('ad').includes('Ad HTML for site ID 10272 size 300x250').and.includes('TEST_SUBID'); expect(thirdBid).to.not.have.property('ad'); }); it('will have the right size attached', () => { + expect(firstBid).to.have.property('width', 728); expect(firstBid).to.have.property('height', 90); expect(secondBid).to.have.property('width', 300); @@ -129,8 +115,8 @@ describe('underdog media adapter test', () => { }); it('will add the CPM to the bid object', () => { - expect(firstBid).to.have.property('cpm', 3.14); - expect(secondBid).to.have.property('cpm', 3.14); + expect(firstBid).to.have.property('cpm', 2.5); + expect(secondBid).to.have.property('cpm', 2.0); expect(thirdBid).to.not.have.property('cpm'); }); diff --git a/test/spec/adapters/wideorbit_spec.js b/test/spec/adapters/wideorbit_spec.js index 36e13066c59..6f02ccbe316 100644 --- a/test/spec/adapters/wideorbit_spec.js +++ b/test/spec/adapters/wideorbit_spec.js @@ -65,7 +65,8 @@ describe('wideorbit adapter tests', function () { bidder: 'wideorbit', params: { PBiD: 1, - PID: 101 + PID: 101, + ReferRer: 'http://www.foo.com?param1=param1¶m2=param2' }, placementCode: 'div-gpt-ad-12345-1' }, @@ -106,6 +107,7 @@ describe('wideorbit adapter tests', function () { expect(parsedBidUrlQueryString).to.have.property('mpp').and.to.equal('0'); expect(parsedBidUrlQueryString).to.have.property('cb').to.have.length.above(0); expect(parsedBidUrlQueryString).to.have.property('hb').and.to.equal('1'); + expect(parsedBidUrlQueryString).to.have.property('url').and.to.equal('http://www.foo.com?param1=param1¶m2=param2'); expect(parsedBidUrlQueryString).to.have.property('gid0').and.to.equal('div-gpt-ad-12345-1'); expect(parsedBidUrlQueryString).to.have.property('rpos0').and.to.equal('0'); @@ -184,6 +186,7 @@ describe('wideorbit adapter tests', function () { expect(parsedBidUrlQueryString).to.have.property('mpp').and.to.equal('0'); expect(parsedBidUrlQueryString).to.have.property('cb').to.have.length.above(0); expect(parsedBidUrlQueryString).to.have.property('hb').and.to.equal('1'); + expect(parsedBidUrlQueryString).to.have.property('url').and.to.be.empty; expect(parsedBidUrlQueryString).to.have.property('gid0').and.to.equal('div-gpt-ad-12345-1'); expect(parsedBidUrlQueryString).to.have.property('rpos0').and.to.equal('1001'); @@ -260,6 +263,7 @@ describe('wideorbit adapter tests', function () { expect(parsedBidUrlQueryString).to.have.property('mpp').and.to.equal('0'); expect(parsedBidUrlQueryString).to.have.property('cb').to.have.length.above(0); expect(parsedBidUrlQueryString).to.have.property('hb').and.to.equal('1'); + expect(parsedBidUrlQueryString).to.have.property('url').and.to.be.empty; expect(parsedBidUrlQueryString).to.have.property('gid0').and.to.equal('div-gpt-ad-12345-1'); expect(parsedBidUrlQueryString).to.have.property('rpos0').and.to.equal('1001'); @@ -461,14 +465,14 @@ describe('wideorbit adapter tests', function () { }); - it('should append an script to the head when type is set to javascript', function () { + it('should append an script to the head when type is set to js', function () { var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); var response = { UserMatchings: [ { - Type: 'javascript', + Type: 'js', Url: 'http%3A%2F%2Fwww.admeta.com%2F1.js' } ], diff --git a/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js b/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js index 52a3045e5c6..f4fa7e754cf 100644 --- a/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js +++ b/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js @@ -4,9 +4,9 @@ var util = require('../../common/utils.js'); module.exports = { 'adequant ad rendering' : function (browser) { browser - .url('http://localhost:9999/test/spec/e2e/gpt-examples/all_bidders_instant_load.html') + .url('http://an.localhost:9999/test/spec/e2e/gpt-examples/all_bidders_instant_load.html') .waitForElementVisible('body', 5000) - .pause(10000) + .pause(7000) .execute(util.findIframeInDiv, ['div-1'], function(result) { this.verify.equal(result.value, true, 'adequant ad not rendered'); }); diff --git a/test/spec/e2e/testcase1/dom-group/dom_spec.js b/test/spec/e2e/testcase1/dom-group/dom_spec.js index 54ff7f05eac..3ed5252bf26 100644 --- a/test/spec/e2e/testcase1/dom-group/dom_spec.js +++ b/test/spec/e2e/testcase1/dom-group/dom_spec.js @@ -19,9 +19,9 @@ module.exports = { } browser - .url('http://localhost:9999/test/spec/e2e/gpt-examples/e2e_default.html') + .url('http://an.localhost:9999/test/spec/e2e/gpt-examples/e2e_default.html') .waitForElementVisible('body', 3000) - .pause(5000) + .pause(3000) .execute(checkAdRendering2, [], function(result) { this.assert.equal(result.value, true, 'Ad of div-2 not rendered'); }); diff --git a/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js b/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js index f71838d0654..67d0306d438 100644 --- a/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js +++ b/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js @@ -6,7 +6,7 @@ module.exports = { browser .url('http://localhost:9999/test/spec/e2e/gpt-examples/gpt_default.html') .waitForElementVisible('body', 3000) - .pause(5000) + .pause(3000) .execute(function(){ if(typeof window.pbjs.bidderSettings == "undefined") { diff --git a/test/spec/integration/bug-825-truncate-bidsRequested_spec.js b/test/spec/integration/bug-825-truncate-bidsRequested_spec.js deleted file mode 100644 index 4475e746b78..00000000000 --- a/test/spec/integration/bug-825-truncate-bidsRequested_spec.js +++ /dev/null @@ -1,68 +0,0 @@ -import { equal, notEqual } from 'assert'; - -import faker from 'faker'; -import { makeAdUnit, makeBidder, makeAdSlot, makeRequest } from './faker/fixtures'; -import adaptermanager from 'src/adaptermanager'; - -function resetPrebid() { - delete window.$$PREBID_GLOBAL$$; - require('src/prebid'); -} - -let pbjsBackup; -let adUnits; -let adapters; - -describe('Bug: #825 adUnit code based refresh times out without setting targets', () => { - describe('Given a page with five ad slots has loaded and the auction is finished', () => { - - before(() => { - pbjsBackup = $$PREBID_GLOBAL$$; - - adapters = [ - makeBidder(), - makeBidder(), - makeBidder() - ]; - - adUnits = [ - makeAdUnit({ bids: adapters }), - makeAdUnit({ bids: adapters }), - makeAdUnit({ bids: adapters }), - makeAdUnit({ bids: adapters }), - makeAdUnit({ bids: adapters }) - ]; - - makeAdSlot({ code: adUnits[0].code }); - makeAdSlot({ code: adUnits[1].code }); - makeAdSlot({ code: adUnits[2].code }); - makeAdSlot({ code: adUnits[3].code }); - makeAdSlot({ code: adUnits[4].code }); - - resetPrebid(); - - adapters.forEach(adapter => { - adaptermanager.bidderRegistry[[adapter.bidder]] = { callBids: adapter.callBids }; - }); - }); - - after(() => { - window.$$PREBID_GLOBAL$$ = pbjsBackup; - }); - - describe('When the first auction completes', () => { - it('Then there will be correct number of bidder requests', () => { - var clock = sinon.useFakeTimers(); - $$PREBID_GLOBAL$$.requestBids(makeRequest({ adUnits })); - equal($$PREBID_GLOBAL$$._bidsRequested.length, 3, 'there are three bidder requests'); - var firstRequestId = $$PREBID_GLOBAL$$._bidsRequested.map(request => request.requestId).filter((value, index, array) => array.indexOf(value) === index)[0]; - $$PREBID_GLOBAL$$.requestBids(makeRequest({ adUnits: [adUnits[0]] })); - clock.tick($$PREBID_GLOBAL$$.bidderTimeout); - var nextRequestId = $$PREBID_GLOBAL$$._bidsRequested.map(request => request.requestId).filter((value, index, array) => array.indexOf(value) === index)[0]; - equal($$PREBID_GLOBAL$$._bidsRequested.length, 3, 'there are still three bidder request'); - notEqual(firstRequestId, nextRequestId, 'the request IDs have changed'); - clock.restore(); - }); - }); - }); -}); diff --git a/test/spec/loaders/adapterLoader_spec.js b/test/spec/loaders/adapterLoader_spec.js index 3fa20ab5322..e3b07a15330 100644 --- a/test/spec/loaders/adapterLoader_spec.js +++ b/test/spec/loaders/adapterLoader_spec.js @@ -5,18 +5,44 @@ const allAdapters = require('../../fixtures/allAdapters'); const expect = require('chai').expect; require('../../../loaders/adapterLoader'); -const defaultAdapters = ["aardvark","adblade","adbutler","adequant","adform","admedia","aol","appnexus","appnexusAst","getintent","hiromedia","indexExchange","kruxlink","komoona","openx","piximedia","pubmatic","pulsepoint","rubicon","sonobi","sovrn","springserve","triplelift","twenga","yieldbot","nginad","brightcom","wideorbit","jcm","underdogmedia","memeglobal","centro","roxot",{"appnexus":{"alias":"brealtime"}},{"appnexus":{"alias":"pagescience"}},{"appnexus":{"alias":"defymedia"}},{"appnexusAst":{"supportedMediaTypes":["video"]}}]; +const defaultAdapters = ["aardvark","adblade","adbutler","adequant","adform","admedia","aol","appnexus","appnexusAst","getintent","hiromedia","indexExchange","kruxlink","komoona","openx","piximedia","pubmatic","pulsepoint","rubicon","sonobi","sovrn","springserve","thoughtleadr","triplelift","twenga","yieldbot","nginad","brightcom","wideorbit","jcm","underdogmedia","memeglobal","centro","roxot",{"appnexus":{"alias":"brealtime"}},{"appnexus":{"alias":"pagescience"}},{"appnexus":{"alias":"defymedia"}},{"appnexusAst":{"supportedMediaTypes":["video"]}}]; const input = `/** INSERT ADAPTERS - DO NOT EDIT OR REMOVE */ /** END INSERT ADAPTERS */`; +const delimiter = '/*!ADAPTER REGISTER DELIMITER*/'; + +const getAdaptersWithDelimiter = () => { + return delimiter +'var AardvarkAdapter = require(\'./adapters/aardvark.js\');\n ' + + 'exports.registerBidAdapter(new AardvarkAdapter(), \'aardvark\');\n' + + delimiter + + 'var AolAdapter = require(\'./adapters/aol.js\');\n ' + + 'exports.registerBidAdapter(new AolAdapter(), \'aol\');\n' + + delimiter + + 'var AppnexusAdapter = require(\'./adapters/appnexus.js\');\n ' + + 'exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\n' + + delimiter + + 'var RubiconAdapter = require(\'./adapters/rubicon.js\');\n ' + + 'exports.registerBidAdapter(new RubiconAdapter(), \'rubicon\');\n' + + delimiter + + 'exports.aliasBidAdapter(\'appnexus\',\'pagescience\');\n' + + delimiter + + 'exports.videoAdapters = ["appnexusAst"];'; +}; + describe('adapterLoader.js', () => { it('should replace with the default set of adapters', () => { - const getAdapterStub = () => defaultAdapters; + const getAdapterStub = () => [ + 'aardvark', + 'aol', + 'appnexus', + 'rubicon', + {"appnexus":{"alias":"pagescience"}}, + {"appnexusAst":{"supportedMediaTypes":["video"]}} + ]; const loader = proxyquire('../../../loaders/adapterLoader', {'./getAdapters' : getAdapterStub}); let output = loader(input); - expect(output).to.equal(allAdapters.getAllAdaptersString()); - + expect(output).to.equal(getAdaptersWithDelimiter()); }); it('should return custom adapter list if file exists', () => { @@ -24,7 +50,11 @@ describe('adapterLoader.js', () => { const getAdapterStub = () => customAdapter; const loader = proxyquire('../../../loaders/adapterLoader', {'fs': {existsSync : ()=> true }, './getAdapters' : getAdapterStub}); let output = loader(input); - const expected = 'let customAdapterName = require(\'/somepath/customAdapterName.js\');\n exports.registerBidAdapter(new customAdapterName, \'customAdapterName\');\nexports.videoAdapters = [];'; + const expected = delimiter + + 'let customAdapterName = require(\'/somepath/customAdapterName.js\');\n ' + + 'exports.registerBidAdapter(new customAdapterName, \'customAdapterName\');\n' + + delimiter + + 'exports.videoAdapters = [];'; expect(output).to.equal(expected); }); @@ -33,7 +63,11 @@ describe('adapterLoader.js', () => { const getAdapterStub = () => customAdapter; const loader = proxyquire('../../../loaders/adapterLoader', {'fs': {existsSync : ()=> false }, './getAdapters' : getAdapterStub}); let output = loader(input); - const expected = 'var AppnexusAdapter = require(\'./adapters/appnexus.js\');\n exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\nexports.videoAdapters = [];'; + const expected = delimiter + + 'var AppnexusAdapter = require(\'./adapters/appnexus.js\');\n ' + + 'exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\n' + + delimiter + + 'exports.videoAdapters = [];'; expect(output).to.equal(expected); }); diff --git a/test/spec/loaders/getAdapters_spec.js b/test/spec/loaders/getAdapters_spec.js index 93d8e4ad69a..3cb6a4db74a 100644 --- a/test/spec/loaders/getAdapters_spec.js +++ b/test/spec/loaders/getAdapters_spec.js @@ -10,6 +10,8 @@ describe('loaders/getAdapters', () => { let defaultAdapters; let customAdapters; + const defaultAdaptersFile = 'adapters.json'; + const adaptersArg = 'adapters'; beforeEach(() => { defaultAdapters = [ 'adapter 1', 'adapter 2', 'adapter 3' ]; @@ -32,7 +34,7 @@ describe('loaders/getAdapters', () => { const getAdapters = proxyquire('../../../loaders/getAdapters', { yargs: { argv: { adapters: 'custom-adapters.json' } } }); - expect(getAdapters()).to.deep.equal(customAdapters); + expect(getAdapters(defaultAdaptersFile, adaptersArg)).to.deep.equal(customAdapters); }); }); @@ -51,7 +53,7 @@ describe('loaders/getAdapters', () => { const getAdapters = proxyquire('../../../loaders/getAdapters', { yargs: { argv: { adapters: 'non-existent-adapters.json' } } }); - expect(getAdapters()).to.deep.equal(defaultAdapters); + expect(getAdapters(defaultAdaptersFile, adaptersArg)).to.deep.equal(defaultAdapters); expect(log).to.match(/non-existent-adapters.json/); console.log = consoleLog; }); @@ -69,7 +71,7 @@ describe('loaders/getAdapters', () => { const getAdapters = proxyquire('../../../loaders/getAdapters', { yargs: { argv: {} } }); - expect(getAdapters()).to.deep.equal(defaultAdapters); + expect(getAdapters(defaultAdaptersFile, adaptersArg)).to.deep.equal(defaultAdapters); }); }); @@ -83,7 +85,7 @@ describe('loaders/getAdapters', () => { const getAdapters = proxyquire('../../../loaders/getAdapters', { yargs: { argv: {} } }); - expect(getAdapters()).to.deep.equal([]); + expect(getAdapters(defaultAdaptersFile, adaptersArg)).to.deep.equal([]); }); }); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 7038ad2e100..f4655f3b2ad 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1614,6 +1614,13 @@ describe('Unit: Prebid Module', function () { expect(newAdserverTargeting).to.deep.equal(window.apntag.tags[adUnitCode].keywords); }); + it('should not find hb_adid key in lowercase for all bidders', () => { + const adUnitCode = '/19968336/header-bid-tag-0'; + $$PREBID_GLOBAL$$.enableSendAllBids(); + $$PREBID_GLOBAL$$.setTargetingForAst(); + const keywords = Object.keys(window.apntag.tags[adUnitCode].keywords).filter(keyword => (keyword.substring(0, 'hb_adid'.length) === 'hb_adid')); + expect(keywords.length).to.equal(0); + }); }); }); diff --git a/test/spec/url_spec.js b/test/spec/url_spec.js index cc4c1178a18..55b7de5b04f 100644 --- a/test/spec/url_spec.js +++ b/test/spec/url_spec.js @@ -24,7 +24,7 @@ describe('helpers.url', () => { }); it('extracts the pathname', () => { - expect(['/pathname/', 'pathname/']).to.include(parsed.pathname); + expect(parsed).to.have.property('pathname', '/pathname/'); }); it('extracts the search query', () => { diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 118109c3971..faa7aaae13a 100755 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -130,7 +130,7 @@ describe('Utils', function () { c:'3' }; - var output = utils.extend(target, source); + var output = Object.assign(target, source); assert.deepEqual(output, expectedResult); }); @@ -140,7 +140,7 @@ describe('Utils', function () { c:'3' }; - var output = utils.extend(target, source); + var output = Object.assign(target, source); assert.deepEqual(output, source); }); @@ -151,7 +151,7 @@ describe('Utils', function () { }; var source = {}; - var output = utils.extend(target, source); + var output = Object.assign(target, source); assert.deepEqual(output, target); }); });