diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 64635b7fc03..3af98f37be0 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -46,11 +46,16 @@ For modules and core platform updates, the initial reviewer should request an ad ## Ticket Coordinator -Each week, Prebid Org assigns one person to keep an eye on incoming issues and PRs. That person should: +Each week, Prebid Org assigns one person to keep an eye on incoming issues and PRs. Every Monday morning a reminder is +sent to the prebid-js slack channel with a link to the spreadsheet. If you're on rotation, please check that list each +Monday to see if you're on-duty. + +When on-duty: - Review issues and PRs at least once per weekday for new items. Encourage a 48 "SLA" on PRs/issues assigned. Aim for touchpoint once every 48/hours. -- For PRs: assign PRs to individuals on the PR review list. Try to be equitable -- not all PRs are created equally. Use the "Assigned" field and add the "Needs Review" label. +- For PRs: assign PRs to individuals on the **PR review list**. Try to be equitable -- not all PRs are created equally. Use the "Assigned" field and add the "Needs Review" label. - For Issues: try to address questions and troubleshooting requests on your own, assigning them to others as needed. Please add labels as appropriate (I.E. bug, question, backlog etc). - Issues that are questions or troubleshooting requests may be closed if the originator doesn't respond within a week to requests for confirmation or details. - Issues that are bug reports should be left open and assigned to someone in PR rotation to confirm or deny the bug status. -- It's polite to check with others before assigning them large tasks. -- If possible, check in on older items and see if they can be unstuck. +- It's polite to check with others before assigning them extra-large tasks. +- If possible, check in on older PRs and Issues and see if they can be unstuck. +- Perform the weekly Prebid.js release per instructions at https://github.com/prebid/Prebid.js/blob/master/RELEASE_SCHEDULE.md . This generally takes place on Tues or Weds. diff --git a/allowedModules.js b/allowedModules.js index 4f8b8039d97..2a521f781f9 100644 --- a/allowedModules.js +++ b/allowedModules.js @@ -1,11 +1,11 @@ const sharedWhiteList = [ - 'core-js/library/fn/array/find', // no ie11 - 'core-js/library/fn/array/includes', // no ie11 - 'core-js/library/fn/set', // ie11 supports Set but not Set#values - 'core-js/library/fn/string/includes', // no ie11 - 'core-js/library/fn/number/is-integer', // no ie11, - 'core-js/library/fn/array/from' // no ie11 + 'core-js-pure/features/array/find', // no ie11 + 'core-js-pure/features/array/includes', // no ie11 + 'core-js-pure/features/set', // ie11 supports Set but not Set#values + 'core-js-pure/features/string/includes', // no ie11 + 'core-js-pure/features/number/is-integer', // no ie11, + 'core-js-pure/features/array/from' // no ie11 ]; module.exports = { diff --git a/gulpHelpers.js b/gulpHelpers.js index aabd28ced02..bcaf3736f15 100644 --- a/gulpHelpers.js +++ b/gulpHelpers.js @@ -84,7 +84,9 @@ module.exports = { if (fs.lstatSync(modulePath).isDirectory()) { modulePath = path.join(modulePath, 'index.js') } - memo[modulePath] = moduleName; + if (fs.existsSync(modulePath)) { + memo[modulePath] = moduleName; + } return memo; }, {}); } catch (err) { diff --git a/integrationExamples/gpt/audienceNetwork_dfp.html b/integrationExamples/gpt/audienceNetwork_dfp.html deleted file mode 100644 index b30df31b276..00000000000 --- a/integrationExamples/gpt/audienceNetwork_dfp.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -

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/modules/1ad4goodBidAdapter.js b/modules/1ad4goodBidAdapter.js index 560808b368f..178f3765587 100644 --- a/modules/1ad4goodBidAdapter.js +++ b/modules/1ad4goodBidAdapter.js @@ -1,8 +1,8 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; const BIDDER_CODE = '1ad4good'; const URL = 'https://hb.1ad4good.org/prebid'; diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index a21915e00a9..f144089b2a2 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1,4 +1,4 @@ -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { loadExternalScript } from '../src/adloader.js' diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 1a16801ba67..cbd30edb432 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -1,8 +1,8 @@ import * as utils from '../src/utils.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; /* * In case you're AdKernel whitelable platform's client who needs branded adapter to diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index e8a27a033b4..b2f24cfa910 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -20,8 +20,22 @@ export const spec = { buildRequests: function (validRequest, bidderRequest) { const payload = { imps: [], - referrer: encodeURIComponent(bidderRequest.refererInfo.referer), }; + if (bidderRequest) { + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + payload.referrer = encodeURIComponent(bidderRequest.refererInfo.referer); + } + if (bidderRequest.gdprConsent) { + payload.gdprConsent = { + consentString: bidderRequest.gdprConsent.consentString, + // will check if the gdprApplies field was populated with a boolean value (ie from page config). If it's undefined, then default to true + gdprApplies: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true + } + } + if (bidderRequest.uspConsent) { + payload.uspConsent = bidderRequest.uspConsent; + } + } validRequest.forEach((bid) => { payload.imps.push(bid); }); diff --git a/modules/adomikAnalyticsAdapter.js b/modules/adomikAnalyticsAdapter.js index ec5923ea0f8..5bbee86df54 100644 --- a/modules/adomikAnalyticsAdapter.js +++ b/modules/adomikAnalyticsAdapter.js @@ -2,8 +2,8 @@ import adapter from '../src/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { logInfo } from '../src/utils.js'; -import find from 'core-js/library/fn/array/find.js'; -import findIndex from 'core-js/library/fn/array/find-index.js'; +import find from 'core-js-pure/features/array/find.js'; +import findIndex from 'core-js-pure/features/array/find-index.js'; // Events used in adomik analytics adapter const auctionInit = CONSTANTS.EVENTS.AUCTION_INIT; diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 94672b20c0c..911da416cfe 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -2,7 +2,7 @@ import {Renderer} from '../src/Renderer.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {isStr, isArray, isNumber, isPlainObject, isBoolean, logError} from '../src/utils.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const ADAPTER_VERSION = 'v1.0.0'; const BID_METHOD = 'POST'; diff --git a/modules/adpod.js b/modules/adpod.js index a2d5cb4a0ed..1e264365082 100644 --- a/modules/adpod.js +++ b/modules/adpod.js @@ -20,12 +20,12 @@ import { setupBeforeHookFnOnce, module } from '../src/hook.js'; import { store } from '../src/videoCache.js'; import { config } from '../src/config.js'; import { ADPOD } from '../src/mediaTypes.js'; -import Set from 'core-js/library/fn/set.js'; -import find from 'core-js/library/fn/array/find.js'; +import Set from 'core-js-pure/features/set'; +import find from 'core-js-pure/features/array/find.js'; import { auctionManager } from '../src/auctionManager.js'; import CONSTANTS from '../src/constants.json'; -const from = require('core-js/library/fn/array/from.js'); +const from = require('core-js-pure/features/array/from.js'); const TARGETING_KEY_PB_CAT_DUR = 'hb_pb_cat_dur'; const TARGETING_KEY_CACHE_ID = 'hb_cache_id'; diff --git a/modules/adprimeBidAdapter.js b/modules/adprimeBidAdapter.js new file mode 100644 index 00000000000..d324886f7c6 --- /dev/null +++ b/modules/adprimeBidAdapter.js @@ -0,0 +1,102 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'adprime'; +const AD_URL = 'https://delta.adprime.com/?c=o&m=multi'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || + !bid.ttl || !bid.currency) { + return false; + } + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl); + case NATIVE: + return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + 'host': location.host, + 'page': location.pathname, + 'placements': placements + }; + if (bidderRequest) { + if (bidderRequest.uspConsent) { + request.ccpa = bidderRequest.uspConsent; + } + if (bidderRequest.gdprConsent) { + request.gdpr = bidderRequest.gdprConsent + } + } + const len = validBidRequests.length; + + for (let i = 0; i < len; i++) { + let bid = validBidRequests[i]; + let sizes + if (bid.mediaTypes) { + if (bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + sizes = bid.mediaTypes[BANNER].sizes + } else if (bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { + sizes = bid.mediaTypes[VIDEO].playerSize + } + } + placements.push({ + placementId: bid.params.placementId, + bidId: bid.bidId, + sizes: sizes || [], + wPlayer: sizes ? sizes[0] : 0, + hPlayer: sizes ? sizes[1] : 0, + traffic: bid.params.traffic || BANNER, + schain: bid.schain || {} + }); + } + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + response.push(resItem); + } + } + return response; + }, +}; + +registerBidder(spec); diff --git a/modules/adprimeBidAdapter.md b/modules/adprimeBidAdapter.md new file mode 100644 index 00000000000..0c6bed68c13 --- /dev/null +++ b/modules/adprimeBidAdapter.md @@ -0,0 +1,53 @@ +# Overview + +``` +Module Name: adprime Bidder Adapter +Module Type: adprime Bidder Adapter +``` + +# Description + +Module that connects to adprime demand sources + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'placementId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'adprime', + params: { + placementId: 0, + traffic: 'banner' + } + } + ] + }, + // Will return test vast xml. All video params are stored under placement in publishers UI + { + code: 'placementId_0', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [ + { + bidder: 'adprime', + params: { + placementId: 0, + traffic: 'video' + } + } + ] + } + ]; +``` diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 82601117017..74bb6dba8d3 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -2,7 +2,7 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {VIDEO, BANNER} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const URL = 'https://ghb.adtelligent.com/auction/'; const OUTSTREAM_SRC = 'https://player.adtelligent.com/outstream-unit/2.01/outstream.min.js'; diff --git a/modules/advangelistsBidAdapter.js b/modules/advangelistsBidAdapter.js index 64e4c58653b..746baa9ae35 100755 --- a/modules/advangelistsBidAdapter.js +++ b/modules/advangelistsBidAdapter.js @@ -2,8 +2,8 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; const ADAPTER_VERSION = '1.0'; const BIDDER_CODE = 'advangelists'; diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index cc76731836b..2d5c64dfe53 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -2,7 +2,7 @@ import { config } from '../src/config.js' import * as utils from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js' -import includes from 'core-js/library/fn/array/includes.js' +import includes from 'core-js-pure/features/array/includes.js' /** * Adapter for requesting bids from adxcg.net diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 9391a3fb94f..412acdde3dd 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const VERSION = '1.0'; const BIDDER_CODE = 'adyoulike'; diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 53cfc85389d..2bec1d1f047 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -4,8 +4,8 @@ import { config } from '../src/config.js'; import { registerBidder, getIabSubCategory } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO, ADPOD } from '../src/mediaTypes.js'; import { auctionManager } from '../src/auctionManager.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { OUTSTREAM, INSTREAM } from '../src/video.js'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/audienceNetworkBidAdapter.js b/modules/audienceNetworkBidAdapter.js index 2d7b7eae0dc..816a6abd0e8 100644 --- a/modules/audienceNetworkBidAdapter.js +++ b/modules/audienceNetworkBidAdapter.js @@ -3,8 +3,8 @@ */ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { generateUUID, deepAccess, convertTypes, formatQS } from '../src/utils.js'; -import findIndex from 'core-js/library/fn/array/find-index.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import findIndex from 'core-js-pure/features/array/find-index.js'; +import includes from 'core-js-pure/features/array/includes.js'; const code = 'audienceNetwork'; const currency = 'USD'; diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 9b6c431fdd7..b5ac0321cae 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -3,8 +3,8 @@ import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; const ADAPTER_VERSION = '1.9'; const ADAPTER_NAME = 'BFIO_PREBID'; diff --git a/modules/bluebillywigBidAdapter.js b/modules/bluebillywigBidAdapter.js new file mode 100644 index 00000000000..9e4f5b62e48 --- /dev/null +++ b/modules/bluebillywigBidAdapter.js @@ -0,0 +1,386 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; +import { createEidsArray } from './userId/eids.js'; + +const DEV_MODE = window.location.search.match(/bbpbs_debug=true/); + +// Blue Billywig Constants +const BB_CONSTANTS = { + BIDDER_CODE: 'bluebillywig', + AUCTION_URL: '$$URL_STARTpbs.bluebillywig.com/openrtb2/auction?pub=$$PUBLICATION', + SYNC_URL: '$$URL_STARTpbs.bluebillywig.com/static/cookie-sync.html?pub=$$PUBLICATION', + RENDERER_URL: 'https://$$PUBLICATION.bbvms.com/r/$$RENDERER.js', + DEFAULT_TIMEOUT: 5000, + DEFAULT_TTL: 300, + DEFAULT_WIDTH: 768, + DEFAULT_HEIGHT: 432, + DEFAULT_NET_REVENUE: true +}; + +// Aliasing +const getConfig = config.getConfig; + +// Helper Functions +export const BB_HELPERS = { + addSiteAppDevice: function(request, pageUrl) { + if (!request) return; + + if (typeof getConfig('app') === 'object') request.app = getConfig('app'); + else if (pageUrl) request.site = { page: pageUrl }; + + if (typeof getConfig('device') === 'object') request.device = getConfig('device'); + if (!request.device) request.device = {}; + if (!request.device.w) request.device.w = window.innerWidth; + if (!request.device.h) request.device.h = window.innerHeight; + }, + addSchain: function(request, validBidRequests) { + if (!request) return; + + const schain = utils.deepAccess(validBidRequests, '0.schain'); + if (schain) request.source.ext = { schain: schain }; + }, + addCurrency: function(request) { + if (!request) return; + + const adServerCur = getConfig('currency.adServerCurrency'); + if (adServerCur && typeof adServerCur === 'string') request.cur = [adServerCur]; + else if (Array.isArray(adServerCur) && adServerCur.length) request.cur = [adServerCur[0]]; + }, + addUserIds: function(request, validBidRequests) { + if (!request) return; + + const bidUserId = utils.deepAccess(validBidRequests, '0.userId'); + const eids = createEidsArray(bidUserId); + + if (eids.length) { + utils.deepSetValue(request, 'user.ext.eids', eids); + } + }, + addDigiTrust: function(request, bidRequests) { + const digiTrust = BB_HELPERS.getDigiTrustParams(bidRequests && bidRequests[0]); + if (digiTrust) utils.deepSetValue(request, 'user.ext.digitrust', digiTrust); + }, + substituteUrl: function (url, publication, renderer) { + return url.replace('$$URL_START', (DEV_MODE) ? 'https://dev.' : 'https://').replace('$$PUBLICATION', publication).replace('$$RENDERER', renderer); + }, + getAuctionUrl: function(publication) { + return BB_HELPERS.substituteUrl(BB_CONSTANTS.AUCTION_URL, publication); + }, + getSyncUrl: function(publication) { + return BB_HELPERS.substituteUrl(BB_CONSTANTS.SYNC_URL, publication); + }, + getRendererUrl: function(publication, renderer) { + return BB_HELPERS.substituteUrl(BB_CONSTANTS.RENDERER_URL, publication, renderer); + }, + getDigiTrustParams: function(bidRequest) { + const digiTrustId = BB_HELPERS.getDigiTrustId(bidRequest); + + if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) return null; + return { + id: digiTrustId.id, + keyv: digiTrustId.keyv + } + }, + getDigiTrustId: function(bidRequest) { + const bidRequestDigiTrust = utils.deepAccess(bidRequest, 'userId.digitrustid.data'); + if (bidRequestDigiTrust) return bidRequestDigiTrust; + + const digiTrustUser = getConfig('digiTrustId'); + return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; + }, + transformRTBToPrebidProps: function(bid, serverResponse) { + bid.cpm = bid.price; delete bid.price; + bid.bidId = bid.impid; + bid.requestId = bid.impid; delete bid.impid; + bid.width = bid.w || BB_CONSTANTS.DEFAULT_WIDTH; + bid.height = bid.h || BB_CONSTANTS.DEFAULT_HEIGHT; + if (bid.adm) { + bid.ad = bid.adm; + bid.vastXml = bid.adm; + delete bid.adm; + } + if (bid.nurl && !bid.adm) { // ad markup is on win notice url, and adm is ommited according to OpenRTB 2.5 + bid.vastUrl = bid.nurl; + delete bid.nurl; + } + bid.netRevenue = BB_CONSTANTS.DEFAULT_NET_REVENUE; + bid.creativeId = bid.crid; delete bid.crid; + bid.currency = serverResponse.cur; + bid.ttl = BB_CONSTANTS.DEFAULT_TTL; + }, +}; + +// Renderer Functions +const BB_RENDERER = { + bootstrapPlayer: function(bid) { + const config = { + code: bid.adUnitCode, + }; + + if (bid.vastXml) config.vastXml = bid.vastXml; + else if (bid.vastUrl) config.vastUrl = bid.vastUrl; + + if (!bid.vastXml && !bid.vastUrl) { + utils.logWarn(`${BB_CONSTANTS.BIDDER_CODE}: No vastXml or vastUrl on bid, bailing...`); + return; + } + + const rendererId = BB_RENDERER.getRendererId(bid.publicationName, bid.rendererCode); + + const ele = document.getElementById(bid.adUnitCode); // NB convention + + let renderer; + + for (let rendererIndex = 0; rendererIndex < window.bluebillywig.renderers.length; rendererIndex++) { + if (window.bluebillywig.renderers[rendererIndex]._id === rendererId) { + renderer = window.bluebillywig.renderers[rendererIndex]; + break; + } + } + + if (renderer) renderer.bootstrap(config, ele); + else utils.logWarn(`${BB_CONSTANTS.BIDDER_CODE}: Couldn't find a renderer with ${rendererId}`); + }, + newRenderer: function(rendererUrl, adUnitCode) { + const renderer = Renderer.install({ + url: rendererUrl, + loaded: false, + adUnitCode + }); + + try { + renderer.setRender(BB_RENDERER.outstreamRender); + } catch (err) { + utils.logWarn(`${BB_CONSTANTS.BIDDER_CODE}: Error tying to setRender on renderer`, err); + } + + return renderer; + }, + outstreamRender: function(bid) { + bid.renderer.push(function() { BB_RENDERER.bootstrapPlayer(bid) }); + }, + getRendererId: function(pub, renderer) { + return `${pub}-${renderer}`; // NB convention! + } +}; + +// Spec Functions +// These functions are used to construct the core spec for the adapter +export const spec = { + code: BB_CONSTANTS.BIDDER_CODE, + supportedMediaTypes: [VIDEO], + syncStore: { bidders: [], }, + isBidRequestValid(bid) { + const publicationNameRegex = /^\w+\.?\w+$/; + const rendererRegex = /^[\w+_]+$/; + + if (!bid.params) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no params set on bid. Rejecting bid: `, bid); + return false; + } + + if (!bid.params.hasOwnProperty('publicationName') || typeof bid.params.publicationName !== 'string') { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no publicationName specified in bid params, or it's not a string. Rejecting bid: `, bid); + return false; + } else if (!publicationNameRegex.test(bid.params.publicationName)) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: publicationName must be in format 'publication' or 'publication.environment'. Rejecting bid: `, bid); + return false; + } + + if ((!bid.params.hasOwnProperty('rendererCode') || typeof bid.params.rendererCode !== 'string')) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no rendererCode was specified in bid params. Rejecting bid: `, bid); + return false; + } else if (!rendererRegex.test(bid.params.rendererCode)) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: rendererCode must be alphanumeric, including underscores. Rejecting bid: `, bid); + return false; + } + + if (!bid.params.accountId) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no accountId specified in bid params. Rejecting bid: `, bid); + return false; + } + + if (bid.params.hasOwnProperty('connections')) { + if (!Array.isArray(bid.params.connections)) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: connections is not of type array. Rejecting bid: `, bid); + return false; + } else { + for (let connectionIndex = 0; connectionIndex < bid.params.connections.length; connectionIndex++) { + const connection = bid.params.connections[connectionIndex]; + if (!bid.params.hasOwnProperty(connection)) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: connection specified in params.connections, but not configured in params. Rejecting bid: `, bid); + return false; + } + } + } + } else { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no connections specified in bid. Rejecting bid: `, bid); + return false; + } + + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no context specified in bid. Rejecting bid: `, bid); + return false; + } + + if (bid.mediaTypes[VIDEO].context !== 'outstream') { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: video.context is invalid, must be "outstream". Rejecting bid: `, bid); + return false; + } + } else { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: mediaTypes or mediaTypes.video is not specified. Rejecting bid: `, bid); + return false; + } + + return true; + }, + buildRequests(validBidRequests, bidderRequest) { + const imps = []; + + for (let validBidRequestIndex = 0; validBidRequestIndex < validBidRequests.length; validBidRequestIndex++) { + const validBidRequest = validBidRequests[validBidRequestIndex]; + const _this = this; + + const ext = validBidRequest.params.connections.reduce(function(extBuilder, connection) { + extBuilder[connection] = validBidRequest.params[connection]; + + if (_this.syncStore.bidders.indexOf(connection) === -1) _this.syncStore.bidders.push(connection); + + return extBuilder; + }, {}); + + imps.push({ id: validBidRequest.bidId, ext, secure: window.location.protocol === 'https' ? 1 : 0, video: utils.deepAccess(validBidRequest, 'mediaTypes.video') }); + } + + const request = { + id: bidderRequest.auctionId, + source: {tid: bidderRequest.auctionId}, + tmax: BB_CONSTANTS.DEFAULT_TIMEOUT, + imp: imps, + test: DEV_MODE ? 1 : 0, + ext: { + prebid: { + targeting: { includewinners: true, includebidderkeys: false } + } + } + }; + + // handle privacy settings for GDPR/CCPA/COPPA + if (bidderRequest.gdprConsent) { + let gdprApplies = 0; + if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + utils.deepSetValue(request, 'regs.ext.gdpr', gdprApplies); + utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } + + if (bidderRequest.uspConsent) { + utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); + this.syncStore.uspConsent = bidderRequest.uspConsent; + } + + if (getConfig('coppa') == true) utils.deepSetValue(request, 'regs.coppa', 1); + + // Enrich the request with any external data we may have + BB_HELPERS.addSiteAppDevice(request, bidderRequest.refererInfo && bidderRequest.refererInfo.referer); + BB_HELPERS.addSchain(request, validBidRequests); + BB_HELPERS.addCurrency(request); + BB_HELPERS.addUserIds(request, validBidRequests); + BB_HELPERS.addDigiTrust(request, validBidRequests); + + return { + method: 'POST', + url: BB_HELPERS.getAuctionUrl(validBidRequests[0].params.publicationName), + data: JSON.stringify(request), + bidderRequest: bidderRequest + }; + }, + interpretResponse(serverResponse, request) { + serverResponse = serverResponse.body || {}; + + if (!serverResponse.hasOwnProperty('seatbid') || !Array.isArray(serverResponse.seatbid)) { + return []; + } + + const bids = []; + + for (let seatbidIndex = 0; seatbidIndex < serverResponse.seatbid.length; seatbidIndex++) { + const seatbid = serverResponse.seatbid[seatbidIndex]; + if (!seatbid.bid || !Array.isArray(seatbid.bid)) continue; + for (let bidIndex = 0; bidIndex < seatbid.bid.length; bidIndex++) { + const bid = seatbid.bid[bidIndex]; + BB_HELPERS.transformRTBToPrebidProps(bid, serverResponse); + + let bidParams; + for (let bidderRequestBidsIndex = 0; bidderRequestBidsIndex < request.bidderRequest.bids.length; bidderRequestBidsIndex++) { + if (request.bidderRequest.bids[bidderRequestBidsIndex].bidId === bid.bidId) { + bidParams = request.bidderRequest.bids[bidderRequestBidsIndex].params; + } + } + + if (bidParams) { + bid.publicationName = bidParams.publicationName; + bid.rendererCode = bidParams.rendererCode; + bid.accountId = bidParams.accountId; + } + + const rendererUrl = BB_HELPERS.getRendererUrl(bid.publicationName, bid.rendererCode); + + bid.renderer = BB_RENDERER.newRenderer(rendererUrl, bid.adUnitCode); + + bids.push(bid); + } + } + + return bids; + }, + getUserSyncs(syncOptions, serverResponses, gdpr) { + if (!serverResponses || !serverResponses.length) return []; + if (!syncOptions.iframeEnabled) return []; + + const queryString = []; + let accountId; + let publication; + + const serverResponse = serverResponses[0]; + if (!serverResponse.body || !serverResponse.body.seatbid) return []; + + for (let seatbidIndex = 0; seatbidIndex < serverResponse.body.seatbid.length; seatbidIndex++) { + const seatbid = serverResponse.body.seatbid[seatbidIndex]; + for (let bidIndex = 0; bidIndex < seatbid.bid.length; bidIndex++) { + const bid = seatbid.bid[bidIndex]; + accountId = bid.accountId || null; + publication = bid.publicationName || null; + + if (publication && accountId) break; + } + if (publication && accountId) break; + } + + if (!publication || !accountId) return []; + + if (gdpr.gdprApplies) queryString.push(`gdpr=${gdpr.gdprApplies ? 1 : 0}`); + if (gdpr.gdprApplies && gdpr.consentString) queryString.push(`gdpr_consent=${gdpr.consentString}`); + + if (this.syncStore.uspConsent) queryString.push(`usp_consent=${this.syncStore.uspConsent}`); + + queryString.push(`accountId=${accountId}`); + queryString.push(`bidders=${btoa(JSON.stringify(this.syncStore.bidders))}`); + queryString.push(`cb=${Date.now()}-${Math.random().toString().replace('.', '')}`); + + if (DEV_MODE) queryString.push('bbpbs_debug=true'); + + // NB syncUrl by default starts with ?pub=$$PUBLICATION + const syncUrl = `${BB_HELPERS.getSyncUrl(publication)}&${queryString.join('&')}`; + + return [{ + type: 'iframe', + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/bluebillywigBidAdapter.md b/modules/bluebillywigBidAdapter.md new file mode 100644 index 00000000000..7879697baf5 --- /dev/null +++ b/modules/bluebillywigBidAdapter.md @@ -0,0 +1,38 @@ +# Overview + +``` +Module Name: Blue Billywig Adapter +Module Type: Bidder Adapter +Maintainer: dev+prebid@bluebillywig.com +``` + +# Description + +Prebid Blue Billywig Bidder Adapter + +# Test Parameters + +``` + const adUnits = [{ + code: 'ad-unit', + sizes: [[[768,432],[640,480],[640,360]]], + mediaTypes: { + video: { + playerSize: [768, 432], + context: 'outstream', + mimes: ['video/mp4'], + protocols: [ 2,3,5,6] + } + }, + bids: [{ + bidder: 'bluebillywig', + params: { + publicationName: "bbprebid", + rendererCode: "renderer", + accountId: 642, + connections: [ 'bluebillywig' ], + bluebillywig: {} + } + }] + }]; +``` diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index a95a19f0402..0303e4f74bd 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -1,7 +1,7 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'bridgewell'; const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb=' + Math.random(); diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 4a1fa9ba8d0..c665eb1a29b 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -7,8 +7,8 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import { gdprDataHandler } from '../src/adapterManager.js'; -import includes from 'core-js/library/fn/array/includes.js'; -import strIncludes from 'core-js/library/fn/string/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import strIncludes from 'core-js-pure/features/string/includes.js'; const DEFAULT_CMP = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 10000; diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 5d137c994fe..ae48b9664fb 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -3,7 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index d72477a582a..c4dc23cf912 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -44,6 +44,11 @@ export const spec = { utils.deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + if (bidderRequest && bidderRequest.gdprConsent) { + utils.deepSetValue(openRtbBidRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(openRtbBidRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + return { method: 'POST', url: BIDDER_ENDPOINT, diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 480e98b31d3..6688d15d8e9 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -2,8 +2,8 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; -import includes from 'core-js/library/fn/array/includes.js'; -import find from 'core-js/library/fn/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'emx_digital'; const ENDPOINT = 'hb.emxdgt.com'; diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 363d0e396f8..e42e55be557 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -6,8 +6,8 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import { hasDeviceAccess } from '../src/utils.js'; import adapterManager, { gdprDataHandler } from '../src/adapterManager.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { registerSyncInner } from '../src/adapters/bidderFactory.js'; import { getHook } from '../src/hook.js'; import { validateStorageEnforcement } from '../src/storageManager.js'; diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index d071127f6b7..cb2401fd90a 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -2,7 +2,7 @@ import * as utils from '../src/utils.js' import { config } from '../src/config.js' import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index 34a4cd9e5d6..5671756e0b2 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -3,7 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js' import { auctionManager } from '../src/auctionManager.js' import { BANNER, VIDEO } from '../src/mediaTypes.js' import {Renderer} from '../src/Renderer.js'; -import find from 'core-js/library/fn/array/find'; +import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'hybrid'; const DSP_ENDPOINT = 'https://hbe198.hybrid.ai/prebidhb'; diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 2725bafdff8..0ab761b0b7b 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -1,8 +1,8 @@ import * as utils from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; -import find from 'core-js/library/fn/array/find.js'; -import isInteger from 'core-js/library/fn/number/is-integer.js'; +import find from 'core-js-pure/features/array/find.js'; +import isInteger from 'core-js-pure/features/number/is-integer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'ix'; diff --git a/modules/konduitAnalyticsAdapter.js b/modules/konduitAnalyticsAdapter.js new file mode 100644 index 00000000000..00df790b18a --- /dev/null +++ b/modules/konduitAnalyticsAdapter.js @@ -0,0 +1,225 @@ +import { ajax } from '../src/ajax.js'; +import adapter from '../src/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; +import * as utils from '../src/utils.js'; +import { targeting } from '../src/targeting.js'; +import { config } from '../src/config.js'; +import CONSTANTS from '../src/constants.json'; + +const TRACKER_HOST = 'tracker.konduit.me'; + +const analyticsType = 'endpoint'; + +const eventDataComposerMap = { + [CONSTANTS.EVENTS.AUCTION_INIT]: obtainAuctionInfo, + [CONSTANTS.EVENTS.AUCTION_END]: obtainAuctionInfo, + [CONSTANTS.EVENTS.BID_REQUESTED]: obtainBidRequestsInfo, + [CONSTANTS.EVENTS.BID_TIMEOUT]: obtainBidTimeoutInfo, + [CONSTANTS.EVENTS.BID_RESPONSE]: obtainBidResponseInfo, + [CONSTANTS.EVENTS.BID_WON]: obtainWinnerBidInfo, + [CONSTANTS.EVENTS.NO_BID]: obtainNoBidInfo, +}; + +// This function is copy from prebid core +function formatQS(query) { + return Object + .keys(query) + .map(k => Array.isArray(query[k]) + ? query[k].map(v => `${k}[]=${v}`).join('&') + : `${k}=${query[k]}`) + .join('&'); +} + +// This function is copy from prebid core +function buildUrl(obj) { + return (obj.protocol || 'http') + '://' + + (obj.host || + obj.hostname + (obj.port ? `:${obj.port}` : '')) + + (obj.pathname || '') + + (obj.search ? `?${formatQS(obj.search || '')}` : '') + + (obj.hash ? `#${obj.hash}` : ''); +} + +const getWinnerBidFromAggregatedEvents = () => { + return konduitAnalyticsAdapter.context.aggregatedEvents + .filter(evt => evt.eventType === CONSTANTS.EVENTS.BID_WON)[0]; +}; + +const isWinnerBidDetected = () => { + return !!getWinnerBidFromAggregatedEvents(); +}; +const isWinnerBidExist = () => { + return !!targeting.getWinningBids()[0]; +}; + +const konduitAnalyticsAdapter = Object.assign( + adapter({ analyticsType }), + { + track ({ eventType, args }) { + if (CONSTANTS.EVENTS.AUCTION_INIT === eventType) { + konduitAnalyticsAdapter.context.aggregatedEvents.splice(0); + } + + if (eventDataComposerMap[eventType]) { + konduitAnalyticsAdapter.context.aggregatedEvents.push({ + eventType, + data: eventDataComposerMap[eventType](args), + }); + } + + if (eventType === CONSTANTS.EVENTS.AUCTION_END) { + if (!isWinnerBidDetected() && isWinnerBidExist()) { + const bidWonData = eventDataComposerMap[CONSTANTS.EVENTS.BID_WON](targeting.getWinningBids()[0]); + + konduitAnalyticsAdapter.context.aggregatedEvents.push({ + eventType: CONSTANTS.EVENTS.BID_WON, + data: bidWonData, + }); + } + sendRequest({ method: 'POST', path: '/analytics-initial-event', payload: composeRequestPayload() }); + } + } + } +); + +function obtainBidTimeoutInfo (args) { + return args.map(item => item.bidder).filter(utils.uniques); +} + +function obtainAuctionInfo (auction) { + return { + auctionId: auction.auctionId, + timestamp: auction.timestamp, + auctionEnd: auction.auctionEnd, + auctionStatus: auction.auctionStatus, + adUnitCodes: auction.adUnitCodes, + labels: auction.labels, + timeout: auction.timeout + }; +} + +function obtainBidRequestsInfo (bidRequests) { + return { + bidderCode: bidRequests.bidderCode, + time: bidRequests.start, + bids: bidRequests.bids.map(function (bid) { + return { + transactionId: bid.transactionId, + adUnitCode: bid.adUnitCode, + bidId: bid.bidId, + startTime: bid.startTime, + sizes: utils.parseSizesInput(bid.sizes).toString(), + params: bid.params + }; + }), + }; +} + +function obtainBidResponseInfo (bidResponse) { + return { + bidderCode: bidResponse.bidder, + transactionId: bidResponse.transactionId, + adUnitCode: bidResponse.adUnitCode, + statusMessage: bidResponse.statusMessage, + mediaType: bidResponse.mediaType, + renderedSize: bidResponse.size, + cpm: bidResponse.cpm, + currency: bidResponse.currency, + netRevenue: bidResponse.netRevenue, + timeToRespond: bidResponse.timeToRespond, + bidId: bidResponse.bidId, + requestId: bidResponse.requestId, + creativeId: bidResponse.creativeId + }; +} + +function obtainNoBidInfo (bidResponse) { + return { + bidderCode: bidResponse.bidder, + transactionId: bidResponse.transactionId, + adUnitCode: bidResponse.adUnitCode, + bidId: bidResponse.bidId, + }; +} + +function obtainWinnerBidInfo (bidResponse) { + return { + adId: bidResponse.adId, + bidderCode: bidResponse.bidder, + adUnitCode: bidResponse.adUnitCode, + statusMessage: bidResponse.statusMessage, + mediaType: bidResponse.mediaType, + renderedSize: bidResponse.size, + cpm: bidResponse.cpm, + currency: bidResponse.currency, + netRevenue: bidResponse.netRevenue, + timeToRespond: bidResponse.timeToRespond, + bidId: bidResponse.requestId, + dealId: bidResponse.dealId, + status: bidResponse.status, + creativeId: bidResponse.creativeId + }; +} + +function composeRequestPayload () { + const konduitId = config.getConfig('konduit.konduitId'); + const { width, height } = window.screen; + + return { + konduitId, + prebidVersion: '$prebid.version$', + environment: { + screen: { width, height }, + language: navigator.language, + }, + events: konduitAnalyticsAdapter.context.aggregatedEvents, + }; +} + +function sendRequest ({ host = TRACKER_HOST, method, path, payload }) { + const formattedUrlOptions = { + protocol: 'https', + hostname: host, + pathname: path, + }; + if (method === 'GET') { + formattedUrlOptions.search = payload; + } + + let konduitAnalyticsRequestUrl = buildUrl(formattedUrlOptions); + + ajax( + konduitAnalyticsRequestUrl, + undefined, + method === 'POST' ? JSON.stringify(payload) : null, + { + contentType: 'application/json', + method, + withCredentials: true + } + ); +} + +konduitAnalyticsAdapter.originEnableAnalytics = konduitAnalyticsAdapter.enableAnalytics; + +konduitAnalyticsAdapter.enableAnalytics = function (analyticsConfig) { + const konduitId = config.getConfig('konduit.konduitId'); + + if (!konduitId) { + utils.logError('A konduitId in config is required to use konduitAnalyticsAdapter'); + return; + } + + konduitAnalyticsAdapter.context = { + aggregatedEvents: [], + }; + + konduitAnalyticsAdapter.originEnableAnalytics(analyticsConfig); +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: konduitAnalyticsAdapter, + code: 'konduit' +}); + +export default konduitAnalyticsAdapter; diff --git a/modules/konduitAnalyticsAdapter.md b/modules/konduitAnalyticsAdapter.md new file mode 100644 index 00000000000..c5854b77ccd --- /dev/null +++ b/modules/konduitAnalyticsAdapter.md @@ -0,0 +1,32 @@ +# Overview +​ +``` +Module Name: Konduit Analytics Adapter +Module Type: Analytics Adapter +Maintainer: support@konduit.me +``` +​ +​ +# Description +​ +Konduit Analytics adapter pushes Prebid events into Konduit platform, which is then organizes the data and presents it to a client in different insightful views. +​ +For more information, visit the [official Konduit website](https://konduitvideo.com/). +​ +​ +# Usage +​ +Konduit Analytics can be enabled with a standard `enableAnalytics` call. +Note it is also important to provide a valid Konduit identifier as a config parameter. +​ +```javascript +pbjs.setConfig({ + konduit: { + konduitId: your_konduit_id, + } +}); +​ +pbjs.enableAnalytics({ + provider: 'konduit' +}) +``` diff --git a/modules/konduitWrapper.js b/modules/konduitWrapper.js index 33bc45f7566..e015fb93616 100644 --- a/modules/konduitWrapper.js +++ b/modules/konduitWrapper.js @@ -2,87 +2,192 @@ import { registerVideoSupport } from '../src/adServerManager.js'; import { targeting } from '../src/targeting.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import { getPriceBucketString } from '../src/cpmBucketManager.js'; +import { getPriceByGranularity } from '../src/auction.js'; + +const SERVER_PROTOCOL = 'https'; +const SERVER_HOST = 'p.konduit.me'; const MODULE_NAME = 'Konduit'; +const KONDUIT_ID_CONFIG = 'konduit.konduitId'; + +export const errorMessages = { + NO_KONDUIT_ID: 'A konduitId param is required to be in configs', + NO_BID: 'A bid was not found', + CACHE_FAILURE: 'A bid was not cached', +}; + +// This function is copy from prebid core +function formatQS(query) { + return Object + .keys(query) + .map(k => Array.isArray(query[k]) + ? query[k].map(v => `${k}[]=${v}`).join('&') + : `${k}=${query[k]}`) + .join('&'); +} + +// This function is copy from prebid core +function buildUrl(obj) { + return (obj.protocol || 'http') + '://' + + (obj.host || + obj.hostname + (obj.port ? `:${obj.port}` : '')) + + (obj.pathname || '') + + (obj.search ? `?${formatQS(obj.search || '')}` : '') + + (obj.hash ? `#${obj.hash}` : ''); +} + function addLogLabel(args) { args = [].slice.call(args); args.unshift(`${MODULE_NAME}: `); return args; } -export function logInfo() { +function logInfo() { utils.logInfo(...addLogLabel(arguments)); } -export function logError() { +function logError() { utils.logError(...addLogLabel(arguments)); } -export function buildVastUrl(options) { - if (!options.params || !options.params.konduit_id) { - logError(`'konduit_id' parameter is required for $$PREBID_GLOBAL$$.adServers.konduit.buildVastUrl function`); - - return null; +function sendRequest ({ host = SERVER_HOST, protocol = SERVER_PROTOCOL, method = 'GET', path, payload, callbacks, timeout }) { + const formattedUrlOptions = { + protocol: protocol, + hostname: host, + pathname: path, + }; + if (method === 'GET') { + formattedUrlOptions.search = payload; } - const bid = options.bid || targeting.getWinningBids()[0]; + let konduitAnalyticsRequestUrl = buildUrl(formattedUrlOptions); + const ajax = ajaxBuilder(timeout); + + ajax( + konduitAnalyticsRequestUrl, + callbacks, + method === 'POST' ? JSON.stringify(payload) : null, + { + contentType: 'application/json', + method, + withCredentials: true + } + ); +} - if (!bid) { - logError('Bid is not provided or not found'); +/** + * This function accepts an object with bid and tries to cache it while generating konduit_cache_key for it. + * In addition, it returns a list with updated bid objects where k_cpm key is added + * @param {Object} options + * @param {Object} [options.bid] - winner bid from publisher + * @param {string} [options.adUnitCode] - to look for winner bid + * @param {string} [options.timeout] - timeout for bidsProcessor request + * @param {function} [options.callback] - callback will be called in the end of the request + */ +export function processBids(options = {}) { + const konduitId = config.getConfig(KONDUIT_ID_CONFIG); + options = options || {}; + + if (!konduitId) { + logError(errorMessages.NO_KONDUIT_ID); + + if (options.callback) { + options.callback(new Error(errorMessages.NO_KONDUIT_ID)); + } return null; } - logInfo('The following bid will be wrapped: ', bid); - - const queryParams = {}; + const bid = options.bid || targeting.getWinningBids(options.adUnitCode)[0]; - const vastUrl = obtainVastUrl(bid); - - if (vastUrl) { - queryParams.konduit_id = options.params.konduit_id; - queryParams.konduit_header_bidding = 1; - queryParams.konduit_url = vastUrl; - } else { - logError('No VAST url found in the bid'); - } - - let resultingUrl = null; + if (!bid) { + logError(errorMessages.NO_BID); - if (queryParams.konduit_url) { - resultingUrl = utils.buildUrl({ - protocol: 'https', - host: 'p.konduit.me', - pathname: '/api/vastProxy', - search: queryParams - }); + if (options.callback) { + options.callback(new Error(errorMessages.NO_BID)); + } - logInfo(`Konduit wrapped VAST url: ${resultingUrl}`); + return null; } - return resultingUrl; -} - -function obtainVastUrl(bid) { - const vastUrl = bid && bid.vastUrl; + const priceGranularity = config.getConfig('priceGranularity'); - if (vastUrl) { - logInfo(`VAST url found in the bid - ${vastUrl}`); + bid.kCpm = bid.cpm; - return encodeURIComponent(vastUrl); + if (!bid.adserverTargeting) { + bid.adserverTargeting = {}; } - const cacheUrl = config.getConfig('cache.url'); - if (cacheUrl) { - const composedCacheUrl = `${cacheUrl}?uuid=${bid.videoCacheKey}`; - - logInfo(`VAST url is taken from cache.url: ${composedCacheUrl}`); - - return encodeURIComponent(composedCacheUrl); - } + bid.adserverTargeting.k_cpm = getPriceByGranularity(priceGranularity)(bid); + + const bidsToProcess = [{ + auctionId: bid.auctionId, + vastUrl: bid.vastUrl, + bidderCode: bid.bidderCode, + creativeId: bid.creativeId, + adUnitCode: bid.adUnitCode, + cpm: bid.cpm, + currency: bid.currency, + }]; + + sendRequest({ + method: 'POST', + path: '/api/bidsProcessor', + timeout: options.timeout || 1000, + payload: { + clientId: konduitId, + bids: bidsToProcess, + }, + callbacks: { + success: (data) => { + let error = null; + logInfo('Bids processed successfully ', data); + try { + const { kCpmData, cacheData } = JSON.parse(data); + const processedBidKey = `${bid.bidderCode}:${bid.creativeId}`; + + if (!utils.isEmpty(cacheData)) { + bid.adserverTargeting.konduit_id = konduitId; + } else { + error = new Error(errorMessages.CACHE_FAILURE); + } + + if (utils.isNumber(kCpmData[processedBidKey])) { + bid.kCpm = kCpmData[processedBidKey]; + const priceStringsObj = getPriceBucketString( + bid.kCpm, + config.getConfig('customPriceBucket'), + config.getConfig('currency.granularityMultiplier') + ); + bid.adserverTargeting.k_cpm = priceStringsObj.custom || priceStringsObj[priceGranularity] || priceStringsObj.med; + } + + if (utils.isStr(cacheData[processedBidKey])) { + bid.konduitCacheKey = cacheData[processedBidKey]; + bid.adserverTargeting.konduit_cache_key = cacheData[processedBidKey]; + } + } catch (err) { + error = err; + logError('Error parsing JSON response for bidsProcessor data: ', err) + } + + if (options.callback) { + options.callback(error, [bid]); + } + }, + error: (error) => { + logError('Bid was not processed successfully ', error); + if (options.callback) { + options.callback(utils.isStr(error) ? new Error(error) : error, [bid]); + } + } + } + }); } registerVideoSupport('konduit', { - buildVastUrl: buildVastUrl, + processBids: processBids, }); diff --git a/modules/konduitWrapper.md b/modules/konduitWrapper.md index adbb50487da..3097801ffab 100644 --- a/modules/konduitWrapper.md +++ b/modules/konduitWrapper.md @@ -1,11 +1,84 @@ -## Konduit video tags wrapper +# Overview + +``` +Module Name: Konduit Accelerate +Module Type: Video Module +Maintainer: support@konduit.me +``` + +# Description + +Konduit Wrapper is a prebid module that allows +- wrapping a bid response so that it is processed through Konduit platform +- obtaining a historical performance indicator for a bid + + +# Configuration + +## Building Prebid with the Konduit wrapper function + +Your Prebid build must include the **konduitWrapper** module. Follow the build instructions for Prebid as explained in the top level README.md file of the Prebid source tree. + +ex: $ gulp build --modules=konduitWrapper -Konduit Wrapper is a prebid module to generate Konduit wrapped VAST tag URLs for a provided bid or a winning bid. +## Prebid related configuration -### Setup +Konduit module should be used with a valid Konduit identifier. +```javascript +pbjs.setConfig({ + konduit: { + konduitId: your_konduit_id, + } +}); +``` + +Please contact support@konduit.me for assistance. + + +## GAM related configuration + +It is important to configure your GAM line items. +Please contact support@konduit.me for assistance. + +In most cases it would require only Creative VAST URL update with the following URL: +``` +https://p.konduit.me/api/vastProxy?konduit_hb=1&konduit_hb_awarded=1&konduit_cache_key=%%PATTERN:konduit_cache_key%%&konduit_id=%%PATTERN:konduit_id%% ``` + + +# Usage + +Konduit module contains a single function that accepts an `options` parameter. + +The `options` parameter can include: +* `bid` - prebid object with VAST url that should be cached (if not passed first winning bid from `auctionManager.getWinningBids()` will be used) +* `adUnitCode` - adUnitCode where a winner bid can be found +* `timeout` - max time to wait for Konduit response with cache key and kCpm data +* `callback` - callback function is called once Konduit cache data for the bid. Arguments of this function are - `error` and `bids` (error should be `null` if Konduit request is successful) + +The function adds two parameters into the passed bid - kCpm and konduitCacheKey. Additionally `processBids` updates bid's `adserverTargeting` with `k_cpm`, `konduti_cache_key` and `konduit_id` fields. + + +```javascript +pbjs.requestBids({ + bidsBackHandler: function (bids) { + pbjs.adServers.konduit.processBids({ + callback: function (error, bids) { + var videoUrl = pbjs.adServers.dfp.buildVideoUrl({ + ... + }); + } + }); + } +}) +``` + + +# Sample code + +```javascript var videoAdUnit = [{ code: 'videoAd', mediaTypes: { @@ -28,46 +101,48 @@ var videoAdUnit = [{ pbjs.que.push(function(){ pbjs.addAdUnits(videoAdUnit); + pbjs.setConfig({ + konduit: { + konduitId: 'your_konduit_id', + }, + }); + pbjs.requestBids({ - timeout : 700, bidsBackHandler : function(bids) { var winnerBid = pbjs.getHighestCpmBids('videoAd')[0]; - var vastTagUrl = pbjs.adServers.konduit.buildVastUrl({ - bid: winnerBid, // just in case if you want to pass your bid - params: { - konduit_id: 'your_konduit_id' + pbjs.adServers.konduit.processBids({ + bid: winnerBid, + adUnitCode: videoAdUnit[0].code, + timeout: 2000, + callback: function (error, processedBids) { + var vastTagUrl = pbjs.adServers.dfp.buildVideoUrl({ + adUnit: videoAdUnit, + params: { + iu: '', + output: 'vast', + }, + }); + + invokeVideoPlayer(vastTagUrl); } }); - - invokeVideoPlayer(vastTagUrl); } }); }); function invokeVideoPlayer(vastTagUrl) { - videojs("video_player_id").ready(function() { - this.vastClient({ - adTagUrl: vastTagUrl, - playAdAlways: true, - verbosity: 4, - autoplay: true - }); - - this.play(); + videojs("video_player_id").ready(function() { + this.vastClient({ + adTagUrl: vastTagUrl, + playAdAlways: true, + verbosity: 4, + autoplay: true }); - } -``` - -Function parameters: -* `bid` - prebid object with VAST url that should be wrapped (if not passed first winning bid from `auctionManager.getWinningBids()` is used) -* `konduit_id` - your personal unique Konduit identifier (required) - -The function returns a Konduit wrapped VAST url if valid parameters are passed in. If some of the parameters are not passed or are invalid the function returns 'null' along with related error logs providing more details. + this.play(); + }); +} +``` -### Building Prebid with the Konduit wrapper function -Your Prebid build must include the **konduitWrapper** module. Follow the build instructions for Prebid as explained in the top level README.md file of the Prebid source tree. - -ex: $ gulp build --modules=konduitWrapper diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 57e5a5a7c2f..cda66f5eafd 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -1,7 +1,7 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -84,6 +84,11 @@ export const spec = { adRequests: [...adRequests], rtbData: handleEids(bidRequests) }; + + if (config.getConfig().debug) { + payload.dbg = true; + } + const payloadString = JSON.stringify(payload); return { method: 'POST', @@ -101,6 +106,10 @@ export const spec = { interpretResponse: function(serverResponse) { const bidResponses = []; + if (serverResponse.body.dbg && window.livewrapped && window.livewrapped.s2sDebug) { + window.livewrapped.s2sDebug(serverResponse.body.dbg); + } + serverResponse.body.ads.forEach(function(ad) { var bidResponse = { requestId: ad.bidId, diff --git a/modules/lunamediaBidAdapter.js b/modules/lunamediaBidAdapter.js index ee431deea1b..83be806af17 100755 --- a/modules/lunamediaBidAdapter.js +++ b/modules/lunamediaBidAdapter.js @@ -2,8 +2,8 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; const ADAPTER_VERSION = '1.0'; const BIDDER_CODE = 'lunamedia'; diff --git a/modules/marsmediaBidAdapter.js b/modules/marsmediaBidAdapter.js index b6dc7800dd9..2a2017c4b5c 100644 --- a/modules/marsmediaBidAdapter.js +++ b/modules/marsmediaBidAdapter.js @@ -16,7 +16,7 @@ function MarsmediaAdapter() { let SUPPORTED_VIDEO_API = [1, 2, 5]; let slotsToBids = {}; let that = this; - let version = '2.2'; + let version = '2.3'; this.isBidRequestValid = function (bid) { return !!(bid.params && bid.params.zoneId); @@ -221,6 +221,20 @@ function MarsmediaAdapter() { }; }; + this.onBidWon = function (bid) { + const cpm = bid.pbMg; + if (typeof bid.nurl !== 'undefined') { + bid.nurl = bid.nurl.replace( + /\$\{AUCTION_PRICE\}/, + cpm + ); + utils.triggerPixel(bid.nurl, null); + }; + const bidString = JSON.stringify(bid); + const encodedBuf = window.btoa(bidString); + utils.triggerPixel('https://ping-hqx-1.go2speed.media/notification/rtb/beacon/?bt=17&hb_j=' + encodedBuf, null); + }; + this.interpretResponse = function (serverResponse) { let responses = serverResponse.body || []; let bids = []; @@ -248,7 +262,8 @@ function MarsmediaAdapter() { creativeId: bid.crid, currency: 'USD', netRevenue: true, - ttl: 350 + ttl: 350, + nurl: bid.nurl }; if (bidRequest.mediaTypes && bidRequest.mediaTypes.video) { diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index 3da69be92eb..d31b62ca2dd 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -119,7 +119,7 @@ class Configure { init() { // Forces Logging % to 100% - let urlObj = utils.parseUrl(pageDetails.page); + let urlObj = URL.parseUrl(pageDetails.page); if (utils.deepAccess(urlObj, 'search.medianet_test') || urlObj.hostname === 'localhost') { this.loggingPercent = 100; this.ajaxState = CONFIG_PASS; @@ -143,7 +143,7 @@ class PageDetail { const twitterUrl = this._getUrlFromSelector('meta[name="twitter:url"]', 'content'); const refererInfo = getRefererInfo(); - this.domain = utils.parseUrl(refererInfo.referer).host; + this.domain = URL.parseUrl(refererInfo.referer).host; this.page = refererInfo.referer; this.is_top = refererInfo.reachedTop; this.referrer = this._getTopWindowReferrer(); @@ -208,6 +208,7 @@ class AdSlot { this.adext = adext; this.logged = false; this.targeting = undefined; + this.medianetPresent = 0; } getLoggingData() { @@ -216,7 +217,8 @@ class AdSlot { mediaTypes: this.mediaTypes && this.mediaTypes.join('|'), szs: this.bannerSizes.join('|'), tmax: this.tmax, - targ: JSON.stringify(this.targeting) + targ: JSON.stringify(this.targeting), + ismn: this.medianetPresent }, this.adext && {'adext': JSON.stringify(this.adext)}, ); @@ -259,6 +261,7 @@ class Bid { getLoggingData() { return { + adid: this.adId, pvnm: this.bidder, src: this.src, ogbdp: this.originalCpm, @@ -304,7 +307,8 @@ class Auction { ets: this.auctionEndTime - this.auctionInitTime, tts: this.setTargetingTime - this.auctionInitTime, wts: this.bidWonTime - this.auctionInitTime, - aucstatus: this.status + aucstatus: this.status, + acid: this.acid } } @@ -375,6 +379,7 @@ function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, us if (bidder === MEDIANET_BIDDER_CODE) { bidObj.crid = utils.deepAccess(bid, 'params.crid'); bidObj.pubcrid = utils.deepAccess(bid, 'params.crid'); + auctions[auctionId].adSlots[adUnitCode].medianetPresent = 1; } }); } @@ -393,8 +398,9 @@ function bidResponseHandler(bid) { Object.assign( bidObj, { cpm, width, height, mediaType, timeToRespond, dealId, creativeId }, - { adId, currency, originalCpm } + { adId, currency } ); + bidObj.originalCpm = originalCpm || cpm; let dfpbd = utils.deepAccess(bid, 'adserverTargeting.hb_pb'); if (!dfpbd) { let priceGranularity = getPriceGranularity(mediaType, bid); @@ -561,6 +567,34 @@ function firePixel(qs) { utils.triggerPixel(ENDPOINT + '&' + qs); } +class URL { + static parseUrl(url) { + let parsed = document.createElement('a'); + parsed.href = decodeURIComponent(url); + return { + hostname: parsed.hostname, + search: URL.parseQS(parsed.search || ''), + host: parsed.host || window.location.host + }; + } + static parseQS(query) { + return !query ? {} : query + .replace(/^\?/, '') + .split('&') + .reduce((acc, criteria) => { + let [k, v] = criteria.split('='); + if (/\[\]$/.test(k)) { + k = k.replace('[]', ''); + acc[k] = acc[k] || []; + acc[k].push(v); + } else { + acc[k] = v || ''; + } + return acc; + }, {}); + } +} + let medianetAnalytics = Object.assign(adapter({URL, analyticsType}), { getlogsQueue() { return logsQueue; diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index a7434cac6ab..edc34f8c9c3 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -135,6 +135,7 @@ function extParams(params, gdpr, uspConsent, userId) { let windowSize = spec.getWindowSize(); let gdprApplies = !!(gdpr && gdpr.gdprApplies); let uspApplies = !!(uspConsent); + let coppaApplies = !!(config.getConfig('coppa')); return Object.assign({}, { customer_id: params.cid }, { prebid_version: $$PREBID_GLOBAL$$.version }, @@ -142,6 +143,7 @@ function extParams(params, gdpr, uspConsent, userId) { (gdprApplies) && { gdpr_consent_string: gdpr.consentString || '' }, { usp_applies: uspApplies }, uspApplies && { usp_consent_string: uspConsent || '' }, + {coppa_applies: coppaApplies}, windowSize.w !== -1 && windowSize.h !== -1 && { screen: windowSize }, userId && { user_id: userId } ); diff --git a/modules/nextrollBidAdapter.js b/modules/nextrollBidAdapter.js index 81c48102bb4..0f273792154 100644 --- a/modules/nextrollBidAdapter.js +++ b/modules/nextrollBidAdapter.js @@ -2,7 +2,7 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'nextroll'; const BIDDER_ENDPOINT = 'https://d.adroll.com/bid/prebid/'; diff --git a/modules/platformioBidAdapter.js b/modules/platformioBidAdapter.js index ad25e262d56..314f738ef81 100644 --- a/modules/platformioBidAdapter.js +++ b/modules/platformioBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; const NATIVE_DEFAULTS = { TITLE_LEN: 100, diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 56dfe00c6a1..9476b7a76a3 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -8,10 +8,10 @@ import { VIDEO, NATIVE } from '../../src/mediaTypes.js'; import { processNativeAdUnitParams } from '../../src/native.js'; import { isValid } from '../../src/adapters/bidderFactory.js'; import events from '../../src/events.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { S2S_VENDORS } from './config.js'; import { ajax } from '../../src/ajax.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const getConfig = config.getConfig; diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 0accd48d003..9e9ed1e0d23 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -6,7 +6,7 @@ import events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import { getHook } from '../src/hook.js'; import { createBid } from '../src/bidfactory.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import { getRefererInfo } from '../src/refererDetection.js'; /** diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 63d0a6f54e5..91022d70df9 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -2,7 +2,7 @@ import * as utils from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'quantcast'; const DEFAULT_BID_FLOOR = 0.0000000001; diff --git a/modules/quantumdexBidAdapter.js b/modules/quantumdexBidAdapter.js new file mode 100644 index 00000000000..c2a130f789d --- /dev/null +++ b/modules/quantumdexBidAdapter.js @@ -0,0 +1,219 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +const BIDDER_CODE = 'quantumdex'; +const ENDPOINT = 'https://useast.quantumdex.io/auction/adapter'; +const USER_SYNC_URL = 'https://useast.quantumdex.io/usersync/adapter'; +var bySlotTargetKey = {}; +var bySlotSizesCount = {} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: ['banner', 'video'], + aliases: ['qde'], + isBidRequestValid: function (bid) { + if (!bid.params) { + return false; + } + if (!bid.params.siteId) { + return false; + } + if (!utils.deepAccess(bid, 'mediaTypes.banner') && !utils.deepAccess(bid, 'mediaTypes.video')) { + return false; + } + if (utils.deepAccess(bid, 'mediaTypes.banner')) { // Quantumdex does not support multi type bids, favor banner over video + if (!utils.deepAccess(bid, 'mediaTypes.banner.sizes')) { + // sizes at the banner is required. + return false; + } + } else if (utils.deepAccess(bid, 'mediaTypes.video')) { + if (!utils.deepAccess(bid, 'mediaTypes.video.playerSize')) { + // playerSize is required for instream adUnits. + return false; + } + } + return true; + }, + + buildRequests: function (validBidRequests, bidderRequest) { + var bids = JSON.parse(JSON.stringify(validBidRequests)) + const payload = {}; + + bids.forEach(bidReq => { + var targetKey = 0; + if (bySlotTargetKey[bidReq.adUnitCode] != undefined) { + targetKey = bySlotTargetKey[bidReq.adUnitCode]; + } else { + var biggestSize = _getBiggestSize(bidReq.sizes); + if (biggestSize) { + if (bySlotSizesCount[biggestSize] != undefined) { + bySlotSizesCount[biggestSize]++ + targetKey = bySlotSizesCount[biggestSize]; + } else { + bySlotSizesCount[biggestSize] = 0; + targetKey = 0 + } + } + } + bySlotTargetKey[bidReq.adUnitCode] = targetKey; + bidReq.targetKey = targetKey; + }); + + payload.device = {}; + payload.device.ua = navigator.userAgent; + payload.device.height = window.top.innerHeight; + payload.device.width = window.top.innerWidth; + payload.device.dnt = _getDoNotTrack(); + payload.device.language = navigator.language; + + payload.site = {}; + payload.site.id = bids[0].params.siteId; + payload.site.page = _extractTopWindowUrlFromBidderRequest(bidderRequest); + payload.site.referrer = _extractTopWindowReferrerFromBidderRequest(bidderRequest); + payload.site.hostname = window.top.location.hostname; + + // Apply GDPR parameters to request. + payload.gdpr = {}; + if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdpr.gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 'true' : 'false'; + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; + } + } + // Apply schain. + if (bids[0].schain) { + payload.schain = JSON.stringify(bids[0].schain) + } + // Apply us_privacy. + if (bidderRequest && bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } + + payload.bids = bids; + + return { + method: 'POST', + url: ENDPOINT, + data: payload, + withCredentials: true, + bidderRequests: bids + }; + }, + interpretResponse: function (serverResponse, bidRequest) { + const serverBody = serverResponse.body; + const serverBids = serverBody.bids; + // check overall response + if (!serverBody || typeof serverBody !== 'object') { + return []; + } + if (!serverBids || typeof serverBids !== 'object') { + return []; + } + + const bidResponses = []; + serverBids.forEach(bid => { + const bidResponse = { + requestId: bid.requestId, + cpm: bid.cpm, + width: bid.width, + height: bid.height, + creativeId: bid.creativeId, + dealId: bid.dealId, + currency: bid.currency, + netRevenue: bid.netRevenue, + ttl: bid.ttl, + mediaType: bid.mediaType + }; + if (bid.vastXml) { + bidResponse.vastXml = utils.replaceAuctionPrice(bid.vastXml, bid.cpm); + } else { + bidResponse.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); + } + bidResponses.push(bidResponse); + }); + return bidResponses; + }, + getUserSyncs: function (syncOptions, serverResponses) { + const syncs = []; + try { + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: USER_SYNC_URL + }); + } + if (syncOptions.pixelEnabled && serverResponses.length > 0) { + serverResponses[0].body.pixel.forEach(px => { + syncs.push({ + type: px.type, + url: px.url + }); + }); + } + } catch (e) { } + return syncs; + } +}; + +function _getBiggestSize(sizes) { + if (sizes.length <= 0) return false + var acreage = 0; + var index = 0; + for (var i = 0; i < sizes.length; i++) { + var currentAcreage = sizes[i][0] * sizes[i][1]; + if (currentAcreage >= acreage) { + acreage = currentAcreage; + index = i; + } + } + return sizes[index][0] + 'x' + sizes[index][1]; +} + +function _getDoNotTrack() { + if (window.top.doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack) { + if (window.top.doNotTrack == '1' || navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') { + return 1; + } else { + return 0; + } + } else { + return 0; + } +} + +/** + * Extracts the page url from given bid request or use the (top) window location as fallback + * + * @param {*} bidderRequest + * @returns {string} + */ +function _extractTopWindowUrlFromBidderRequest(bidderRequest) { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl')) { + return bidderRequest.refererInfo.canonicalUrl; + } + + try { + return window.top.location.href; + } catch (e) { + return window.location.href; + } +} + +/** + * Extracts the referrer from given bid request or use the (top) document referrer as fallback + * + * @param {*} bidderRequest + * @returns {string} + */ +function _extractTopWindowReferrerFromBidderRequest(bidderRequest) { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.referer')) { + return bidderRequest.refererInfo.referer; + } + + try { + return window.top.document.referrer; + } catch (e) { + return window.document.referrer; + } +} + +registerBidder(spec); diff --git a/modules/quantumdexBidAdapter.md b/modules/quantumdexBidAdapter.md new file mode 100644 index 00000000000..8c35ea8cb05 --- /dev/null +++ b/modules/quantumdexBidAdapter.md @@ -0,0 +1,56 @@ +# Overview + +``` +Module Name: Quantum Digital Exchange Bidder Adapter +Module Type: Bidder Adapter +Maintainer: ken@quantumdex.io +``` + +# Description + +Connects to Quantum Digital Exchange for bids. +Quantumdex bid adapter supports Banner and Video (Instream and Outstream) ads. + +# Test Parameters +``` +var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [ + { + bidder: 'quantumdex', + params: { + siteId: 'quantumdex-site-id', // siteId provided by Quantumdex + } + } + ] + } +]; +``` + +# Video Test Parameters +``` +var videoAdUnit = { + code: 'test-div', + sizes: [[640, 480]], + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream' + }, + }, + bids: [ + { + bidder: 'quantumdex', + params: { + siteId: 'quantumdex-site-id', // siteId provided by Quantumdex + } + } + ] +}; +``` \ No newline at end of file diff --git a/modules/rakutenBidAdapter/index.js b/modules/rakutenBidAdapter/index.js new file mode 100644 index 00000000000..e567509b3c1 --- /dev/null +++ b/modules/rakutenBidAdapter/index.js @@ -0,0 +1,80 @@ +import { registerBidder } from '../../src/adapters/bidderFactory.js'; +import { BANNER } from '../../src/mediaTypes.js'; +import { config } from '../../src/config.js'; +const BIDDER_CODE = 'rakuten'; +const ENDPOINT = 'https://s-bid.rmp.rakuten.com/h'; +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: bid => !!bid.params.adSpotId, + buildRequests: (validBidRequests, bidderRequest) => { + const bidRequests = []; + validBidRequests.forEach(bid => { + var _a, _b; + const params = bid.params; + bidRequests.push({ + method: 'GET', + url: config.getConfig('rakuten.endpoint') || ENDPOINT, + data: { + bi: bid.bidId, + t: params.adSpotId, + s: document.location.protocol, + ua: navigator.userAgent, + l: navigator.browserLanguage || + navigator.language, + d: document.domain, + tp: bidderRequest.refererInfo.stack[0] || window.location.href, + pp: bidderRequest.refererInfo.referer, + gdpr: ((_a = bidderRequest.gdprConsent) === null || _a === void 0 ? void 0 : _a.gdprApplies) ? 1 : 0, + ...((_b = bidderRequest.gdprConsent) === null || _b === void 0 ? void 0 : _b.consentString) && { + cd: bidderRequest.gdprConsent.consentString + }, + ...bidderRequest.uspConsent && { + ccpa: bidderRequest.uspConsent + } + } + }); + }); + return bidRequests; + }, + interpretResponse: (response, request) => { + const sb = response.body; + const bidResponses = []; + if (sb.cpm && sb.ad) { + bidResponses.push({ + requestId: sb.bid_id, + cpm: sb.cpm, + width: sb.width || 0, + height: sb.height || 0, + creativeId: sb.creative_id || 0, + dealId: sb.deal_id || '', + currency: sb.currency || 'USD', + netRevenue: (typeof sb.net_revenue === 'undefined') ? true : !!sb.net_revenue, + mediaType: BANNER, + ttl: sb.ttl, + ad: sb.ad + }); + } + return bidResponses; + }, + getUserSyncs: function (syncOptions, serverResponses) { + const syncs = []; + if (syncOptions.pixelEnabled && serverResponses[0].body !== undefined) { + const bidResponseObj = serverResponses[0].body; + if (!bidResponseObj) { + return []; + } + if (bidResponseObj.sync_urls && bidResponseObj.sync_urls.length > 0) { + bidResponseObj.sync_urls.forEach(syncUrl => { + if (syncUrl && syncUrl !== 'null' && syncUrl.length > 0) { + syncs.push({ + type: 'image', + url: syncUrl + }); + } + }); + } + } + return syncs; + } +}; +registerBidder(spec); diff --git a/modules/rakutenBidAdapter/index.md b/modules/rakutenBidAdapter/index.md new file mode 100644 index 00000000000..cc6aa68151d --- /dev/null +++ b/modules/rakutenBidAdapter/index.md @@ -0,0 +1,33 @@ +# Overview + +``` +Module Name: Rakuten Bidder Adapter +Module Type: Bidder Adapter +Maintainer: @snapwich +``` + +# Description + +Bid adapter for Rakuten RSSP + +Rakuten bid adapter supports Banner currently. + +# Test Parameters + +``` + var adUnits = [ + { + code: 'test-ad-div', + sizes: [[300, 250]], + mediaTypes: {banner: {}}, + bids: [ + { + bidder: 'rakuten', + params: { + adSpotId: 42 + } + } + ] + } + ]; +``` diff --git a/modules/roxotAnalyticsAdapter.js b/modules/roxotAnalyticsAdapter.js index 0e9cda9b6bc..b6857d69f45 100644 --- a/modules/roxotAnalyticsAdapter.js +++ b/modules/roxotAnalyticsAdapter.js @@ -1,7 +1,7 @@ import adapter from '../src/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import {ajaxBuilder} from '../src/ajax.js'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index b0d7c0e04de..ca760ca49eb 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -1,7 +1,7 @@ import * as utils from '../src/utils.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; const BIDDER_CODE = 'rtbhouse'; const REGIONS = ['prebid-eu', 'prebid-us', 'prebid-asia']; diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index 2831d793aad..d0eb8c6a589 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -5,7 +5,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { loadExternalScript } from '../src/adloader.js'; -const PROD_ENDPOINT = 'https://bs1.showheroes.com/api/v1/bid'; +const PROD_ENDPOINT = 'https://bs.showheroes.com/api/v1/bid'; const STAGE_ENDPOINT = 'https://bid-service.stage.showheroes.com/api/v1/bid'; const PROD_PUBLISHER_TAG = 'https://static.showheroes.com/publishertag.js'; const STAGE_PUBLISHER_TAG = 'https://pubtag.stage.showheroes.com/publishertag.js'; @@ -38,6 +38,7 @@ export const spec = { const isNativeRender = utils.deepAccess(validBidRequests[0], 'renderer'); const outstreamOptions = utils.deepAccess(validBidRequests[0], 'params.outstreamOptions'); const isBanner = !!validBidRequests[0].mediaTypes.banner || (isOutstream && !(isCustomRender || isNativeRender || isNodeRender)); + const defaultSchain = validBidRequests[0].schain || {}; validBidRequests.forEach((bid) => { const videoSizes = getVideoSizes(bid); @@ -76,6 +77,7 @@ export const spec = { height: size[1] }, params: bid.params, + schain: bid.schain || defaultSchain, }; }; @@ -215,7 +217,7 @@ function outstreamRender(bid) { return; } - const slot = utils.getBidIdParameter('slot', bid.renderer.config); + const slot = utils.getBidIdParameter('slot', bid.renderer.config) || bid.adUnitCode; if (slot && window.document.getElementById(slot)) { window.document.getElementById(slot).appendChild(embedCode); } else if (slot) { @@ -274,7 +276,7 @@ function getVideoSizes(bidRequest) { } function getBannerSizes(bidRequest) { - return formatSizes(utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes') || bidRequest.sizes || []); + return formatSizes(utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes') || []); } function formatSizes(sizes) { diff --git a/modules/sigmoidAnalyticsAdapter.js b/modules/sigmoidAnalyticsAdapter.js index 88bb5eb23a9..303fbbc8995 100644 --- a/modules/sigmoidAnalyticsAdapter.js +++ b/modules/sigmoidAnalyticsAdapter.js @@ -1,6 +1,6 @@ /* Sigmoid Analytics Adapter for prebid.js v1.1.0-pre Updated : 2018-03-28 */ -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import adapter from '../src/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 98effb301fb..339a598bae5 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -6,7 +6,7 @@ import * as utils from '../src/utils.js'; import { processNativeAdUnitParams } from '../src/native.js'; import { adunitCounter } from '../src/adUnits.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { getHook } from '../src/hook.js'; import { adUnitSetupChecks diff --git a/modules/somoBidAdapter.js b/modules/somoBidAdapter.js index 298b1c3050e..c6e31790005 100644 --- a/modules/somoBidAdapter.js +++ b/modules/somoBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; const VIDEO_TARGETING = ['mimes', 'minduration', 'maxduration', 'protocols', diff --git a/modules/sovrnAnalyticsAdapter.js b/modules/sovrnAnalyticsAdapter.js index 8a712680dac..0d2576edb05 100644 --- a/modules/sovrnAnalyticsAdapter.js +++ b/modules/sovrnAnalyticsAdapter.js @@ -4,8 +4,8 @@ import CONSTANTS from '../src/constants.json' import {ajaxBuilder} from '../src/ajax.js' import * as utils from '../src/utils.js' import {config} from '../src/config.js' -import find from 'core-js/library/fn/array/find.js' -import includes from 'core-js/library/fn/array/includes.js' +import find from 'core-js-pure/features/array/find.js' +import includes from 'core-js-pure/features/array/includes.js' const ajax = ajaxBuilder(0) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index ae2fd91cb6f..2ca2aeeae82 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -3,7 +3,8 @@ import { getAdUnitSizes, logWarn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import {config} from '../src/config.js'; const BID_HOST = 'https://prebid.technoratimedia.com'; const USER_SYNC_HOST = 'https://ad-cdn.technoratimedia.com'; @@ -156,7 +157,9 @@ export const spec = { }; if (isVideo) { const [, uuid] = nurl.match(/ID=([^&]*)&?/); - bidObj.videoCacheKey = encodeURIComponent(uuid); + if (!config.getConfig('cache.url')) { + bidObj.videoCacheKey = encodeURIComponent(uuid); + } bidObj.vastUrl = nurl; } bids.push(bidObj); diff --git a/modules/userId/index.js b/modules/userId/index.js index 4ab39493b92..200ae8d9e4b 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -108,7 +108,7 @@ * @property {(function|undefined)} callback - function that will return an id */ -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import {config} from '../../src/config.js'; import events from '../../src/events.js'; import * as utils from '../../src/utils.js'; diff --git a/modules/viewdeosDXBidAdapter.js b/modules/viewdeosDXBidAdapter.js index fb028fca8c8..52894ef2dd9 100644 --- a/modules/viewdeosDXBidAdapter.js +++ b/modules/viewdeosDXBidAdapter.js @@ -2,7 +2,7 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {VIDEO, BANNER} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; -import findIndex from 'core-js/library/fn/array/find-index.js'; +import findIndex from 'core-js-pure/features/array/find-index.js'; const URL = 'https://ghb.sync.viewdeos.com/auction/'; const OUTSTREAM_SRC = 'https://player.sync.viewdeos.com/outstream-unit/2.01/outstream.min.js'; diff --git a/modules/widespaceBidAdapter.js b/modules/widespaceBidAdapter.js index 5165b00a98c..3cea5ca57a1 100644 --- a/modules/widespaceBidAdapter.js +++ b/modules/widespaceBidAdapter.js @@ -4,8 +4,8 @@ import { parseQueryStringParameters, parseSizesInput } from '../src/utils.js'; -import includes from 'core-js/library/fn/array/includes.js'; -import find from 'core-js/library/fn/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; import { getStorageManager } from '../src/storageManager.js'; export const storage = getStorageManager(); diff --git a/modules/windtalkerBidAdapter.js b/modules/windtalkerBidAdapter.js index 0c0ef571954..512cc7f839b 100644 --- a/modules/windtalkerBidAdapter.js +++ b/modules/windtalkerBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; const NATIVE_DEFAULTS = { TITLE_LEN: 100, diff --git a/modules/xhbBidAdapter.js b/modules/xhbBidAdapter.js index 0f7669f6eeb..55e5495f505 100644 --- a/modules/xhbBidAdapter.js +++ b/modules/xhbBidAdapter.js @@ -2,8 +2,8 @@ import { Renderer } from '../src/Renderer.js'; import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; const BIDDER_CODE = 'xhb'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index e1e491cdce9..6cfa0c1a548 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' -import find from 'core-js/library/fn/array/find.js' +import find from 'core-js-pure/features/array/find.js' import { VIDEO, BANNER } from '../src/mediaTypes.js' import { Renderer } from '../src/Renderer.js' diff --git a/package-lock.json b/package-lock.json index e8387d41383..146fb49c435 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3035,6 +3035,14 @@ "lodash": "^4.17.4", "mkdirp": "^0.5.1", "source-map-support": "^0.4.15" + }, + "dependencies": { + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", + "dev": true + } } }, "babel-runtime": { @@ -3044,6 +3052,13 @@ "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + } } }, "babel-template": { @@ -4359,9 +4374,9 @@ } }, "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==" }, "core-js-compat": { "version": "3.6.4", @@ -4381,6 +4396,11 @@ } } }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", diff --git a/package.json b/package.json index 3b581fec1cb..464e1515877 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "3.19.0-pre", + "version": "3.20.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { @@ -96,7 +96,7 @@ }, "dependencies": { "babel-plugin-transform-object-assign": "^6.22.0", - "core-js": "^2.4.1", + "core-js": "^3.0.0", "criteo-direct-rsa-validate": "^1.1.0", "crypto-js": "^3.3.0", "deep-equal": "^1.0.1", @@ -106,6 +106,7 @@ "fun-hooks": "^0.9.8", "jsencrypt": "^3.0.0-rc.1", "just-clone": "^1.0.2", - "live-connect-js": "1.1.1" + "live-connect-js": "1.1.1", + "core-js-pure": "^3.6.5" } } diff --git a/src/Renderer.js b/src/Renderer.js index 2efe717eca4..b4a912d5714 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -1,6 +1,6 @@ import { loadExternalScript } from './adloader.js'; import * as utils from './utils.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const moduleCode = 'outstream'; /** diff --git a/src/adapterManager.js b/src/adapterManager.js index f658a36aeda..2108bb7a4f6 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -7,8 +7,8 @@ import { newBidder } from './adapters/bidderFactory.js'; import { ajaxBuilder } from './ajax.js'; import { config, RANDOM } from './config.js'; import { hook } from './hook.js'; -import includes from 'core-js/library/fn/array/includes.js'; -import find from 'core-js/library/fn/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; import { adunitCounter } from './adUnits.js'; import { getRefererInfo } from './refererDetection.js'; diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index af3ab5ed8c1..42aa91fa74a 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -7,7 +7,7 @@ import { nativeBidIsValid } from '../native.js'; import { isValidVideoBid } from '../video.js'; import CONSTANTS from '../constants.json'; import events from '../events.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { ajax } from '../ajax.js'; import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest, flatten, uniques, timestamp, deepAccess, isArray } from '../utils.js'; import { ADPOD } from '../mediaTypes.js'; diff --git a/src/adloader.js b/src/adloader.js index ce21d763797..1c18ce82b16 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -1,4 +1,4 @@ -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import * as utils from './utils.js'; const _requestCache = {}; diff --git a/src/auction.js b/src/auction.js index d953763cea8..6166e59bbf0 100644 --- a/src/auction.js +++ b/src/auction.js @@ -65,7 +65,7 @@ import { Renderer } from './Renderer.js'; import { config } from './config.js'; import { userSync } from './userSync.js'; import { hook } from './hook.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import { OUTSTREAM } from './video.js'; import { VIDEO } from './mediaTypes.js'; diff --git a/src/auctionManager.js b/src/auctionManager.js index bf95f0ab05e..3d4bd0afe99 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -18,7 +18,7 @@ import { uniques, flatten, logWarn } from './utils.js'; import { newAuction, getStandardBidderSettings, AUCTION_COMPLETED } from './auction.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const CONSTANTS = require('./constants.json'); diff --git a/src/config.js b/src/config.js index af0fd12de88..3284be52296 100644 --- a/src/config.js +++ b/src/config.js @@ -13,12 +13,12 @@ */ import { isValidPriceConfig } from './cpmBucketManager.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; -import Set from 'core-js/library/fn/set.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import Set from 'core-js-pure/features/set'; import { mergeDeep } from './utils.js'; -const from = require('core-js/library/fn/array/from.js'); +const from = require('core-js-pure/features/array/from.js'); const utils = require('./utils.js'); const CONSTANTS = require('./constants.json'); diff --git a/src/cpmBucketManager.js b/src/cpmBucketManager.js index 2f0ae4312e9..a6b76cc38e2 100644 --- a/src/cpmBucketManager.js +++ b/src/cpmBucketManager.js @@ -1,4 +1,4 @@ -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; const utils = require('./utils.js'); const _defaultPrecision = 2; diff --git a/src/native.js b/src/native.js index 0d8461a5b6f..e41d7740ffa 100644 --- a/src/native.js +++ b/src/native.js @@ -1,5 +1,5 @@ import { deepAccess, getBidRequest, getKeyByValue, insertHtmlIntoIframe, logError, triggerPixel } from './utils.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; const CONSTANTS = require('./constants.json'); diff --git a/src/prebid.js b/src/prebid.js index f45a15d5f19..1710849ba92 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -9,7 +9,7 @@ import { auctionManager } from './auctionManager.js'; import { targeting } from './targeting.js'; import { hook } from './hook.js'; import { sessionLoader } from './debugging.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { adunitCounter } from './adUnits.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; import { createBid } from './bidfactory.js'; diff --git a/src/secureCreatives.js b/src/secureCreatives.js index 36e5efe22cd..060a30b0a98 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -8,9 +8,9 @@ import { fireNativeTrackers, getAssetMessage } from './native.js'; import { EVENTS } from './constants.json'; import { logWarn, replaceAuctionPrice } from './utils.js'; import { auctionManager } from './auctionManager.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; const BID_WON = EVENTS.BID_WON; diff --git a/src/sizeMapping.js b/src/sizeMapping.js index ff54ff8f251..313da3f422a 100644 --- a/src/sizeMapping.js +++ b/src/sizeMapping.js @@ -1,6 +1,6 @@ import { config } from './config.js'; import {logWarn, isPlainObject, deepAccess, deepClone, getWindowTop} from './utils.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; let sizeConfig = []; diff --git a/src/storageManager.js b/src/storageManager.js index 75ad10908dc..0d88a8ccea1 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -1,6 +1,6 @@ import { hook } from './hook.js'; import * as utils from './utils.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; const moduleTypeWhiteList = ['core', 'prebid-module']; diff --git a/src/targeting.js b/src/targeting.js index f843dd13a6a..45c098554a5 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -4,7 +4,7 @@ import { NATIVE_TARGETING_KEYS } from './native.js'; import { auctionManager } from './auctionManager.js'; import { sizeSupported } from './sizeMapping.js'; import { ADPOD } from './mediaTypes.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; const utils = require('./utils.js'); var CONSTANTS = require('./constants.json'); diff --git a/src/userSync.js b/src/userSync.js index 5da22254f2c..fceeb1d722d 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -1,6 +1,6 @@ import * as utils from './utils.js'; import { config } from './config.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { getCoreStorageManager } from './storageManager.js'; export const USERSYNC_DEFAULT_CONFIG = { diff --git a/src/utils.js b/src/utils.js index e7b937a970d..38ab6d88e12 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,8 +2,8 @@ import { config } from './config.js'; import clone from 'just-clone'; import deepequal from 'deep-equal'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; const CONSTANTS = require('./constants.json'); diff --git a/src/video.js b/src/video.js index c3deb73ad4d..befeb2ded39 100644 --- a/src/video.js +++ b/src/video.js @@ -1,7 +1,7 @@ import adapterManager from './adapterManager.js'; import { getBidRequest, deepAccess, logError } from './utils.js'; import { config } from '../src/config.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { hook } from './hook.js'; const VIDEO_MEDIA_TYPE = 'video'; diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 3ec3d67e448..35a29727614 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -1,13 +1,18 @@ -import { getKeyValueTargetingPairs, auctionCallbacks, AUCTION_COMPLETED } from 'src/auction.js'; +import { + getKeyValueTargetingPairs, + auctionCallbacks, + AUCTION_COMPLETED, + adjustBids, + getMediaTypeGranularity, +} from 'src/auction.js'; import CONSTANTS from 'src/constants.json'; -import { adjustBids, getMediaTypeGranularity } from 'src/auction.js'; import * as auctionModule from 'src/auction.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; import { createBid } from 'src/bidfactory.js'; import { config } from 'src/config.js'; import * as store from 'src/videoCache.js'; import * as ajaxLib from 'src/ajax.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; import { server } from 'test/mocks/xhr.js'; var assert = require('assert'); diff --git a/test/spec/e2e/longform/basic_w_bidderSettings.spec.js b/test/spec/e2e/longform/basic_w_bidderSettings.spec.js index 06413fb809a..684f5acab17 100644 --- a/test/spec/e2e/longform/basic_w_bidderSettings.spec.js +++ b/test/spec/e2e/longform/basic_w_bidderSettings.spec.js @@ -1,4 +1,4 @@ -const includes = require('core-js/library/fn/array/includes'); +const includes = require('core-js-pure/features/array/includes.js'); const expect = require('chai').expect; const testServer = require('../../../helpers/testing-utils'); diff --git a/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js b/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js index 82310738246..175e5d041d9 100644 --- a/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js +++ b/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js @@ -1,4 +1,4 @@ -const includes = require('core-js/library/fn/array/includes'); +const includes = require('core-js-pure/features/array/includes.js'); const expect = require('chai').expect; const testServer = require('../../../helpers/testing-utils'); diff --git a/test/spec/e2e/longform/basic_w_priceGran.spec.js b/test/spec/e2e/longform/basic_w_priceGran.spec.js index 696b7fa3359..294557193b4 100644 --- a/test/spec/e2e/longform/basic_w_priceGran.spec.js +++ b/test/spec/e2e/longform/basic_w_priceGran.spec.js @@ -1,4 +1,4 @@ -const includes = require('core-js/library/fn/array/includes'); +const includes = require('core-js-pure/features/array/includes.js'); const expect = require('chai').expect; const testServer = require('../../../helpers/testing-utils'); diff --git a/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js b/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js index 224ff1cbc34..d92e5361ae3 100644 --- a/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js +++ b/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js @@ -1,4 +1,4 @@ -const includes = require('core-js/library/fn/array/includes'); +const includes = require('core-js-pure/features/array/includes.js'); const expect = require('chai').expect; const testServer = require('../../../helpers/testing-utils'); diff --git a/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js b/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js index 95237366d0e..de3e334e755 100644 --- a/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js +++ b/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js @@ -1,4 +1,4 @@ -const includes = require('core-js/library/fn/array/includes'); +const includes = require('core-js-pure/features/array/includes.js'); const expect = require('chai').expect; const testServer = require('../../../helpers/testing-utils'); diff --git a/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js b/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js index 6b628067138..849eb5ade4d 100644 --- a/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js +++ b/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js @@ -1,4 +1,4 @@ -const includes = require('core-js/library/fn/array/includes'); +const includes = require('core-js-pure/features/array/includes.js'); const expect = require('chai').expect; const testServer = require('../../../helpers/testing-utils'); diff --git a/test/spec/modules/adprimeBidAdapter_spec.js b/test/spec/modules/adprimeBidAdapter_spec.js new file mode 100644 index 00000000000..7524665e33f --- /dev/null +++ b/test/spec/modules/adprimeBidAdapter_spec.js @@ -0,0 +1,270 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/adprimeBidAdapter.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; + +describe('AdprimebBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'adprime', + params: { + placementId: 0, + traffic: BANNER + } + }; + + const bidderRequest = { + refererInfo: { + referer: 'test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and placementId parameters present', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + delete bid.params.placementId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid], bidderRequest); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://delta.adprime.com/?c=o&m=multi'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); + expect(placement.placementId).to.equal(0); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + bidderRequest.gdprConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.width).to.equal(300); + expect(dataItem.height).to.equal(250); + expect(dataItem.ad).to.equal('Test'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); +}); diff --git a/test/spec/modules/bluebillywigBidAdapter_spec.js b/test/spec/modules/bluebillywigBidAdapter_spec.js new file mode 100644 index 00000000000..fcf3e16ad8b --- /dev/null +++ b/test/spec/modules/bluebillywigBidAdapter_spec.js @@ -0,0 +1,898 @@ +import { expect } from 'chai'; +import { spec } from 'modules/bluebillywigBidAdapter.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; +import { auctionManager } from 'src/auctionManager.js'; +import { deepClone, deepAccess } from 'src/utils.js'; +import { config } from 'src/config.js'; +import { VIDEO } from 'src/mediaTypes.js'; + +const BB_CONSTANTS = { + BIDDER_CODE: 'bluebillywig', + AUCTION_URL: '$$URL_STARTpbs.bluebillywig.com/openrtb2/auction?pub=$$PUBLICATION', + SYNC_URL: '$$URL_STARTpbs.bluebillywig.com/static/cookie-sync.html?pub=$$PUBLICATION', + RENDERER_URL: 'https://$$PUBLICATION.bbvms.com/r/$$RENDERER.js', + DEFAULT_TIMEOUT: 5000, + DEFAULT_TTL: 300, + DEFAULT_WIDTH: 768, + DEFAULT_HEIGHT: 432, + DEFAULT_NET_REVENUE: true +}; + +describe('BlueBillywigAdapter', () => { + describe('isBidRequestValid', () => { + const baseValidBid = { + bidder: BB_CONSTANTS.BIDDER_CODE, + params: { + accountId: 123, + publicationName: 'bbprebid.dev', + rendererCode: 'glorious_renderer', + connections: [ BB_CONSTANTS.BIDDER_CODE ], + bluebillywig: {} + }, + mediaTypes: { + video: { + context: 'outstream' + } + } + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(baseValidBid)).to.equal(true); + }); + + it('should return false when publicationName is missing', () => { + const bid = deepClone(baseValidBid); + delete bid.params.publicationName; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when publicationName is not a string', () => { + const bid = deepClone(baseValidBid); + + bid.params.publicationName = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.publicationName = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.publicationName = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.publicationName = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when publicationName is formatted poorly', () => { + const bid = deepClone(baseValidBid); + + bid.params.publicationName = 'bb.'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.publicationName = 'bb-test'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.publicationName = '?'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when renderer is not specified', () => { + const bid = deepClone(baseValidBid); + + delete bid.params.rendererCode; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when renderer is not a string', () => { + const bid = deepClone(baseValidBid); + + bid.params.rendererCode = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererCode = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererCode = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererCode = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when renderer is formatted poorly', () => { + const bid = deepClone(baseValidBid); + + bid.params.rendererCode = 'bb.'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererCode = 'bb-test'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererCode = '?'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when accountId is not specified', () => { + const bid = deepClone(baseValidBid); + + delete bid.params.accountId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when connections is not specified', () => { + const bid = deepClone(baseValidBid); + + delete bid.params.connections; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when connections is not an array', () => { + const bid = deepClone(baseValidBid); + + bid.params.connections = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.connections = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.connections = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.connections = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.connections = 'string'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when a connection is missing', () => { + const bid = deepClone(baseValidBid); + + bid.params.connections.push('potatoes'); + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.connections.pop(); + + delete bid.params[BB_CONSTANTS.BIDDER_CODE]; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail if bid has no mediaTypes', () => { + const bid = deepClone(baseValidBid); + + delete bid.mediaTypes; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail if bid has no mediaTypes.video', () => { + const bid = deepClone(baseValidBid); + + delete bid.mediaTypes[VIDEO]; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail if bid has no mediaTypes.video.context', () => { + const bid = deepClone(baseValidBid); + + delete bid.mediaTypes[VIDEO].context; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail if mediaTypes.video.context is not "outstream"', () => { + const bid = deepClone(baseValidBid); + + bid.mediaTypes[VIDEO].context = 'instream'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + const publicationName = 'bbprebid.dev'; + const rendererCode = 'glorious_renderer'; + + const baseValidBid = { + bidder: BB_CONSTANTS.BIDDER_CODE, + params: { + accountId: 123, + publicationName: publicationName, + rendererCode: rendererCode, + connections: [ BB_CONSTANTS.BIDDER_CODE ], + bluebillywig: {} + }, + mediaTypes: { + video: { + context: 'outstream' + } + } + }; + + const baseValidBidRequests = [baseValidBid]; + + const validBidderRequest = { + auctionId: '12abc345-67d8-9012-e345-6f78901a2b34', + auctionStart: 1585918458868, + bidderCode: BB_CONSTANTS.BIDDER_CODE, + bidderRequestId: '1a2345b67c8d9e0', + bids: [{ + adUnitCode: 'ad-unit-test', + auctionId: '12abc345-67d8-9012-e345-6f78901a2b34', + bidId: '1234ab567c89de0', + bidRequestsCount: 1, + bidder: BB_CONSTANTS.BIDDER_CODE, + bidderRequestId: '1a2345b67c8d9e0', + params: baseValidBid.params, + sizes: [[768, 432], [640, 480], [630, 360]], + transactionId: '2b34c5de-f67a-8901-bcd2-34567efabc89' + }], + start: 11585918458869, + timeout: 3000 + }; + + it('sends bid request to AUCTION_URL via POST', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + expect(request.url).to.equal(`https://pbs.bluebillywig.com/openrtb2/auction?pub=${publicationName}`); + expect(request.method).to.equal('POST'); + }); + + it('sends data as a string', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + expect(request.data).to.be.a('string'); + }); + + it('sends all bid parameters', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + + it('builds the base request properly', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.id).to.equal(validBidderRequest.auctionId); + expect(payload.source).to.be.an('object'); + expect(payload.source.tid).to.equal(validBidderRequest.auctionId); + expect(payload.tmax).to.equal(BB_CONSTANTS.DEFAULT_TIMEOUT); + expect(payload.imp).to.be.an('array'); + expect(payload.test).to.be.a('number'); + expect(payload).to.have.nested.property('ext.prebid.targeting'); + expect(payload.ext.prebid.targeting).to.be.an('object'); + expect(payload.ext.prebid.targeting.includewinners).to.equal(true); + expect(payload.ext.prebid.targeting.includebidderkeys).to.equal(false); + }); + + it('adds an impression to the payload', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.imp.length).to.equal(1); + }); + + it('adds connections to ext', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.imp[0].ext).to.have.all.keys(['bluebillywig']); + }); + + it('adds gdpr when present', () => { + const newValidBidderRequest = deepClone(validBidderRequest); + newValidBidderRequest.gdprConsent = { + consentString: 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + gdprApplies: true + }; + + const request = spec.buildRequests(baseValidBidRequests, newValidBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('regs.ext.gdpr'); + expect(payload.regs.ext.gdpr).to.be.a('number'); + expect(payload.regs.ext.gdpr).to.equal(1); + expect(payload).to.have.nested.property('user.ext.consent'); + expect(payload.user.ext.consent).to.equal(newValidBidderRequest.gdprConsent.consentString); + }); + + it('sets gdpr to 0 when explicitly gdprApplies: false', () => { + const newValidBidderRequest = deepClone(validBidderRequest); + newValidBidderRequest.gdprConsent = { + gdprApplies: false + }; + + const request = spec.buildRequests(baseValidBidRequests, newValidBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('regs.ext.gdpr'); + expect(payload.regs.ext.gdpr).to.be.a('number'); + expect(payload.regs.ext.gdpr).to.equal(0); + }); + + it('adds usp_consent when present', () => { + const newValidBidderRequest = deepClone(validBidderRequest); + newValidBidderRequest.uspConsent = '1YYY'; + + const request = spec.buildRequests(baseValidBidRequests, newValidBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('regs.ext.us_privacy'); + expect(payload.regs.ext.us_privacy).to.equal(newValidBidderRequest.uspConsent); + }); + + it('sets coppa to 1 when specified in config', () => { + config.setConfig({'coppa': true}); + + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('regs.coppa'); + expect(payload.regs.coppa).to.equal(1); + + config.resetConfig(); + }); + + it('does not set coppa when disabled in the config', () => { + config.setConfig({'coppa': false}); + + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(deepAccess(payload, 'regs.coppa')).to.be.undefined; + + config.resetConfig(); + }); + + it('does not set coppa when not specified in config', () => { + config.resetConfig(); + + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(deepAccess(payload, 'regs.coppa')).to.be.undefined; + }); + + it('should add window size to request by default', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('device.w'); + expect(payload).to.have.nested.property('device.h'); + expect(payload.device.w).to.be.a('number'); + expect(payload.device.h).to.be.a('number'); + }); + + it('should add app when specified in config', () => { + config.setConfig({ app: { bundle: 'org.prebid.mobile.demoapp', domain: 'prebid.org' } }); + + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.property('app'); + expect(payload).to.have.nested.property('app.bundle'); + expect(payload).to.have.nested.property('app.domain'); + expect(payload.app.bundle).to.equal('org.prebid.mobile.demoapp'); + expect(payload.app.domain).to.equal('prebid.org'); + + config.resetConfig(); + }); + + it('should add referrerInfo as site when no app is set', () => { + const newValidBidderRequest = deepClone(validBidderRequest); + + newValidBidderRequest.refererInfo = { referer: 'https://www.bluebillywig.com' }; + + const request = spec.buildRequests(baseValidBidRequests, newValidBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('site.page'); + expect(payload.site.page).to.equal('https://www.bluebillywig.com'); + }); + + it('should not add referrerInfo as site when app is set', () => { + config.setConfig({ app: { bundle: 'org.prebid.mobile.demoapp', domain: 'prebid.org' } }); + + const newValidBidderRequest = deepClone(validBidderRequest); + newValidBidderRequest.refererInfo = { referer: 'https://www.bluebillywig.com' }; + + const request = spec.buildRequests(baseValidBidRequests, newValidBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.site).to.be.undefined; + config.resetConfig(); + }); + + it('should add device size to request when specified in config', () => { + config.setConfig({ device: { w: 1, h: 1 } }); + + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('device.w'); + expect(payload).to.have.nested.property('device.h'); + expect(payload.device.w).to.be.a('number'); + expect(payload.device.h).to.be.a('number'); + expect(payload.device.w).to.equal(1); + expect(payload.device.h).to.equal(1); + + config.resetConfig(); + }); + + it('should set schain on the request when set on config', () => { + const schain = { + validation: 'lax', + config: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } + }; + + const newBaseValidBidRequests = deepClone(baseValidBidRequests); + newBaseValidBidRequests[0].schain = schain; + + const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('source.ext.schain'); + expect(payload.source.ext.schain).to.deep.equal(schain); + }); + + it('should add currency when specified on the config', () => { + config.setConfig({ currency: { adServerCurrency: 'USD' } }); + + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.property('cur'); + expect(payload.cur).to.eql(['USD']); // NB not equal, eql to check for same array because [1] === [1] fails normally + + config.resetConfig(); + }); + + it('should also take in array for currency on the config', () => { + config.setConfig({ currency: { adServerCurrency: ['USD', 'PHP'] } }); + + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.property('cur'); + expect(payload.cur).to.eql(['USD']); // NB not equal, eql to check for same array because [1] === [1] fails normally + + config.resetConfig(); + }); + + it('should not set cur when currency is not specified on the config', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.cur).to.be.undefined; + }); + + it('should set user ids when present', () => { + const userId = { tdid: 123 }; + + const newBaseValidBidRequests = deepClone(baseValidBidRequests); + newBaseValidBidRequests[0].userId = { criteoId: 'sample-userid' }; + + const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('user.ext.eids'); + expect(payload.user.ext.eids).to.be.an('array'); + expect(payload.user.ext.eids.length).to.equal(1); + }); + + it('should not set user ids when none present', () => { + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(deepAccess(payload, 'user.ext.eids')).to.be.undefined; + }); + + it('should set digitrust when present on bid', () => { + const digiTrust = {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}; + + const newBaseValidBidRequests = deepClone(baseValidBidRequests); + newBaseValidBidRequests[0].userId = { digitrustid: digiTrust }; + + const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(payload).to.have.nested.property('user.ext.digitrust'); + expect(payload.user.ext.digitrust.id).to.equal(digiTrust.data.id); + expect(payload.user.ext.digitrust.keyv).to.equal(digiTrust.data.keyv); + }); + + it('should not set digitrust when opted out', () => { + const digiTrust = {data: {id: 'DTID', keyv: 4, privacy: {optout: true}, producer: 'ABC', version: 2}}; + + const newBaseValidBidRequests = deepClone(baseValidBidRequests); + newBaseValidBidRequests[0].userId = { digitrustid: digiTrust }; + + const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(deepAccess(payload, 'user.ext.digitrust')).to.be.undefined; + }); + }); + describe('interpretResponse', () => { + const publicationName = 'bbprebid.dev'; + const rendererCode = 'glorious_renderer'; + + const baseValidBid = { + bidder: BB_CONSTANTS.BIDDER_CODE, + params: { + accountId: 123, + publicationName: publicationName, + rendererCode: rendererCode, + connections: [ BB_CONSTANTS.BIDDER_CODE ], + bluebillywig: {} + }, + mediaTypes: { + video: { + context: 'outstream' + } + } + }; + + const baseValidBidRequests = [baseValidBid]; + + const validBidderRequest = { + auctionId: '12abc345-67d8-9012-e345-6f78901a2b34', + auctionStart: 1585918458868, + bidderCode: BB_CONSTANTS.BIDDER_CODE, + bidderRequestId: '1a2345b67c8d9e0', + bids: [{ + adUnitCode: 'ad-unit-test', + auctionId: '12abc345-67d8-9012-e345-6f78901a2b34', + bidId: '1234ab567c89de0', + bidRequestsCount: 1, + bidder: BB_CONSTANTS.BIDDER_CODE, + bidderRequestId: '1a2345b67c8d9e0', + params: baseValidBid.params, + sizes: [[768, 432], [640, 480], [630, 360]], + transactionId: '2b34c5de-f67a-8901-bcd2-34567efabc89' + }], + start: 11585918458869, + timeout: 3000 + }; + + const validResponse = { + id: 'a12abc345-67d8-9012-e345-6f78901a2b34', + seatbid: [ + { + bid: [ + { + id: '1', + impid: '1234ab567c89de0', + price: 1, + adm: '\r\nBB Adserver00:00:51', + adid: '67069817', + adomain: [ + 'bluebillywig.com' + ], + cid: '3535', + crid: '67069817', + w: 1, + h: 1, + publicationName: 'bbprebid', + accountId: 123, + ext: { + prebid: { + targeting: { + hb_bidder: 'bluebillywig', + hb_pb: '1.00', + hb_size: '1x1' + }, + type: 'video' + }, + bidder: { + prebid: { + targeting: { + hb_bidder: 'bluebillywig', + hb_pb: '10.00', + hb_size: '1x1' + }, + type: 'video', + video: { + duration: 51, + primary_category: '' + } + }, + bidder: { + bluebillywig: { + brand_id: 1, + auction_id: 1, + bid_ad_type: 1, + creative_info: { + video: { + duration: 51, + mimes: [ + 'video/x-flv', + 'video/mp4', + 'video/webm' + ] + } + } + } + } + } + } + } + ], + seat: 'bluebillywig' + } + ], + cur: 'USD', + ext: { + responsetimemillis: { + bluebillywig: 0 + }, + tmaxrequest: 5000 + } + }; + + const serverResponse = { body: validResponse }; + + it('should build bid array', () => { + const response = deepClone(serverResponse); + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', () => { + const response = deepClone(serverResponse); + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + const bid = result[0]; + + // BB_HELPERS.transformRTBToPrebidProps + expect(bid.cpm).to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(bid.bidId).to.equal(serverResponse.body.seatbid[0].bid[0].impid); + expect(bid.requestId).to.equal(serverResponse.body.seatbid[0].bid[0].impid); + expect(bid.width).to.equal(serverResponse.body.seatbid[0].bid[0].w || BB_CONSTANTS.DEFAULT_WIDTH); + expect(bid.height).to.equal(serverResponse.body.seatbid[0].bid[0].h || BB_CONSTANTS.DEFAULT_HEIGHT); + expect(bid.ad).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(bid.netRevenue).to.equal(BB_CONSTANTS.DEFAULT_NET_REVENUE); + expect(bid.creativeId).to.equal(serverResponse.body.seatbid[0].bid[0].crid); + expect(bid.currency).to.equal(serverResponse.body.cur); + expect(bid.ttl).to.equal(BB_CONSTANTS.DEFAULT_TTL); + + expect(bid.publicationName).to.equal(validBidderRequest.bids[0].params.publicationName); + expect(bid.rendererCode).to.equal(validBidderRequest.bids[0].params.rendererCode); + expect(bid.accountId).to.equal(validBidderRequest.bids[0].params.accountId); + }); + + it('should not give anything when seatbid is an empty array', () => { + const seatbidEmptyArray = deepClone(serverResponse); + seatbidEmptyArray.body.seatbid = []; + + const response = seatbidEmptyArray; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(result.length).to.equal(0); + }); + + it('should not give anything when seatbid is missing', () => { + const seatbidMissing = deepClone(serverResponse); + delete seatbidMissing.body.seatbid; + + const response = seatbidMissing; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(result.length).to.equal(0); + }); + + const seatbidNotArrayResponse = deepClone(serverResponse); + it('should not give anything when seatbid is not an array', () => { + const invalidValues = [ false, null, {}, void (0), 123, 'string' ]; + + for (const invalidValue of invalidValues) { + seatbidNotArrayResponse.body.seatbid = invalidValue + const response = deepClone(seatbidNotArrayResponse); // interpretResponse is destructive + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(result.length).to.equal(0); + } + }); + + it('should not give anything when seatbid.bid is an empty array', () => { + const seatbidBidEmpty = deepClone(serverResponse); + seatbidBidEmpty.body.seatbid[0].bid = []; + + const response = seatbidBidEmpty; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(result.length).to.equal(0); + }); + + it('should not give anything when seatbid.bid is missing', () => { + const seatbidBidMissing = deepClone(serverResponse); + delete seatbidBidMissing.body.seatbid[0].bid; + + const response = seatbidBidMissing; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(result.length).to.equal(0); + }); + + it('should not give anything when seatbid.bid is not an array', () => { + const seatbidBidNotArray = deepClone(serverResponse); + + const invalidValues = [ false, null, {}, void (0), 123, 'string' ]; + + for (const invalidValue of invalidValues) { + seatbidBidNotArray.body.seatbid[0].bid = invalidValue; + + const response = deepClone(seatbidBidNotArray); // interpretResponse is destructive + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(result.length).to.equal(0); + } + }); + }); + describe('getUserSyncs', () => { + const publicationName = 'bbprebid.dev'; + const rendererCode = 'glorious_renderer'; + + const baseValidBid = { + bidder: BB_CONSTANTS.BIDDER_CODE, + params: { + accountId: 123, + publicationName: publicationName, + rendererCode: rendererCode, + connections: [ BB_CONSTANTS.BIDDER_CODE ], + bluebillywig: {} + }, + mediaTypes: { + video: { + context: 'outstream' + } + } + }; + + const validBidRequests = [baseValidBid]; + + const validBidderRequest = { + auctionId: '12abc345-67d8-9012-e345-6f78901a2b34', + auctionStart: 1585918458868, + bidderCode: BB_CONSTANTS.BIDDER_CODE, + bidderRequestId: '1a2345b67c8d9e0', + bids: [{ + adUnitCode: 'ad-unit-test', + auctionId: '12abc345-67d8-9012-e345-6f78901a2b34', + bidId: '1234ab567c89de0', + bidRequestsCount: 1, + bidder: BB_CONSTANTS.BIDDER_CODE, + bidderRequestId: '1a2345b67c8d9e0', + params: baseValidBid.params, + sizes: [[768, 432], [640, 480], [630, 360]], + transactionId: '2b34c5de-f67a-8901-bcd2-34567efabc89' + }], + start: 11585918458869, + timeout: 3000 + }; + const validResponse = { + id: 'a12abc345-67d8-9012-e345-6f78901a2b34', + seatbid: [ + { + bid: [ + { + id: '1', + impid: '1234ab567c89de0', + price: 1, + adm: '\r\nBB Adserver00:00:51', + adid: '67069817', + adomain: [ + 'bluebillywig.com' + ], + cid: '3535', + crid: '67069817', + w: 1, + h: 1, + publicationName: 'bbprebid', + accountId: 123, + ext: { + prebid: { + targeting: { + hb_bidder: 'bluebillywig', + hb_pb: '1.00', + hb_size: '1x1' + }, + type: 'video' + }, + bidder: { + prebid: { + targeting: { + hb_bidder: 'bluebillywig', + hb_pb: '10.00', + hb_size: '1x1' + }, + type: 'video', + video: { + duration: 51, + primary_category: '' + } + }, + bidder: { + bluebillywig: { + brand_id: 1, + auction_id: 1, + bid_ad_type: 1, + creative_info: { + video: { + duration: 51, + mimes: [ + 'video/x-flv', + 'video/mp4', + 'video/webm' + ] + } + } + } + } + } + } + } + ], + seat: 'bluebillywig' + } + ], + cur: 'USD', + ext: { + responsetimemillis: { + bluebillywig: 0 + }, + tmaxrequest: 5000 + } + }; + + const serverResponse = { body: validResponse }; + + const gdpr = { + consentString: 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAA AAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + gdprApplies: true + }; + + it('should return empty if no server response', function () { + const result = spec.getUserSyncs({}, false, gdpr); + expect(result).to.be.empty; + }); + + it('should return empty if server response is empty', function () { + const result = spec.getUserSyncs({}, [], gdpr); + expect(result).to.be.empty; + }); + + it('should return empty if iframeEnabled is not true', () => { + const result = spec.getUserSyncs({iframeEnabled: false}, [serverResponse], gdpr); + expect(result).to.be.empty; + }); + + it('should append the various values if they exist', function() { + // push data to syncStore + spec.buildRequests(validBidRequests, validBidderRequest); + + const result = spec.getUserSyncs({iframeEnabled: true}, [serverResponse], gdpr); + + expect(result).to.not.be.empty; + + expect(result[0].url).to.include('gdpr=1'); + expect(result[0].url).to.include(gdpr.consentString); + expect(result[0].url).to.include('accountId=123'); + expect(result[0].url).to.include(`bidders=${btoa(JSON.stringify(validBidRequests[0].params.connections))}`); + expect(result[0].url).to.include('cb='); + }); + }); +}); diff --git a/test/spec/modules/deepintentBidAdapter_spec.js b/test/spec/modules/deepintentBidAdapter_spec.js index 8b462b3217d..6d9b883e2bb 100644 --- a/test/spec/modules/deepintentBidAdapter_spec.js +++ b/test/spec/modules/deepintentBidAdapter_spec.js @@ -162,6 +162,23 @@ describe('Deepintent adapter', function () { let data2 = JSON.parse(bRequest2.data); expect(data2.regs).to.equal(undefined); }); + it('bid Request check: GDPR Check', function () { + let bidRequest = { + gdprConsent: { + consentString: 'kjfdnidasd123sadsd', + gdprApplies: true + } + }; + let bRequest = spec.buildRequests(request, bidRequest); + let data = JSON.parse(bRequest.data); + expect(data.user.ext.consent).to.equal('kjfdnidasd123sadsd'); + expect(data.regs.ext.gdpr).to.equal(1); + let bidRequest2 = {}; + let bRequest2 = spec.buildRequests(request, bidRequest2); + let data2 = JSON.parse(bRequest2.data); + expect(data2.regs).to.equal(undefined); + expect(data2.user.ext).to.equal(undefined); + }); }); describe('user sync check', function () { it('user sync url check', function () { diff --git a/test/spec/modules/eplanningAnalyticsAdapter_spec.js b/test/spec/modules/eplanningAnalyticsAdapter_spec.js index 9bb71e7033d..255d116a0ff 100644 --- a/test/spec/modules/eplanningAnalyticsAdapter_spec.js +++ b/test/spec/modules/eplanningAnalyticsAdapter_spec.js @@ -1,5 +1,5 @@ import eplAnalyticsAdapter from 'modules/eplanningAnalyticsAdapter.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { expect } from 'chai'; import { parseUrl } from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; diff --git a/test/spec/modules/fintezaAnalyticsAdapter_spec.js b/test/spec/modules/fintezaAnalyticsAdapter_spec.js index ae9ceceecc3..29136c85241 100644 --- a/test/spec/modules/fintezaAnalyticsAdapter_spec.js +++ b/test/spec/modules/fintezaAnalyticsAdapter_spec.js @@ -1,5 +1,5 @@ import fntzAnalyticsAdapter from 'modules/fintezaAnalyticsAdapter.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { expect } from 'chai'; import { parseUrl } from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; diff --git a/test/spec/modules/konduitAnalyticsAdapter_spec.js b/test/spec/modules/konduitAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..ac557d27f90 --- /dev/null +++ b/test/spec/modules/konduitAnalyticsAdapter_spec.js @@ -0,0 +1,126 @@ +import konduitAnalyticsAdapter from 'modules/konduitAnalyticsAdapter'; +import { expect } from 'chai'; +import { config } from '../../../src/config.js'; +import { server } from 'test/mocks/xhr.js'; +let events = require('src/events'); +let adapterManager = require('src/adapterManager').default; +let CONSTANTS = require('src/constants.json'); + +const eventsData = { + [CONSTANTS.EVENTS.AUCTION_INIT]: { + 'auctionId': 'test_auction_id', + 'timestamp': Date.now(), + 'auctionStatus': 'inProgress', + 'adUnitCodes': ['video-test'], + 'timeout': 700 + }, + [CONSTANTS.EVENTS.BID_REQUESTED]: { + 'bidderCode': 'test_bidder_code', + 'time': Date.now(), + 'bids': [{ + 'transactionId': 'test_transaction_id', + 'adUnitCode': 'video-test', + 'bidId': 'test_bid_id', + 'sizes': '640x480', + 'params': { 'testParam': 'test_param' } + }] + }, + [CONSTANTS.EVENTS.NO_BID]: { + 'bidderCode': 'test_bidder_code2', + 'transactionId': 'test_transaction_id', + 'adUnitCode': 'video-test', + 'bidId': 'test_bid_id' + }, + [CONSTANTS.EVENTS.BID_RESPONSE]: { + 'bidderCode': 'test_bidder_code', + 'adUnitCode': 'video-test', + 'statusMessage': 'Bid available', + 'mediaType': 'video', + 'renderedSize': '640x480', + 'cpm': 0.5, + 'currency': 'USD', + 'netRevenue': true, + 'timeToRespond': 124, + 'requestId': 'test_request_id', + 'creativeId': 144876543 + }, + [CONSTANTS.EVENTS.AUCTION_END]: { + 'auctionId': 'test_auction_id', + 'timestamp': Date.now(), + 'auctionEnd': Date.now() + 400, + 'auctionStatus': 'completed', + 'adUnitCodes': ['video-test'], + 'timeout': 700 + }, + [CONSTANTS.EVENTS.BID_WON]: { + 'bidderCode': 'test_bidder_code', + 'adUnitCode': 'video-test', + 'statusMessage': 'Bid available', + 'mediaType': 'video', + 'renderedSize': '640x480', + 'cpm': 0.5, + 'currency': 'USD', + 'netRevenue': true, + 'timeToRespond': 124, + 'requestId': 'test_request_id', + 'creativeId': 144876543 + }, +}; + +describe(`Konduit Analytics Adapter`, () => { + const konduitId = 'test'; + + beforeEach(function () { + sinon.spy(konduitAnalyticsAdapter, 'track'); + sinon.stub(events, 'getEvents').returns([]); + config.setConfig({ konduit: { konduitId } }); + }); + + afterEach(function () { + events.getEvents.restore(); + konduitAnalyticsAdapter.track.restore(); + konduitAnalyticsAdapter.disableAnalytics(); + }); + + it(`should add all events to an aggregatedEvents queue + inside konduitAnalyticsAdapter.context and send a request with correct data`, function () { + server.respondWith(JSON.stringify({ key: 'test' })); + + adapterManager.registerAnalyticsAdapter({ + code: 'konduit', + adapter: konduitAnalyticsAdapter + }); + + adapterManager.enableAnalytics({ + provider: 'konduit', + }); + + expect(konduitAnalyticsAdapter.context).to.be.an('object'); + expect(konduitAnalyticsAdapter.context.aggregatedEvents).to.be.an('array'); + + const eventTypes = [ + CONSTANTS.EVENTS.AUCTION_INIT, + CONSTANTS.EVENTS.BID_REQUESTED, + CONSTANTS.EVENTS.NO_BID, + CONSTANTS.EVENTS.BID_RESPONSE, + CONSTANTS.EVENTS.BID_WON, + CONSTANTS.EVENTS.AUCTION_END, + ]; + const args = eventTypes.map(eventType => eventsData[eventType]); + + eventTypes.forEach((eventType, i) => { + events.emit(eventType, args[i]); + }); + + server.respond(); + + expect(konduitAnalyticsAdapter.context.aggregatedEvents.length).to.be.equal(6); + expect(server.requests[0].url).to.match(/http(s):\/\/\w*\.konduit\.me\/analytics-initial-event/); + + const requestBody = JSON.parse(server.requests[0].requestBody); + expect(requestBody.konduitId).to.be.equal(konduitId); + expect(requestBody.prebidVersion).to.be.equal('$prebid.version$'); + expect(requestBody.environment).to.be.an('object'); + sinon.assert.callCount(konduitAnalyticsAdapter.track, 6); + }); +}); diff --git a/test/spec/modules/konduitWrapper_spec.js b/test/spec/modules/konduitWrapper_spec.js index 4a0c627e885..d70cb7a6c60 100644 --- a/test/spec/modules/konduitWrapper_spec.js +++ b/test/spec/modules/konduitWrapper_spec.js @@ -1,72 +1,123 @@ import { expect } from 'chai'; -import parse from 'url-parse'; -import { buildVastUrl } from 'modules/konduitWrapper.js'; -import { parseQS } from 'src/utils.js'; +import { processBids, errorMessages } from 'modules/konduitWrapper.js'; import { config } from 'src/config.js'; +import { server } from 'test/mocks/xhr.js'; describe('The Konduit vast wrapper module', function () { - it('should make a wrapped request url when `bid` passed', function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + const konduitId = 'test'; + beforeEach(function() { + config.setConfig({ konduit: { konduitId } }); + }); - const url = parse(buildVastUrl({ - bid, - params: { 'konduit_id': 'testId' }, - })); + describe('processBids function', () => { + it(`should make a correct processBids request and add kCpm and konduitCacheKey + to the passed bids and to the adserverTargeting object`, function () { + const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - expect(url.protocol).to.equal('https:'); - expect(url.host).to.equal('p.konduit.me'); + server.respondWith(JSON.stringify({ + kCpmData: { [`${bid.bidderCode}:${bid.creativeId}`]: bid.cpm }, + cacheData: { [`${bid.bidderCode}:${bid.creativeId}`]: 'test_cache_key' }, + })); - const queryParams = parseQS(url.query); - expect(queryParams).to.have.property('konduit_url', encodeURIComponent('http://some-vast-url.com')); - expect(queryParams).to.have.property('konduit_header_bidding', '1'); - expect(queryParams).to.have.property('konduit_id', 'testId'); - }); + processBids({ bid }); + server.respond(); - it('should return null when no `konduit_id` (required param) passed', function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + expect(server.requests.length).to.equal(1); - const url = buildVastUrl({ bid }); + const requestBody = JSON.parse(server.requests[0].requestBody); - expect(url).to.equal(null); - }); + expect(requestBody.clientId).to.equal(konduitId); - it('should return null when either bid or adUnit is not passed', function () { - const url = buildVastUrl({ params: { 'konduit_id': 'testId' } }); + expect(bid.konduitCacheKey).to.equal('test_cache_key'); + expect(bid.kCpm).to.equal(bid.cpm); - expect(url).to.equal(null); - }); + expect(bid.adserverTargeting).to.be.an('object'); + + expect(bid.adserverTargeting.k_cpm).to.equal(bid.pbCg || bid.pbAg); + expect(bid.adserverTargeting.konduit_cache_key).to.equal('test_cache_key'); + expect(bid.adserverTargeting.konduit_id).to.equal(konduitId); + }); + + it(`should call callback with error object in arguments if cacheData is empty in the response`, function () { + const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + + server.respondWith(JSON.stringify({ + kCpmData: { [`${bid.bidderCode}:${bid.creativeId}`]: bid.cpm }, + cacheData: {}, + })); + const callback = sinon.spy(); + processBids({ bid, callback }); + server.respond(); - it('should return null when bid does not contain vastUrl', function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + expect(server.requests.length).to.equal(1); - delete bid.vastUrl; + const requestBody = JSON.parse(server.requests[0].requestBody); - const url = buildVastUrl({ - bid, - params: { 'konduit_id': 'testId' }, + expect(requestBody.clientId).to.equal(konduitId); + + expect(bid.konduitCacheKey).to.be.undefined; + expect(bid.kCpm).to.equal(bid.cpm); + + expect(bid.adserverTargeting.k_cpm).to.equal(bid.pbCg || bid.pbAg); + expect(bid.adserverTargeting.konduit_cache_key).to.be.undefined; + expect(bid.adserverTargeting.konduit_id).to.be.undefined; + + expect(callback.firstCall.args[0]).to.be.an('error'); }); - expect(url).to.equal(null); - }); + it('should call callback if processBids request is sent successfully', function () { + const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + server.respondWith(JSON.stringify({ key: 'test' })); + const callback = sinon.spy(); + processBids({ + bid, + callback + }); + server.respond(); + + expect(callback.calledOnce).to.be.true; + }); - it('should return wrapped vastUrl based on cached url in params', function () { - config.setConfig({ cache: { url: 'https://cached.url.com' } }); - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + it('should call callback with error object in arguments if processBids request is failed', function () { + const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + const callback = sinon.spy(); + processBids({ + bid, + callback + }); + server.respond(); + + expect(callback.calledOnce).to.be.true; + expect(callback.firstCall.args[0]).to.be.an('error'); + }); - delete bid.vastUrl; + it('should call callback with error object in arguments if no konduitId in configs', function () { + config.setConfig({ konduit: { konduitId: null } }); - const expectedUrl = encodeURIComponent(`https://cached.url.com?uuid=${bid.videoCacheKey}`); + const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); + const callback = sinon.spy(); + processBids({ + bid, + callback + }); - const url = parse(buildVastUrl({ - bid, - params: { 'konduit_id': 'testId' }, - })); - const queryParams = parseQS(url.query); + expect(callback.calledOnce).to.be.true; + expect(callback.firstCall.args[0]).to.be.an('error'); + expect(callback.firstCall.args[0].message).to.equal(errorMessages.NO_KONDUIT_ID); + }); - expect(queryParams).to.have.property('konduit_url', expectedUrl); + it('should call callback with error object in arguments if no bids found', function () { + const callback = sinon.spy(); + processBids({ + bid: null, + callback + }); - config.resetConfig(); + expect(callback.calledOnce).to.be.true; + expect(callback.firstCall.args[0]).to.be.an('error'); + expect(callback.firstCall.args[0].message).to.equal(errorMessages.NO_BID); + }); }); }); @@ -103,7 +154,7 @@ function createBid(cpm, adUnitCode, durationBucket, priceIndustryDuration, uuid, 'pbLg': '5.00', 'pbMg': '5.00', 'pbHg': '5.00', - 'pbAg': '5.00', + 'pbAg': `${cpm}.00`, 'pbDg': '5.00', 'pbCg': '', 'size': '640x360', diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index b82bf8db160..800ca744d9c 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -11,6 +11,8 @@ describe('Livewrapped adapter tests', function () { beforeEach(function () { sandbox = sinon.sandbox.create(); + window.livewrapped = undefined; + bidderRequest = { bidderCode: 'livewrapped', auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', @@ -970,6 +972,39 @@ describe('Livewrapped adapter tests', function () { expect(bids).to.deep.equal(expectedResponse); }) + + it('should send debug-data to external debugger', function() { + let lwResponse = { + ads: [ + { + id: '28e5ddf4-3c01-11e8-86a7-0a44794250d4', + callerId: 'site_outsider_0', + tag: 'ad', + width: 300, + height: 250, + cpmBid: 2.565917, + bidId: '32e50fad901ae89', + auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + ttl: 120 + } + ], + currency: 'USD', + dbg: 'debugdata' + }; + + let debugData; + + window.livewrapped = { + s2sDebug: function(dbg) { + debugData = dbg; + } + }; + + spec.interpretResponse({body: lwResponse}); + + expect(debugData).to.equal(lwResponse.dbg); + }) }); describe('user sync', function () { diff --git a/test/spec/modules/marsmediaBidAdapter_spec.js b/test/spec/modules/marsmediaBidAdapter_spec.js index c0399e5d0a2..e02870d9890 100644 --- a/test/spec/modules/marsmediaBidAdapter_spec.js +++ b/test/spec/modules/marsmediaBidAdapter_spec.js @@ -61,7 +61,8 @@ describe('marsmedia adapter tests', function () { 'h': 250, 'adm': '
My Compelling Ad
', 'price': 1, - 'crid': 'cr-cfy24' + 'crid': 'cr-cfy24', + 'nurl': '' } ] }; @@ -140,7 +141,8 @@ describe('marsmedia adapter tests', function () { 'cid': '467415', 'crid': 'cr-vid', 'w': 800, - 'h': 600 + 'h': 600, + 'nurl': '' } ] }; @@ -173,7 +175,8 @@ describe('marsmedia adapter tests', function () { 'cid': '467415', 'crid': 'cr-vid', 'w': 800, - 'h': 600 + 'h': 600, + 'nurl': '' } ] }; @@ -604,4 +607,21 @@ describe('marsmedia adapter tests', function () { expect(r1adapter.getUserSyncs()).to.deep.equal([]); }); }); + + describe('on bidWon', function () { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function() { + utils.triggerPixel.restore(); + }); + it('exists and is a function', () => { + expect(spec.onBidWon).to.exist.and.to.be.a('function'); + }); + it('should return nothing', function () { + var response = spec.onBidWon({}); + expect(response).to.be.an('undefined') + expect(utils.triggerPixel.called).to.equal(true); + }); + }); }); diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index 22e2b1117d5..cf23bc62655 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -309,6 +309,7 @@ let VALID_BID_REQUEST = [{ 'prebid_version': $$PREBID_GLOBAL$$.version, 'gdpr_applies': false, 'usp_applies': false, + 'coppa_applies': false, 'screen': { 'w': 1000, 'h': 1000 @@ -393,6 +394,7 @@ let VALID_BID_REQUEST = [{ 'prebid_version': $$PREBID_GLOBAL$$.version, 'gdpr_applies': false, 'usp_applies': false, + 'coppa_applies': false, 'screen': { 'w': 1000, 'h': 1000 @@ -478,6 +480,7 @@ let VALID_BID_REQUEST = [{ 'prebid_version': $$PREBID_GLOBAL$$.version, 'gdpr_applies': false, 'usp_applies': false, + 'coppa_applies': false, 'screen': { 'w': 1000, 'h': 1000 @@ -564,6 +567,7 @@ let VALID_BID_REQUEST = [{ britepoolid: '82efd5e1-816b-4f87-97f8-044f407e2911' }, 'usp_applies': false, + 'coppa_applies': false, 'screen': { 'w': 1000, 'h': 1000 @@ -651,6 +655,7 @@ let VALID_BID_REQUEST = [{ 'prebid_version': $$PREBID_GLOBAL$$.version, 'gdpr_applies': false, 'usp_applies': false, + 'coppa_applies': true, 'screen': { 'w': 1000, 'h': 1000 @@ -992,6 +997,7 @@ let VALID_BID_REQUEST = [{ 'gdpr_consent_string': 'consentString', 'gdpr_applies': true, 'usp_applies': true, + 'coppa_applies': false, 'usp_consent_string': '1NYN', 'screen': { 'w': 1000, @@ -1149,6 +1155,12 @@ describe('Media.net bid adapter', function () { }); it('should have valid crid present in bid request', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'coppa': true + }; + return config[key]; + }); let bidreq = spec.buildRequests(VALID_BID_REQUEST_WITH_CRID, VALID_AUCTIONDATA); expect(JSON.parse(bidreq.data)).to.deep.equal(VALID_PAYLOAD_WITH_CRID); }); diff --git a/test/spec/modules/quantumdexBidAdapter_spec.js b/test/spec/modules/quantumdexBidAdapter_spec.js new file mode 100644 index 00000000000..ef8791cca57 --- /dev/null +++ b/test/spec/modules/quantumdexBidAdapter_spec.js @@ -0,0 +1,604 @@ +import { expect } from 'chai' +import { spec } from 'modules/quantumdexBidAdapter.js' +import { newBidder } from 'src/adapters/bidderFactory.js' +import { userSync } from '../../../src/userSync.js'; + +describe('QuantumdexBidAdapter', function () { + const adapter = newBidder(spec) + + describe('.code', function () { + it('should return a bidder code of quantumdex', function () { + expect(spec.code).to.equal('quantumdex') + }) + }) + + describe('inherited functions', function () { + it('should exist and be a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function') + }) + }) + + describe('.isBidRequestValid', function () { + it('should return false if there are no params', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false if there is no siteId param', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + params: { + site_id: '1a2b3c4d5e6f1a2b3c4d', + }, + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false if there is no mediaTypes', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + params: { + siteId: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true if the bid is valid', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + params: { + siteId: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + describe('banner', () => { + it('should return false if there are no banner sizes', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + params: { + siteId: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + banner: { + + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true if there is banner sizes', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + params: { + siteId: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('video', () => { + it('should return false if there is no playerSize defined in the video mediaType', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + params: { + siteId: '1a2b3c4d5e6f1a2b3c4d', + sizes: [[300, 250], [300, 600]] + }, + 'mediaTypes': { + video: { + context: 'instream' + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true if there is playerSize defined on the video mediaType', () => { + const bid = { + 'bidder': 'quantumdex', + 'adUnitCode': 'adunit-code', + params: { + siteId: '1a2b3c4d5e6f1a2b3c4d', + }, + 'mediaTypes': { + video: { + context: 'instream', + playerSize: [[640, 480]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + }); + + describe('.buildRequests', function () { + beforeEach(function () { + sinon.stub(userSync, 'canBidderRegisterSync'); + }); + afterEach(function () { + userSync.canBidderRegisterSync.restore(); + }); + let bidRequest = [{ + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 0 + }, + ] + }, + 'bidder': 'quantumdex', + 'params': { + 'siteId': '1a2b3c4d5e6f1a2b3c4d', + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'targetKey': 0, + 'bidId': '30b31c1838de1f', + }, + { + 'bidder': 'quantumdex', + 'params': { + 'ad_unit': '/7780971/sparks_prebid_LB', + 'sizes': [[300, 250], [300, 600]], + 'referrer': 'overrides_top_window_location' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[120, 600], [300, 600], [160, 600]], + 'targetKey': 1, + 'bidId': '30b31c1838de1e', + }]; + + let bidderRequests = { + 'gdprConsent': { + 'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + 'vendorData': {}, + 'gdprApplies': true + }, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'https://example.com', + 'stack': ['https://example.com'] + }, + uspConsent: 'someCCPAString' + }; + + it('should return a properly formatted request', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/adapter') + expect(bidRequests.method).to.equal('POST') + expect(bidRequests.bidderRequests).to.eql(bidRequest); + }) + + it('should return a properly formatted request with GDPR applies set to true', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/adapter') + expect(bidRequests.method).to.equal('POST') + expect(bidRequests.data.gdpr.gdprApplies).to.equal('true') + expect(bidRequests.data.gdpr.consentString).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') + }) + + it('should return a properly formatted request with GDPR applies set to false', function () { + bidderRequests.gdprConsent.gdprApplies = false; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/adapter') + expect(bidRequests.method).to.equal('POST') + expect(bidRequests.data.gdpr.gdprApplies).to.equal('false') + expect(bidRequests.data.gdpr.consentString).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') + }) + it('should return a properly formatted request with GDPR applies set to false with no consent_string param', function () { + let bidderRequests = { + 'gdprConsent': { + 'consentString': undefined, + 'vendorData': {}, + 'gdprApplies': false + }, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'https://example.com', + 'stack': ['https://example.com'] + } + }; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/adapter') + expect(bidRequests.method).to.equal('POST') + expect(bidRequests.data.gdpr.gdprApplies).to.equal('false') + expect(bidRequests.data.gdpr).to.not.include.keys('consentString') + }) + it('should return a properly formatted request with GDPR applies set to true with no consentString param', function () { + let bidderRequests = { + 'gdprConsent': { + 'consentString': undefined, + 'vendorData': {}, + 'gdprApplies': true + }, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'https://example.com', + 'stack': ['https://example.com'] + } + }; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/adapter') + expect(bidRequests.method).to.equal('POST') + expect(bidRequests.data.gdpr.gdprApplies).to.equal('true') + expect(bidRequests.data.gdpr).to.not.include.keys('consentString') + }) + it('should return a properly formatted request with schain defined', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(JSON.parse(bidRequests.data.schain)).to.deep.equal(bidRequest[0].schain) + }); + it('should return a properly formatted request with us_privacy included', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.us_privacy).to.equal('someCCPAString'); + }); + }); + + describe('.interpretResponse', function () { + const bidRequests = { + 'method': 'POST', + 'url': 'https://useast.quantumdex.io/auction/adapter', + 'withCredentials': true, + 'data': { + 'device': { + 'ua': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36', + 'height': 937, + 'width': 1920, + 'dnt': 0, + 'language': 'vi' + }, + 'site': { + 'id': '343', + 'page': 'https://www.example.com/page', + 'referrer': '', + 'hostname': 'www.example.com' + } + }, + 'bidderRequests': [ + { + 'bidder': 'quantumdex', + 'params': { + 'siteId': '343' + }, + 'crumbs': { + 'pubcid': 'c2b2ba08-9954-4850-8ee5-2bf4a2b35eff' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[160, 600], [120, 600]] + } + }, + 'adUnitCode': 'vi_3431035_1', + 'transactionId': '994d8404-4a28-4c43-98d8-a38dbb061910', + 'sizes': [[160, 600], [120, 600]], + 'bidId': '3000aa31c41a29c21', + 'bidderRequestId': '299926b3d3628cdd7', + 'auctionId': '22445943-a0aa-4c63-a413-4deb64fcff1c', + 'src': 'client', + 'bidRequestsCount': 41, + 'bidderRequestsCount': 41, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'freegames66.com', + 'sid': '343', + 'hp': 1 + } + ] + } + }, + { + 'bidder': 'quantumdex', + 'params': { + 'siteId': '343' + }, + 'crumbs': { + 'pubcid': 'c2b2ba08-9954-4850-8ee5-2bf4a2b35eff' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [250, 250], [200, 200], [180, 150]] + } + }, + 'adUnitCode': 'vi_3431033_1', + 'transactionId': '800fe87e-bfec-43a5-ace0-c4e2373ff4b5', + 'sizes': [[300, 250], [250, 250], [200, 200], [180, 150]], + 'bidId': '30024615be22ef66a', + 'bidderRequestId': '299926b3d3628cdd7', + 'auctionId': '22445943-a0aa-4c63-a413-4deb64fcff1c', + 'src': 'client', + 'bidRequestsCount': 41, + 'bidderRequestsCount': 41, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'freegames66.com', + 'sid': '343', + 'hp': 1 + } + ] + } + }, + { + 'bidder': 'quantumdex', + 'params': { + 'siteId': '343' + }, + 'crumbs': { + 'pubcid': 'c2b2ba08-9954-4850-8ee5-2bf4a2b35eff' + }, + 'mediaTypes': { + 'video': { + 'playerSize': [[640, 480]], + 'context': 'instream', + 'mimes': [ + 'video/mp4', + 'video/x-flv', + 'video/x-ms-wmv', + 'application/vnd.apple.mpegurl', + 'application/x-mpegurl', + 'video/3gpp', + 'video/mpeg', + 'video/ogg', + 'video/quicktime', + 'video/webm', + 'video/x-m4v', + 'video/ms-asf', + 'video/x-msvideo' + ], + 'protocols': [1, 2, 3, 4, 5, 6], + 'playbackmethod': [6], + 'maxduration': 120, + 'linearity': 1, + 'api': [2] + } + }, + 'adUnitCode': 'vi_3431909', + 'transactionId': '33d83d87-43cc-499b-aabe-5c22eb6acfbb', + 'sizes': [[640, 480]], + 'bidId': '1854b40107d6745c', + 'bidderRequestId': '1840763b6bda185d', + 'auctionId': 'df495de0-5d42-471f-a501-73bcd7254b80', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'freegames66.com', + 'sid': '343', + 'hp': 1 + } + ] + } + } + ] + }; + + let serverResponse = { + 'body': { + 'bids': [ + { + 'requestId': '3000aa31c41a29c21', + 'cpm': 1.07, + 'width': 160, + 'height': 600, + 'ad': `
Quantumdex AD
`, + 'ttl': 500, + 'creativeId': '1234abcd', + 'netRevenue': true, + 'currency': 'USD', + 'dealId': 'quantumdex', + 'mediaType': 'banner' + }, + { + 'requestId': '30024615be22ef66a', + 'cpm': 1, + 'width': 300, + 'height': 250, + 'ad': `
Quantumdex AD
`, + 'ttl': 500, + 'creativeId': '1234abcd', + 'netRevenue': true, + 'currency': 'USD', + 'dealId': 'quantumdex', + 'mediaType': 'banner' + }, + { + 'requestId': '1854b40107d6745c', + 'cpm': 1.25, + 'width': 300, + 'height': 250, + 'vastXml': 'quantumdex', + 'ttl': 500, + 'creativeId': '30292e432662bd5f86d90774b944b038', + 'netRevenue': true, + 'currency': 'USD', + 'dealId': 'quantumdex', + 'mediaType': 'video' + } + ], + 'pixel': [{ + 'url': 'https://example.com/pixel.png', + 'type': 'image' + }] + } + }; + + let prebidResponse = [ + { + 'requestId': '3000aa31c41a29c21', + 'cpm': 1.07, + 'width': 160, + 'height': 600, + 'ad': `
Quantumdex AD
`, + 'ttl': 500, + 'creativeId': '1234abcd', + 'netRevenue': true, + 'currency': 'USD', + 'dealId': 'quantumdex', + 'mediaType': 'banner' + }, + { + 'requestId': '30024615be22ef66a', + 'cpm': 1, + 'width': 300, + 'height': 250, + 'ad': `
Quantumdex AD
`, + 'ttl': 500, + 'creativeId': '1234abcd', + 'netRevenue': true, + 'currency': 'USD', + 'dealId': 'quantumdex', + 'mediaType': 'banner' + }, + { + 'requestId': '1854b40107d6745c', + 'cpm': 1.25, + 'width': 300, + 'height': 250, + 'vastXml': 'quantumdex', + 'ttl': 500, + 'creativeId': '30292e432662bd5f86d90774b944b038', + 'netRevenue': true, + 'currency': 'USD', + 'dealId': 'quantumdex', + 'mediaType': 'video' + } + ]; + + it('should map bidResponse to prebidResponse', function () { + const response = spec.interpretResponse(serverResponse, bidRequests); + response.forEach((resp, i) => { + expect(resp.requestId).to.equal(prebidResponse[i].requestId); + expect(resp.cpm).to.equal(prebidResponse[i].cpm); + expect(resp.width).to.equal(prebidResponse[i].width); + expect(resp.height).to.equal(prebidResponse[i].height); + expect(resp.ttl).to.equal(prebidResponse[i].ttl); + expect(resp.creativeId).to.equal(prebidResponse[i].creativeId); + expect(resp.netRevenue).to.equal(prebidResponse[i].netRevenue); + expect(resp.currency).to.equal(prebidResponse[i].currency); + expect(resp.dealId).to.equal(prebidResponse[i].dealId); + if (resp.mediaType === 'video') { + expect(resp.vastXml.indexOf('quantumdex')).to.be.greaterThan(0); + } + if (resp.mediaType === 'banner') { + expect(resp.ad.indexOf('Quantumdex AD')).to.be.greaterThan(0); + } + }); + }); + }); + + describe('.getUserSyncs', function () { + let bidResponse = [{ + 'body': { + 'pixel': [{ + 'url': 'https://pixel-test', + 'type': 'image' + }] + } + }]; + + it('should return one sync pixel', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, bidResponse)).to.deep.equal([{ + type: 'image', + url: 'https://pixel-test' + }]); + }); + it('should return an empty array when sync is enabled but there are no bidResponses', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, [])).to.have.length(0); + }); + + it('should return an empty array when sync is enabled but no sync pixel returned', function () { + const pixel = Object.assign({}, bidResponse); + delete pixel[0].body.pixel; + expect(spec.getUserSyncs({ pixelEnabled: true }, bidResponse)).to.have.length(0); + }); + + it('should return an empty array', function () { + expect(spec.getUserSyncs({ pixelEnabled: false }, bidResponse)).to.have.length(0); + expect(spec.getUserSyncs({ pixelEnabled: true }, [])).to.have.length(0); + }); + }); +}); diff --git a/test/spec/modules/rakutenBidAdapter_spec.js b/test/spec/modules/rakutenBidAdapter_spec.js new file mode 100644 index 00000000000..15b22afbe29 --- /dev/null +++ b/test/spec/modules/rakutenBidAdapter_spec.js @@ -0,0 +1,171 @@ +import { expect } from 'chai' +import { spec } from 'modules/rakutenBidAdapter/index.js' +import { newBidder } from 'src/adapters/bidderFactory.js' +import {config} from '../../../src/config.js'; + +describe('rakutenBidAdapter', function() { + const adapter = newBidder(spec); + const ENDPOINT = 'https://s-bid.rmp.rakuten.com/h'; + let sandbox; + + beforeEach(function() { + config.resetConfig(); + }); + + afterEach(function () { + config.resetConfig(); + }); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function') + }) + }); + + describe('isBidRequestValid', () => { + let bid = { + bidder: 'rakuten', + params: { + adSpotId: '56789' + } + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true) + }); + + it('should return false when required params are not passed', () => { + bid.params.adSpotId = ''; + expect(spec.isBidRequestValid(bid)).to.equal(false) + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }); + + describe('buildRequests', () => { + const bidRequests = [ + { + // banner + params: { + adSpotId: '58278' + } + } + ]; + + const bidderRequest = { + bids: bidRequests, + refererInfo: { + referer: 'http://test.com', + stack: ['http://test.com'] + }, + gdprConsent: { + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + vendorData: {}, + gdprApplies: true + }, + uspConsent: '1YN-' + }; + + it('sends bid request to ENDPOINT via GET', () => { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('GET') + expect(request.data.gdpr).to.equal(1); + expect(request.data.cd).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + expect(request.data.ccpa).to.equal('1YN-'); + }); + + it('allows url override', () => { + config.setConfig({ + rakuten: { + endpoint: '//test.rakuten.com' + } + }); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.url).to.equal('//test.rakuten.com'); + }) + }); + + describe('interpretResponse', () => { + const bidRequests = { + banner: { + method: 'GET', + url: '', + data: { + t: '56789', + s: 'https', + ua: + 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Mobile Safari/537.36', + l: 'ja', + d: 'examples.com', + tp: 'https://examples.com/foo/fuga', + pp: 'https://examples.com/hoge/muga' + } + } + }; + + const serverResponse = { + noAd: [], + noAd2: { + requestId: 'biequa9oaph4we' + }, + banner: { + requestId: 'biequa9oaph4we', + cpm: 37.66, + width: 300, + height: 250, + creativeId: 140281, + dealId: 'phoh3pad-ai4ah-xoh7x-ahk7cheasae3oh', + currency: 'USD', + netRevenue: 300, + ttl: 3000, + ad: '' + } + }; + + it('handles nobid responses', () => { + const result = spec.interpretResponse( + { body: serverResponse.noAd }, + bidRequests.banner + ); + expect(result.length).to.equal(0); + + const result2 = spec.interpretResponse( + { body: serverResponse.noAd2 }, + bidRequests.banner + ); + expect(result2.length).to.equal(0); + }) + }); + describe('spec.getUserSyncs', function () { + const syncResponse = [{ + body: { + request_id: 'biequa9oaph4we', + sync_urls: ['https://rdn1.test/sync?uid=9876543210', 'https://rdn2.test/sync?uid=9876543210'] + } + }]; + const nosyncResponse = [{ + body: { + request_id: 'biequa9oaph4we', + sync_urls: [] + } + }]; + let syncOptions + beforeEach(function () { + syncOptions = { + pixelEnabled: true + } + }); + it('sucess usersync url', function () { + const result = []; + result.push({type: 'image', url: 'https://rdn1.test/sync?uid=9876543210'}); + result.push({type: 'image', url: 'https://rdn2.test/sync?uid=9876543210'}); + expect(spec.getUserSyncs(syncOptions, syncResponse)).to.deep.equal(result); + }); + }); +}); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 29cc6cb2c0a..bfc8de0f20d 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2,13 +2,9 @@ import {expect} from 'chai'; import adapterManager from 'src/adapterManager.js'; import {spec, getPriceGranularity, masSizeOrdering, resetUserSync, hasVideoMediaType, FASTLANE_ENDPOINT} from 'modules/rubiconBidAdapter.js'; import {parse as parseQuery} from 'querystring'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {userSync} from 'src/userSync.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; -import find from 'core-js/library/fn/array/find.js'; - -var CONSTANTS = require('src/constants.json'); +import find from 'core-js-pure/features/array/find.js'; const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index 4f2fd9d641a..9525146a629 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -16,6 +16,23 @@ const gdpr = { } } +const schain = { + 'schain': { + 'validation': 'strict', + 'config': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'some.com', + 'sid': '00001', + 'hp': 1 + } + ] + } + } +} + const bidRequestCommonParams = { 'bidder': 'showheroes-bs', 'params': { @@ -91,8 +108,11 @@ const bidRequestBannerMultiSizes = { } const bidRequestVideoAndBanner = { - ...bidRequestBanner, - ...bidRequestVideo + ...bidRequestCommonParams, + 'mediaTypes': { + ...bidRequestBanner.mediaTypes, + ...bidRequestVideo.mediaTypes + } } describe('shBidAdapter', function () { @@ -131,23 +151,29 @@ describe('shBidAdapter', function () { it('check sizes formats', function () { const request = spec.buildRequests([{ 'params': {}, - 'mediaTypes': {}, - 'sizes': [[640, 480]], + 'mediaTypes': { + 'banner': { + 'sizes': [[320, 240]] + } + }, }], bidderRequest) const payload = request.data.requests[0]; expect(payload).to.be.an('object'); - expect(payload.size).to.have.property('width', 640); - expect(payload.size).to.have.property('height', 480); + expect(payload.size).to.have.property('width', 320); + expect(payload.size).to.have.property('height', 240); const request2 = spec.buildRequests([{ 'params': {}, - 'mediaTypes': {}, - 'sizes': [320, 240], + 'mediaTypes': { + 'video': { + 'playerSize': [640, 360] + } + }, }], bidderRequest) const payload2 = request2.data.requests[0]; expect(payload).to.be.an('object'); - expect(payload2.size).to.have.property('width', 320); - expect(payload2.size).to.have.property('height', 240); + expect(payload2.size).to.have.property('width', 640); + expect(payload2.size).to.have.property('height', 360); }) it('should get size from mediaTypes when sizes property is empty', function () { @@ -245,6 +271,16 @@ describe('shBidAdapter', function () { expect(payload).to.be.an('object'); expect(payload.gdprConsent).to.eql(gdpr.gdprConsent) }) + + it('passes schain object if present', function() { + const request = spec.buildRequests([{ + ...bidRequestVideo, + ...schain + }], bidderRequest) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload.schain).to.eql(schain.schain); + }) }) describe('interpretResponse', function () { diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index b67da86ebf2..f6fa7a20594 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -1,5 +1,6 @@ import { assert, expect } from 'chai'; import { BANNER } from 'src/mediaTypes.js'; +import {config} from 'src/config.js'; import { spec } from 'modules/synacormediaBidAdapter.js'; describe('synacormediaBidAdapter ', function () { @@ -779,6 +780,44 @@ describe('synacormediaBidAdapter ', function () { expect(spec.interpretResponse({ body: null })).to.not.exist; expect(spec.interpretResponse({ body: 'some error text' })).to.not.exist; }); + + it('should not include videoCacheKey property on the returned response when cache url is present in the config', function () { + let sandbox = sinon.sandbox.create(); + let serverRespVideo = { + body: { + id: 'abcd1234', + seatbid: [ + { + bid: [ + { + id: '11339128001692337~9999~0', + impid: 'v2da7322b2df61f-640x480', + price: 0.45, + nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', + adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', + adomain: [ 'psacentral.org' ], + cid: 'bidder-crid', + crid: 'bidder-cid', + cat: [] + } + ], + seat: '9999' + } + ] + } + }; + + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + 'cache.url': 'faKeCacheUrl' + }; + return config[key]; + }); + + let resp = spec.interpretResponse(serverRespVideo); + sandbox.restore(); + expect(resp[0].videoCacheKey).to.not.exist; + }); }); describe('getUserSyncs', function () { it('should return a usersync when iframes is enabled', function () { diff --git a/test/spec/modules/widespaceBidAdapter_spec.js b/test/spec/modules/widespaceBidAdapter_spec.js index 95f0117e5d1..382bf2c593e 100644 --- a/test/spec/modules/widespaceBidAdapter_spec.js +++ b/test/spec/modules/widespaceBidAdapter_spec.js @@ -1,6 +1,6 @@ import {expect} from 'chai'; import {spec, storage} from 'modules/widespaceBidAdapter.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; describe('+widespaceAdatperTest', function () { // Dummy bid request diff --git a/test/spec/sizeMapping_spec.js b/test/spec/sizeMapping_spec.js index b6b8c00ada3..78dd9797c36 100644 --- a/test/spec/sizeMapping_spec.js +++ b/test/spec/sizeMapping_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { resolveStatus, setSizeConfig, sizeSupported } from 'src/sizeMapping.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import includes from 'core-js-pure/features/array/includes.js'; let utils = require('src/utils'); let deepClone = utils.deepClone; diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index 8ee345166e1..9ccdd6aef59 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -11,8 +11,8 @@ import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; import { setSizeConfig } from 'src/sizeMapping.js'; -import find from 'core-js/library/fn/array/find.js'; -import includes from 'core-js/library/fn/array/includes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; import s2sTesting from 'modules/s2sTesting.js'; var events = require('../../../../src/events'); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 252c074cd61..7b4061850cb 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -13,22 +13,16 @@ import { targeting, newTargeting, filters } from 'src/targeting.js'; import { config as configObj } from 'src/config.js'; import * as ajaxLib from 'src/ajax.js'; import * as auctionModule from 'src/auction.js'; -import { newBidder, registerBidder } from 'src/adapters/bidderFactory.js'; +import { registerBidder } from 'src/adapters/bidderFactory.js'; import { _sendAdToCreative } from 'src/secureCreatives.js'; -import find from 'core-js/library/fn/array/find.js'; +import find from 'core-js-pure/features/array/find.js'; var assert = require('chai').assert; var expect = require('chai').expect; -var urlParse = require('url-parse'); - -var prebid = require('src/prebid'); var utils = require('src/utils'); -var bidfactory = require('src/bidfactory'); -var adloader = require('test/mocks/adloaderStub'); var adapterManager = require('src/adapterManager').default; var events = require('src/events'); -var adserver = require('src/adserver'); var CONSTANTS = require('src/constants.json'); // These bid adapters are required to be loaded for the following tests to work