diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9b7c39d53d0..584f6f8894a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ "master" ] + branches: [ "master"] pull_request: # The branches below must be a subset of the branches above - branches: [ "master" ] + branches: [ "master"] schedule: - cron: '22 11 * * 0' diff --git a/integrationExamples/gpt/captifyRtdProvider_example.html b/integrationExamples/gpt/captifyRtdProvider_example.html deleted file mode 100644 index 955fbf8be70..00000000000 --- a/integrationExamples/gpt/captifyRtdProvider_example.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - -
-

- Module will add key/value pairs in ad calls. - Check out for captify_segments key in the payload sent to Xandr to endpoint https://ib.adnxs.com/ut/v3/prebid : keywords.key[captify_segments] should have an array of string as value. - This array will have Xandr RTSS segment ids. -

-
-

Basic Prebid.js Example with CaptifyRTD

-
Div-1
-
- -
- - - diff --git a/integrationExamples/gpt/growthcode.html b/integrationExamples/gpt/growthcode.html index ede51d2d869..d8ad6c4a5af 100644 --- a/integrationExamples/gpt/growthcode.html +++ b/integrationExamples/gpt/growthcode.html @@ -7,10 +7,13 @@ - - - - - - - - - - - - -
-

Note: for this example to work, you need access to a bid simulation tool from your MASS enabled Exchange partner.

-
- -
-
- - diff --git a/libraries/analyticsAdapter/AnalyticsAdapter.js b/libraries/analyticsAdapter/AnalyticsAdapter.js index b6e270b3c3c..e1933d215e4 100644 --- a/libraries/analyticsAdapter/AnalyticsAdapter.js +++ b/libraries/analyticsAdapter/AnalyticsAdapter.js @@ -134,13 +134,7 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler } handlers = Object.fromEntries( Array.from(trackedEvents) .map((ev) => { - const handler = ev === CONSTANTS.EVENTS.AUCTION_INIT - ? (args) => { - // TODO: remove this special case in v8 - args.config = typeof config === 'object' ? config.options || {} : {}; - this.enqueue({eventType: ev, args}); - } - : (args) => this.enqueue({eventType: ev, args}); + const handler = (args) => this.enqueue({eventType: ev, args}); events.on(ev, handler); return [ev, handler]; }) diff --git a/libraries/appnexusKeywords/anKeywords.js b/libraries/appnexusKeywords/anKeywords.js new file mode 100644 index 00000000000..5dc0b453253 --- /dev/null +++ b/libraries/appnexusKeywords/anKeywords.js @@ -0,0 +1,140 @@ +import {_each, deepAccess, getValueString, isArray, isStr, mergeDeep, isNumber} from '../../src/utils.js'; +import {getAllOrtbKeywords} from '../keywords/keywords.js'; +import {CLIENT_SECTIONS} from '../../src/fpd/oneClient.js'; + +const ORTB_SEGTAX_KEY_MAP = { + 526: '1plusX', + 527: '1plusX', + 541: 'captify_segments', + 540: 'perid' +}; +const ORTB_SEG_PATHS = ['user.data'].concat( + CLIENT_SECTIONS.map((prefix) => `${prefix}.content.data`) +); + +/** + * Converts an object of arrays (either strings or numbers) into an array of objects containing key and value properties + * normally read from bidder params + * eg { foo: ['bar', 'baz'], fizz: ['buzz'] } + * becomes [{ key: 'foo', value: ['bar', 'baz']}, {key: 'fizz', value: ['buzz']}] + * @param {Object} keywords object of arrays representing keyvalue pairs + * @param {string} paramName name of parent object (eg 'keywords') containing keyword data, used in error handling + * @returns {Array<{key, value}>} + */ +export function transformBidderParamKeywords(keywords, paramName = 'keywords') { + const arrs = []; + + _each(keywords, (v, k) => { + if (isArray(v)) { + let values = []; + _each(v, (val) => { + val = getValueString(paramName + '.' + k, val); + if (val || val === '') { + values.push(val); + } + }); + v = values; + } else { + v = getValueString(paramName + '.' + k, v); + if (isStr(v)) { + v = [v]; + } else { + return; + } // unsuported types - don't send a key + } + v = v.filter(kw => kw !== '') + const entry = {key: k} + if (v.length > 0) { + entry.value = v; + } + arrs.push(entry); + }); + + return arrs; +} + +// converts a comma separated list of keywords into the standard keyword object format used in appnexus bid params +// 'genre=rock,genre=pop,pets=dog,music' goes to { 'genre': ['rock', 'pop'], 'pets': ['dog'], 'music': [''] } +export function convertKeywordStringToANMap(keyStr) { + if (isStr(keyStr) && keyStr !== '') { + // will split based on commas and will eat white space before/after the comma + return convertKeywordsToANMap(keyStr.split(/\s*(?:,)\s*/)); + } else { + return {} + } +} + +/** + * @param {Array} kwarray: keywords as an array of strings + * @return {{}} appnexus-style keyword map + */ +function convertKeywordsToANMap(kwarray) { + const result = {}; + kwarray.forEach(kw => { + // if = exists, then split + if (kw.indexOf('=') !== -1) { + let kwPair = kw.split('='); + let key = kwPair[0]; + let val = kwPair[1]; + + // then check for existing key in result > if so add value to the array > if not, add new key and create value array + if (result.hasOwnProperty(key)) { + result[key].push(val); + } else { + result[key] = [val]; + } + } else { + if (!result.hasOwnProperty(kw)) { + result[kw] = []; + } + } + }) + return result; +} + +/** + * @param ortb2 + * @return {{}} appnexus-style keyword map using all keywords contained in ortb2 + */ +export function getANMapFromOrtbKeywords(ortb2) { + return convertKeywordsToANMap(getAllOrtbKeywords(ortb2)); +} + +export function getANKewyordParamFromMaps(...anKeywordMaps) { + return transformBidderParamKeywords( + mergeDeep(...anKeywordMaps.map(kwMap => Object.fromEntries( + Object.entries(kwMap || {}) + .map(([k, v]) => [k, (isNumber(v) || isStr(v)) ? [v] : v]) + ))) + ) +} + +export function getANKeywordParam(ortb2, ...anKeywordsMaps) { + return getANKewyordParamFromMaps( + getANMapFromOrtbKeywords(ortb2), + getANMapFromOrtbSegments(ortb2), + ...anKeywordsMaps + ) +} + +export function getANMapFromOrtbSegments(ortb2) { + let ortbSegData = {}; + ORTB_SEG_PATHS.forEach(path => { + let ortbSegsArrObj = deepAccess(ortb2, path) || []; + ortbSegsArrObj.forEach(segObj => { + // only read segment data from known sources + const segtax = ORTB_SEGTAX_KEY_MAP[deepAccess(segObj, 'ext.segtax')]; + if (segtax) { + segObj.segment.forEach(seg => { + // if source was in multiple locations of ortb or had multiple segments in same area, stack them together into an array + if (ortbSegData[segtax]) { + ortbSegData[segtax].push(seg.id); + } else { + ortbSegData[segtax] = [seg.id] + } + }); + } + }); + }); + return ortbSegData; +} diff --git a/libraries/categoryTranslationMapping/index.js b/libraries/categoryTranslationMapping/index.js new file mode 100644 index 00000000000..13b10423450 --- /dev/null +++ b/libraries/categoryTranslationMapping/index.js @@ -0,0 +1,100 @@ +/** + * Provides mapping objects used by bidders for categoryTranslation type logic for Adpod feature + */ +export const APPNEXUS_CATEGORY_MAPPING = { + '1': 'IAB20-3', + '2': 'IAB18-5', + '3': 'IAB10-1', + '4': 'IAB2-3', + '5': 'IAB19-8', + '6': 'IAB22-1', + '7': 'IAB18-1', + '8': 'IAB12-3', + '9': 'IAB5-1', + '10': 'IAB4-5', + '11': 'IAB13-4', + '12': 'IAB8-7', + '13': 'IAB9-7', + '14': 'IAB7-1', + '15': 'IAB20-18', + '16': 'IAB10-7', + '17': 'IAB19-18', + '18': 'IAB13-6', + '19': 'IAB18-4', + '20': 'IAB1-5', + '21': 'IAB1-6', + '22': 'IAB3-4', + '23': 'IAB19-13', + '24': 'IAB22-2', + '25': 'IAB3-9', + '26': 'IAB17-18', + '27': 'IAB19-6', + '28': 'IAB1-7', + '29': 'IAB9-30', + '30': 'IAB20-7', + '31': 'IAB20-17', + '32': 'IAB7-32', + '33': 'IAB16-5', + '34': 'IAB19-34', + '35': 'IAB11-5', + '36': 'IAB12-3', + '37': 'IAB11-4', + '38': 'IAB12-3', + '39': 'IAB9-30', + '41': 'IAB7-44', + '42': 'IAB7-1', + '43': 'IAB7-30', + '50': 'IAB19-30', + '51': 'IAB17-12', + '52': 'IAB19-30', + '53': 'IAB3-1', + '55': 'IAB13-2', + '56': 'IAB19-30', + '57': 'IAB19-30', + '58': 'IAB7-39', + '59': 'IAB22-1', + '60': 'IAB7-39', + '61': 'IAB21-3', + '62': 'IAB5-1', + '63': 'IAB12-3', + '64': 'IAB20-18', + '65': 'IAB11-2', + '66': 'IAB17-18', + '67': 'IAB9-9', + '68': 'IAB9-5', + '69': 'IAB7-44', + '71': 'IAB22-3', + '73': 'IAB19-30', + '74': 'IAB8-5', + '78': 'IAB22-1', + '85': 'IAB12-2', + '86': 'IAB22-3', + '87': 'IAB11-3', + '112': 'IAB7-32', + '113': 'IAB7-32', + '114': 'IAB7-32', + '115': 'IAB7-32', + '118': 'IAB9-5', + '119': 'IAB9-5', + '120': 'IAB9-5', + '121': 'IAB9-5', + '122': 'IAB9-5', + '123': 'IAB9-5', + '124': 'IAB9-5', + '125': 'IAB9-5', + '126': 'IAB9-5', + '127': 'IAB22-1', + '132': 'IAB1-2', + '133': 'IAB19-30', + '137': 'IAB3-9', + '138': 'IAB19-3', + '140': 'IAB2-3', + '141': 'IAB2-1', + '142': 'IAB2-3', + '143': 'IAB17-13', + '166': 'IAB11-4', + '175': 'IAB3-1', + '176': 'IAB13-4', + '182': 'IAB8-9', + '183': 'IAB3-5' +}; diff --git a/libraries/keywords/keywords.js b/libraries/keywords/keywords.js new file mode 100644 index 00000000000..645c9c8d38f --- /dev/null +++ b/libraries/keywords/keywords.js @@ -0,0 +1,31 @@ +import {CLIENT_SECTIONS} from '../../src/fpd/oneClient.js'; +import {deepAccess} from '../../src/utils.js'; + +const ORTB_KEYWORDS_PATHS = ['user.keywords'].concat( + CLIENT_SECTIONS.flatMap((prefix) => ['keywords', 'content.keywords'].map(suffix => `${prefix}.${suffix}`)) +); + +/** + * @param commaSeparatedKeywords: any number of either keyword arrays, or comma-separated keyword strings + * @returns an array with all unique keywords contained across all inputs + */ +export function mergeKeywords(...commaSeparatedKeywords) { + const keywords = new Set(); + commaSeparatedKeywords + .filter(kwds => kwds) + .flatMap(kwds => Array.isArray(kwds) ? kwds : kwds.split(',')) + .map(kw => kw.replace(/^\s*/, '').replace(/\s*$/, '')) + .filter(kw => kw) + .forEach(kw => keywords.add(kw)); + return Array.from(keywords.keys()); +} + +/** + * Get an array with all keywords contained in an ortb2 object. + */ +export function getAllOrtbKeywords(ortb2, ...extraCommaSeparatedKeywords) { + return mergeKeywords( + ...ORTB_KEYWORDS_PATHS.map(path => deepAccess(ortb2, path)), + ...extraCommaSeparatedKeywords + ) +} diff --git a/libraries/objectGuard/objectGuard.js b/libraries/objectGuard/objectGuard.js new file mode 100644 index 00000000000..cf3d2f38256 --- /dev/null +++ b/libraries/objectGuard/objectGuard.js @@ -0,0 +1,99 @@ +import {isData, objectTransformer, sessionedApplies} from '../../src/activities/redactor.js'; +import {deepAccess, deepClone, deepEqual, deepSetValue} from '../../src/utils.js'; + +/** + * @typedef {Object} ObjectGuard + * @property {*} obj a view on the guarded object + * @property {function(): void} verify a function that checks for and rolls back disallowed changes to the guarded object + */ + +/** + * Create a factory function for object guards using the given rules. + * + * An object guard is a pair {obj, verify} where: + * - `obj` is a view on the guarded object that applies "redact" rules (the same rules used in activites/redactor.js) + * - `verify` is a function that, when called, will check that the guarded object was not modified + * in a way that violates any "write protect" rules, and rolls back any offending changes. + * + * This is meant to provide sandboxed version of a privacy-sensitive object, where reads + * are filtered through redaction rules and writes are checked against write protect rules. + * + * @param {Array[TransformationRule]} rules + * @return {function(*, ...[*]): ObjectGuard} + */ +export function objectGuard(rules) { + const root = {}; + const writeRules = []; + + rules.forEach(rule => { + if (rule.wp) writeRules.push(rule); + if (!rule.get) return; + rule.paths.forEach(path => { + let node = root; + path.split('.').forEach(el => { + node.children = node.children || {}; + node.children[el] = node.children[el] || {}; + node = node.children[el]; + }) + node.rule = rule; + }); + }); + + const wpTransformer = objectTransformer(writeRules); + + function mkGuard(obj, tree, applies) { + return new Proxy(obj, { + get(target, prop, receiver) { + const val = Reflect.get(target, prop, receiver); + if (tree.hasOwnProperty(prop)) { + const {children, rule} = tree[prop]; + if (children && val != null && typeof val === 'object') { + return mkGuard(val, children, applies); + } else if (rule && isData(val) && applies(rule)) { + return rule.get(val); + } + } + return val; + }, + }); + } + + function mkVerify(transformResult) { + return function () { + transformResult.forEach(fn => fn()); + } + } + + return function guard(obj, ...args) { + const session = {}; + return { + obj: mkGuard(obj, root.children || {}, sessionedApplies(session, ...args)), + verify: mkVerify(wpTransformer(session, obj, ...args)) + } + }; +} + +/** + * @param {TransformationRuleDef} ruleDef + * @return {TransformationRule} + */ +export function writeProtectRule(ruleDef) { + return Object.assign({ + wp: true, + run(root, path, object, property, applies) { + const origHasProp = object && object.hasOwnProperty(property); + const original = origHasProp ? object[property] : undefined; + const origCopy = origHasProp && original != null && typeof original === 'object' ? deepClone(original) : original; + return function () { + const object = path == null ? root : deepAccess(root, path); + const finalHasProp = object && isData(object[property]); + const finalValue = finalHasProp ? object[property] : undefined; + if (!origHasProp && finalHasProp && applies()) { + delete object[property]; + } else if ((origHasProp !== finalHasProp || finalValue !== original || !deepEqual(finalValue, origCopy)) && applies()) { + deepSetValue(root, (path == null ? [] : [path]).concat(property).join('.'), origCopy); + } + } + } + }, ruleDef) +} diff --git a/libraries/objectGuard/ortbGuard.js b/libraries/objectGuard/ortbGuard.js new file mode 100644 index 00000000000..7911b378c3d --- /dev/null +++ b/libraries/objectGuard/ortbGuard.js @@ -0,0 +1,88 @@ +import {isActivityAllowed} from '../../src/activities/rules.js'; +import {ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD} from '../../src/activities/activities.js'; +import { + appliesWhenActivityDenied, + ortb2TransmitRules, + ORTB_EIDS_PATHS, + ORTB_UFPD_PATHS +} from '../../src/activities/redactor.js'; +import {objectGuard, writeProtectRule} from './objectGuard.js'; +import {mergeDeep} from '../../src/utils.js'; + +function ortb2EnrichRules(isAllowed = isActivityAllowed) { + return [ + { + name: ACTIVITY_ENRICH_EIDS, + paths: ORTB_EIDS_PATHS, + applies: appliesWhenActivityDenied(ACTIVITY_ENRICH_EIDS, isAllowed) + }, + { + name: ACTIVITY_ENRICH_UFPD, + paths: ORTB_UFPD_PATHS, + applies: appliesWhenActivityDenied(ACTIVITY_ENRICH_UFPD, isAllowed) + } + ].map(writeProtectRule) +} + +export function ortb2GuardFactory(isAllowed = isActivityAllowed) { + return objectGuard(ortb2TransmitRules(isAllowed).concat(ortb2EnrichRules(isAllowed))); +} + +/** + * + * + * @typedef {Function} ortb2Guard + * @param {{}} ortb2 ORTB object to guard + * @param {{}} params activity params to use for activity checks + * @returns {ObjectGuard} + */ + +/* + * Get a guard for an ORTB object. Read access is restricted in the same way it'd be redacted (see activites/redactor.js); + * and writes are checked against the enrich* activites. + * + * @type ortb2Guard + */ +export const ortb2Guard = ortb2GuardFactory(); + +export function ortb2FragmentsGuardFactory(guardOrtb2 = ortb2Guard) { + return function guardOrtb2Fragments(fragments, params) { + fragments.global = fragments.global || {}; + fragments.bidder = fragments.bidder || {}; + const bidders = new Set(Object.keys(fragments.bidder)); + const verifiers = []; + + function makeGuard(ortb2) { + const guard = guardOrtb2(ortb2, params); + verifiers.push(guard.verify); + return guard.obj; + } + + const obj = { + global: makeGuard(fragments.global), + bidder: Object.fromEntries(Object.entries(fragments.bidder).map(([bidder, ortb2]) => [bidder, makeGuard(ortb2)])) + }; + + return { + obj, + verify() { + Object.entries(obj.bidder) + .filter(([bidder]) => !bidders.has(bidder)) + .forEach(([bidder, ortb2]) => { + const repl = {}; + const guard = guardOrtb2(repl, params); + mergeDeep(guard.obj, ortb2); + guard.verify(); + fragments.bidder[bidder] = repl; + }) + verifiers.forEach(fn => fn()); + } + } + } +} + +/** + * Get a guard for an ortb2Fragments object. + * @type {function(*, *): ObjectGuard} + */ +export const guardOrtb2Fragments = ortb2FragmentsGuardFactory(); diff --git a/libraries/ortbConverter/processors/default.js b/libraries/ortbConverter/processors/default.js index 00922608707..8db2c1c461e 100644 --- a/libraries/ortbConverter/processors/default.js +++ b/libraries/ortbConverter/processors/default.js @@ -1,4 +1,4 @@ -import {deepSetValue, mergeDeep} from '../../../src/utils.js'; +import {generateUUID, mergeDeep} from '../../../src/utils.js'; import {bannerResponseProcessor, fillBannerImp} from './banner.js'; import {fillVideoImp, fillVideoResponse} from './video.js'; import {setResponseMediaType} from './mediaType.js'; @@ -21,17 +21,16 @@ export const DEFAULT_PROCESSORS = { fn: clientSectionChecker('ORTB request') }, props: { - // sets request properties id, tmax, test, source.tid + // sets request properties id, tmax, test fn(ortbRequest, bidderRequest) { Object.assign(ortbRequest, { - id: ortbRequest.id || bidderRequest.auctionId, + id: ortbRequest.id || generateUUID(), test: ortbRequest.test || 0 }); const timeout = parseInt(bidderRequest.timeout, 10); if (!isNaN(timeout)) { ortbRequest.tmax = timeout; } - deepSetValue(ortbRequest, 'source.tid', ortbRequest.source?.tid || bidderRequest.auctionId); } } }, diff --git a/modules/.submodules.json b/modules/.submodules.json index 226673488cf..b45fb7f2303 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -38,13 +38,15 @@ "tapadIdSystem", "teadsIdSystem", "tncIdSystem", - "trustpidSystem", + "utiqSystem", "uid2IdSystem", + "euidIdSystem", "unifiedIdSystem", "verizonMediaIdSystem", "zeotapIdPlusIdSystem", "adqueryIdSystem", - "gravitoIdSystem" + "gravitoIdSystem", + "freepassIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/1plusXRtdProvider.js b/modules/1plusXRtdProvider.js index 390f4f4cd81..c5c4594ff22 100644 --- a/modules/1plusXRtdProvider.js +++ b/modules/1plusXRtdProvider.js @@ -1,10 +1,9 @@ import { submodule } from '../src/hook.js'; -import { config } from '../src/config.js'; import { ajax } from '../src/ajax.js'; import { logMessage, logError, - deepAccess, mergeDeep, - isNumber, isArray, deepSetValue + deepAccess, deepSetValue, mergeDeep, + isNumber, isArray, } from '../src/utils.js'; // Constants @@ -14,7 +13,6 @@ const ORTB2_NAME = '1plusX.com' const PAPI_VERSION = 'v1.0'; const LOG_PREFIX = '[1plusX RTD Module]: '; const OPE_FPID = 'ope_fpid' -const LEGACY_SITE_KEYWORDS_BIDDERS = ['appnexus']; export const segtaxes = { // cf. https://github.com/InteractiveAdvertisingBureau/openrtb/pull/108 AUDIENCE: 526, @@ -151,21 +149,11 @@ const getTargetingDataFromPapi = (papiUrl) => { * @param {string[]} topics Represents the topics of the page * @returns {Object} Object describing the updates to make on bidder configs */ -export const buildOrtb2Updates = ({ segments = [], topics = [] }, bidder) => { - // Currently appnexus bidAdapter doesn't support topics in `site.content.data.segment` - // Therefore, writing them in `site.keywords` until it's supported - // Other bidAdapters do fine with `site.content.data.segment` - const writeToLegacySiteKeywords = LEGACY_SITE_KEYWORDS_BIDDERS.includes(bidder); - if (writeToLegacySiteKeywords) { - const site = { - keywords: topics.join(',') - }; - return { site }; - } - +export const buildOrtb2Updates = ({ segments = [], topics = [] }) => { const userData = { name: ORTB2_NAME, - segment: segments.map((segmentId) => ({ id: segmentId })) + segment: segments.map((segmentId) => ({ id: segmentId })), + ext: { segtax: segtaxes.AUDIENCE } }; const siteContentData = { name: ORTB2_NAME, @@ -179,74 +167,46 @@ export const buildOrtb2Updates = ({ segments = [], topics = [] }, bidder) => { * Merges the targeting data with the existing config for bidder and updates * @param {string} bidder Bidder for which to set config * @param {Object} ortb2Updates Updates to be applied to bidder config - * @param {Object} bidderConfigs All current bidder configs - * @returns {Object} Updated bidder config + * @param {Object} biddersOrtb2 All current bidder configs */ -export const updateBidderConfig = (bidder, ortb2Updates, bidderConfigs) => { - const { site, siteContentData, userData } = ortb2Updates; - const bidderConfigCopy = mergeDeep({}, bidderConfigs[bidder]); - - if (site) { - // Legacy : cf. comment on buildOrtb2Updates first lines - const currentSite = deepAccess(bidderConfigCopy, 'ortb2.site'); - const updatedSite = mergeDeep(currentSite, site); - deepSetValue(bidderConfigCopy, 'ortb2.site', updatedSite); - } +export const updateBidderConfig = (bidder, ortb2Updates, biddersOrtb2) => { + const { siteContentData, userData } = ortb2Updates; + mergeDeep(biddersOrtb2, { [bidder]: {} }); + const bidderConfig = deepAccess(biddersOrtb2, bidder); - if (siteContentData) { - const siteDataPath = 'ortb2.site.content.data'; - const currentSiteContentData = deepAccess(bidderConfigCopy, siteDataPath) || []; + { + const siteDataPath = 'site.content.data'; + const currentSiteContentData = deepAccess(bidderConfig, siteDataPath) || []; const updatedSiteContentData = [ ...currentSiteContentData.filter(({ name }) => name != siteContentData.name), siteContentData ]; - deepSetValue(bidderConfigCopy, siteDataPath, updatedSiteContentData); + deepSetValue(bidderConfig, siteDataPath, updatedSiteContentData); } - if (userData) { - const userDataPath = 'ortb2.user.data'; - const currentUserData = deepAccess(bidderConfigCopy, userDataPath) || []; + { + const userDataPath = 'user.data'; + const currentUserData = deepAccess(bidderConfig, userDataPath) || []; const updatedUserData = [ ...currentUserData.filter(({ name }) => name != userData.name), userData ]; - deepSetValue(bidderConfigCopy, userDataPath, updatedUserData); + deepSetValue(bidderConfig, userDataPath, updatedUserData); } - - return bidderConfigCopy; }; -const setAppnexusAudiences = (audiences) => { - config.setConfig({ - appnexusAuctionKeywords: { - '1plusX': audiences, - }, - }); -} - /** * Updates bidder configs with the targeting data retreived from Profile API * @param {Object} papiResponse Response from Profile API * @param {Object} config Module configuration * @param {string[]} config.bidders Bidders specified in module's configuration */ -export const setTargetingDataToConfig = (papiResponse, { bidders }) => { - const bidderConfigs = config.getBidderConfig(); +export const setTargetingDataToConfig = (papiResponse, { bidders, biddersOrtb2 }) => { const { s: segments, t: topics } = papiResponse; + const ortb2Updates = buildOrtb2Updates({ segments, topics }); for (const bidder of bidders) { - const ortb2Updates = buildOrtb2Updates({ segments, topics }, bidder); - const updatedBidderConfig = updateBidderConfig(bidder, ortb2Updates, bidderConfigs); - if (updatedBidderConfig) { - config.setBidderConfig({ - bidders: [bidder], - config: updatedBidderConfig - }); - } - if (bidder === 'appnexus') { - // Do the legacy stuff for appnexus with segments - setAppnexusAudiences(segments); - } + updateBidderConfig(bidder, ortb2Updates, biddersOrtb2); } } @@ -272,13 +232,14 @@ const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, userConsent try { // Get the required config const { customerId, bidders } = extractConfig(moduleConfig, reqBidsConfigObj); + const { ortb2Fragments: { bidder: biddersOrtb2 } } = reqBidsConfigObj; // Get PAPI URL const papiUrl = getPapiUrl(customerId, extractConsent(userConsent) || {}, extractFpid()) // Call PAPI getTargetingDataFromPapi(papiUrl) .then((papiResponse) => { logMessage(LOG_PREFIX, 'Get targeting data request successful'); - setTargetingDataToConfig(papiResponse, { bidders }); + setTargetingDataToConfig(papiResponse, { bidders, biddersOrtb2 }); callback(); }) } catch (error) { diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 606933e7b63..26fc0a11fd5 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -1,19 +1,19 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; import { deepAccess, - uniques, - isArray, + getWindowSelf, getWindowTop, + isArray, isGptPubadsDefined, isSlotMatchingAdUnitCode, logInfo, logWarn, - getWindowSelf, mergeDeep, - pick + pick, + uniques } from '../src/utils.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; // **************************** UTILS *************************** // const BIDDER_CODE = '33across'; @@ -183,7 +183,8 @@ function buildRequests(bidRequests, bidderRequest) { uspConsent, pageUrl, referer, - ttxSettings + ttxSettings, + bidderRequest, }) ) } @@ -246,7 +247,7 @@ function _getMRAKey(bidRequest) { } // Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request -function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageUrl, referer, ttxSettings }) { +function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageUrl, referer, ttxSettings, bidderRequest }) { const ttxRequest = {}; const firstBidRequest = bidRequests[0]; const { siteId, test } = firstBidRequest.params; @@ -271,7 +272,7 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageU ttxRequest.site.ref = referer; } - ttxRequest.id = firstBidRequest.auctionId; + ttxRequest.id = bidderRequest?.bidderRequestId; if (gdprConsent.consentString) { ttxRequest.user = setExtensions(ttxRequest.user, { @@ -681,7 +682,6 @@ function _createBidResponse(bid, cur) { bid.adomain && bid.adomain.length; const bidResponse = { requestId: bid.impid, - bidderCode: BIDDER_CODE, cpm: bid.price, width: bid.w, height: bid.h, diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js index 12dc36d694c..36935e80d3b 100644 --- a/modules/adWMGBidAdapter.js +++ b/modules/adWMGBidAdapter.js @@ -59,11 +59,12 @@ export const spec = { } const request = { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: bidRequest.auctionId, requestId: bidRequest.bidId, bidRequestsCount: bidRequest.bidRequestsCount, bidderRequestId: bidRequest.bidderRequestId, - transactionId: bidRequest.transactionId, + transactionId: bidRequest.ortb2Imp?.ext?.tid, referrer: referrer, timeout: timeout, adUnit: adUnit, diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 86021d2a90c..57dd80aa9b1 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -44,7 +44,11 @@ const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; const GVLID = 617; export const storage = getStorageManager({bidderCode: BIDDER_CODE}); -export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js'; + +const BB_PUBLICATION = 'adagio'; +const BB_RENDERER_DEFAULT = 'renderer'; +export const BB_RENDERER_URL = `https://${BB_PUBLICATION}.bbvms.com/r/$RENDERER.js`; + const MAX_SESS_DURATION = 30 * 60 * 1000; const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0ki9JJI2uYm/6VEYo8TJED9WfMkiJ4vf02CW3RvSWwc35bif2SK1L8Nn/GfFYr/2/GG/Rm0vUsv+vBHky6nuuYls20Og0HDhMgaOlXoQ/cxMuiy5QSktp'; const ADAGIO_PUBKEY_E = 65537; @@ -62,7 +66,7 @@ export const ORTB_VIDEO_PARAMS = { 'startdelay': (value) => isInteger(value), 'placement': (value) => isInteger(value), 'linearity': (value) => isInteger(value), - 'skip': (value) => isInteger(value), + 'skip': (value) => [1, 0].includes(value), 'skipmin': (value) => isInteger(value), 'skipafter': (value) => isInteger(value), 'sequence': (value) => isInteger(value), @@ -443,16 +447,6 @@ function _buildVideoBidRequest(bidRequest) { }); } -function _renderer(bid) { - bid.renderer.push(() => { - if (typeof window.ADAGIO.outstreamPlayer === 'function') { - window.ADAGIO.outstreamPlayer(bid); - } else { - logError(`${LOG_PREFIX} Adagio outstream player is not defined`); - } - }); -} - function _parseNativeBidResponse(bid) { if (!bid.admNative || !Array.isArray(bid.admNative.assets)) { logError(`${LOG_PREFIX} Invalid native response`); @@ -866,7 +860,7 @@ function storeRequestInAdagioNS(bidRequest) { bidder: bidRequest.bidder, params: bidRequest.params // use the updated bid.params object with auto-detected params }], - auctionId: bidRequest.auctionId, + auctionId: bidRequest.auctionId, // this auctionId has been generated by adagioBidAdapter pageviewId: internal.getPageviewId(), printNumber, localPbjs: '$$PREBID_GLOBAL$$', @@ -875,12 +869,83 @@ function storeRequestInAdagioNS(bidRequest) { // (legacy) Store internal adUnit information w.ADAGIO.adUnits[bidRequest.adUnitCode] = { - auctionId: bidRequest.auctionId, + auctionId: bidRequest.auctionId, // this auctionId has been generated by adagioBidAdapter pageviewId: internal.getPageviewId(), printNumber, }; } +// See https://support.bluebillywig.com/developers/vast-renderer/ +const OUTSTREAM_RENDERER = { + bootstrapPlayer: function(bid) { + const rendererCode = bid.outstreamRendererCode; + + 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) { + logError(`${LOG_PREFIX} no vastXml or vastUrl on bid`); + return; + } + + if (!window.bluebillywig || !window.bluebillywig.renderers || !window.bluebillywig.renderers.length) { + logError(`${LOG_PREFIX} no BlueBillywig renderers found!`); + return; + } + + const rendererId = this.getRendererId(BB_PUBLICATION, rendererCode); + + const override = {} + if (bid.skipOffset) { + override.skipOffset = bid.skipOffset.toString() + } + + const renderer = window.bluebillywig.renderers.find(bbr => bbr._id === rendererId); + if (!renderer) { + logError(`${LOG_PREFIX} couldn't find a renderer with ID ${rendererId}`); + return; + } + + const el = document.getElementById(bid.adUnitCode); + + renderer.bootstrap(config, el, override); + }, + newRenderer: function(adUnitCode, rendererCode) { + const rendererUrl = BB_RENDERER_URL.replace('$RENDERER', rendererCode); + + const renderer = Renderer.install({ + url: rendererUrl, + loaded: false, + adUnitCode + }); + + try { + renderer.setRender(this.outstreamRender); + } catch (err) { + logError(`${LOG_PREFIX} error trying to setRender`, err); + } + + return renderer; + }, + outstreamRender: function(bid) { + bid.renderer.push(() => { + OUTSTREAM_RENDERER.bootstrapPlayer(bid) + }); + }, + getRendererId: function(publication, renderer) { + // By convention, the RENDERER_ID is always the publication name (adagio) and the ad unit code (eg. renderer) + // joined together by a dash. It's used to identify the correct renderer instance on the page in case there's multiple. + return `${publication}-${renderer}`; + } +}; + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -916,7 +981,14 @@ export const spec = { const syncEnabled = deepAccess(config.getConfig('userSync'), 'syncEnabled') const usIfr = syncEnabled && userSync.canBidderRegisterSync('iframe', 'adagio') - const adUnits = _map(validBidRequests, (bidRequest) => { + const aucId = generateUUID() + + const adUnits = _map(validBidRequests, (rawBidRequest) => { + const bidRequest = {...rawBidRequest} + + // Fix https://github.com/prebid/Prebid.js/issues/9781 + bidRequest.auctionId = aucId + const globalFeatures = GlobalExchange.getOrSetGlobalFeatures(); const features = { ...globalFeatures, @@ -1029,6 +1101,12 @@ export const spec = { storeRequestInAdagioNS(bidRequest); + // Remove these fields at the very end, so we can still use them before. + delete bidRequest.transactionId; + delete bidRequest.ortb2Imp; + delete bidRequest.ortb2; + delete bidRequest.sizes; + return bidRequest; }); @@ -1110,21 +1188,18 @@ export const spec = { const mediaTypeContext = deepAccess(bidReq, 'mediaTypes.video.context'); // Adagio SSP returns a `vastXml` only. No `vastUrl` nor `videoCacheKey`. if (!bidObj.vastUrl && bidObj.vastXml) { - bidObj.vastUrl = 'data:text/xml;charset=utf-8;base64,' + btoa(bidObj.vastXml.replace(/\\"/g, '"')); + bidObj.vastUrl = 'data:text/xml;charset=utf-8;base64,' + window.btoa(bidObj.vastXml.replace(/\\"/g, '"')); } if (mediaTypeContext === OUTSTREAM) { - bidObj.renderer = Renderer.install({ - id: bidObj.requestId, - adUnitCode: bidObj.adUnitCode, - url: bidObj.urlRenderer || RENDERER_URL, - config: { - ...deepAccess(bidReq, 'mediaTypes.video'), - ...deepAccess(bidObj, 'outstream', {}) - } - }); - - bidObj.renderer.setRender(_renderer); + bidObj.outstreamRendererCode = deepAccess(bidReq, 'params.rendererCode', BB_RENDERER_DEFAULT) + + if (deepAccess(bidReq, 'mediaTypes.video.skip')) { + const skipOffset = deepAccess(bidReq, 'mediaTypes.video.skipafter', 5) // default 5s. + bidObj.skipOffset = skipOffset + } + + bidObj.renderer = OUTSTREAM_RENDERER.newRenderer(bidObj.adUnitCode, bidObj.outstreamRendererCode); } } @@ -1170,33 +1245,8 @@ export const spec = { * @returns {object} updated params */ transformBidParams(params, isOrtb, adUnit, bidRequests) { - const adagioBidderRequest = find(bidRequests, bidRequest => bidRequest.bidderCode === 'adagio'); - const adagioBid = find(adagioBidderRequest.bids, bid => bid.adUnitCode === adUnit.code); - - if (isOrtb) { - autoFillParams(adagioBid); - - adagioBid.params.auctionId = deepAccess(adagioBidderRequest, 'auctionId'); - - const globalFeatures = GlobalExchange.getOrSetGlobalFeatures(); - adagioBid.params.features = { - ...globalFeatures, - print_number: getPrintNumber(adagioBid.adUnitCode, adagioBidderRequest).toString(), - adunit_position: getSlotPosition(adagioBid.params.adUnitElementId) // adUnitElementId à déplacer ??? - }; - - adagioBid.params.pageviewId = internal.getPageviewId(); - adagioBid.params.prebidVersion = '$prebid.version$'; - adagioBid.params.data = GlobalExchange.getExchangeData(); - - if (deepAccess(adagioBid, 'mediaTypes.video.context') === OUTSTREAM) { - adagioBid.params.playerName = setPlayerName(adagioBid); - } - - storeRequestInAdagioNS(adagioBid); - } - - return adagioBid.params; + // We do not have a prebid server adapter. So let's return unchanged params. + return params; } }; diff --git a/modules/adbookpspBidAdapter.js b/modules/adbookpspBidAdapter.js index 917d18e9fae..cb03f2ffc17 100644 --- a/modules/adbookpspBidAdapter.js +++ b/modules/adbookpspBidAdapter.js @@ -203,7 +203,7 @@ function buildRegs(bidderRequest) { function buildSource(bidRequests, bidderRequest) { const source = { fd: 1, - tid: bidderRequest.auctionId, + tid: bidderRequest.ortb2.source.tid, }; const schain = deepAccess(bidRequests, '0.schain'); diff --git a/modules/addefendBidAdapter.js b/modules/addefendBidAdapter.js index d73c25935ee..dbb186fdc86 100644 --- a/modules/addefendBidAdapter.js +++ b/modules/addefendBidAdapter.js @@ -30,6 +30,7 @@ export const spec = { for (var i = 0; i < validBidRequests.length; i++) { let vb = validBidRequests[i]; let o = vb.params; + // TODO: fix auctionId/transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 bid.auctionId = vb.auctionId; o.bidId = vb.bidId; o.transactionId = vb.transactionId; diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index 82bd7f03ff0..5c4b03c3bb2 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -3,7 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {deepAccess, deepSetValue, mergeDeep, parseSizesInput, deepClone} from '../src/utils.js'; +import {deepAccess, deepClone, deepSetValue, mergeDeep, parseSizesInput} from '../src/utils.js'; import {config} from '../src/config.js'; import {Renderer} from '../src/Renderer.js'; @@ -58,7 +58,7 @@ export const spec = { const adxDomain = setOnAny(validBidRequests, 'params.adxDomain') || 'adx.adform.net'; const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; - const tid = bidderRequest.auctionId; + const tid = bidderRequest.ortb2?.source?.tid; const test = setOnAny(validBidRequests, 'params.test'); const currency = getConfig('currency.adServerCurrency'); const cur = currency && [ currency ]; @@ -139,7 +139,7 @@ export const spec = { }); const request = { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, site, app, user, diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index 199dd0955c7..f18b7629d8f 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -1,8 +1,8 @@ -import {tryAppendQueryString, getBidIdParameter, escapeUnsafeChars} from '../src/utils.js'; +import {tryAppendQueryString, getBidIdParameter, escapeUnsafeChars, deepAccess} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; const ADG_BIDDER_CODE = 'adgeneration'; @@ -21,14 +21,14 @@ export const spec = { }, /** * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids + * @param validBidRequests + * @param bidderRequest * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - const ADGENE_PREBID_VERSION = '1.5.0'; + const ADGENE_PREBID_VERSION = '1.6.0'; let serverRequests = []; for (let i = 0, len = validBidRequests.length; i < len; i++) { const validReq = validBidRequests[i]; @@ -38,6 +38,9 @@ export const spec = { const criteoId = getCriteoId(validReq); const id5id = getId5Id(validReq); const id5LinkType = getId5LinkType(validReq); + const imuid = deepAccess(validReq, 'userId.imuid'); + const gpid = deepAccess(validReq, 'ortb2Imp.ext.gpid'); + const sua = deepAccess(validReq, 'ortb2.device.sua'); let data = ``; data = tryAppendQueryString(data, 'posall', 'SSPLOC'); const id = getBidIdParameter('id', validReq.params); @@ -45,7 +48,7 @@ export const spec = { data = tryAppendQueryString(data, 'sdktype', '0'); data = tryAppendQueryString(data, 'hb', 'true'); data = tryAppendQueryString(data, 't', 'json3'); - data = tryAppendQueryString(data, 'transactionid', validReq.transactionId); + data = tryAppendQueryString(data, 'transactionid', validReq.ortb2Imp?.ext?.tid); data = tryAppendQueryString(data, 'sizes', getSizes(validReq)); data = tryAppendQueryString(data, 'currency', getCurrencyType()); data = tryAppendQueryString(data, 'pbver', '$prebid.version$'); @@ -54,6 +57,12 @@ export const spec = { data = tryAppendQueryString(data, 'adgext_criteo_id', criteoId); data = tryAppendQueryString(data, 'adgext_id5_id', id5id); data = tryAppendQueryString(data, 'adgext_id5_id_link_type', id5LinkType); + data = tryAppendQueryString(data, 'adgext_imuid', imuid); + data = tryAppendQueryString(data, 'adgext_uid2', validReq.userId ? validReq.userId.uid2 : null); + data = tryAppendQueryString(data, 'gpid', gpid ? encodeURIComponent(gpid) : null); + data = tryAppendQueryString(data, 'uach', sua ? JSON.stringify(sua) : null); + data = tryAppendQueryString(data, 'schain', validReq.schain ? encodeURIComponent(JSON.stringify(validReq.schain)) : null); + // native以外にvideo等の対応が入った場合は要修正 if (!validReq.mediaTypes || !validReq.mediaTypes.native) { data = tryAppendQueryString(data, 'imark', '1'); diff --git a/modules/adhashBidAdapter.js b/modules/adhashBidAdapter.js index 08f9466823b..96e93883de6 100644 --- a/modules/adhashBidAdapter.js +++ b/modules/adhashBidAdapter.js @@ -1,9 +1,9 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { includes } from '../src/polyfill.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -const VERSION = '3.2'; +const VERSION = '3.6'; const BAD_WORD_STEP = 0.1; const BAD_WORD_MIN = 0.2; const ADHASH_BIDDER_CODE = 'adhash'; @@ -19,6 +19,8 @@ const ADHASH_BIDDER_CODE = 'adhash'; * @returns boolean flag is the page safe */ function brandSafety(badWords, maxScore) { + const delimiter = '~'; + /** * Performs the ROT13 encoding on the string argument and returns the resulting string. * The Adhash bidder uses ROT13 so that the response is not blocked by: @@ -40,17 +42,17 @@ function brandSafety(badWords, maxScore) { /** * Calculates the scoring for each bad word with dimishing returns * @param {integer} points points that this word costs - * @param {integer} occurances number of occurances + * @param {integer} occurrences number of occurrences * @returns {float} final score */ - const scoreCalculator = (points, occurances) => { + const scoreCalculator = (points, occurrences) => { let positive = true; if (points < 0) { points *= -1; positive = false; } let result = 0; - for (let i = 0; i < occurances; i++) { + for (let i = 0; i < occurrences; i++) { result += Math.max(points - i * BAD_WORD_STEP, BAD_WORD_MIN); } return positive ? result : -result; @@ -60,22 +62,50 @@ function brandSafety(badWords, maxScore) { * Checks what rule will match in the given array with words * @param {string} rule rule type (full, partial, starts, ends, regexp) * @param {string} decodedWord decoded word - * @param {array} wordsToMatch array to find a match + * @param {string} wordsToMatch list of all words on the page separated by delimiters * @returns {object|boolean} matched rule and occurances. If nothing is matched returns false */ const wordsMatchedWithRule = function (rule, decodedWord, wordsToMatch) { - if (rule === 'full' && wordsToMatch && wordsToMatch.includes(decodedWord)) { - return { rule, occurances: wordsToMatch.filter(element => element === decodedWord).length }; - } else if (rule === 'partial' && wordsToMatch && wordsToMatch.some(element => element.indexOf(decodedWord) > -1)) { - return { rule, occurances: wordsToMatch.filter(element => element.indexOf(decodedWord) > -1).length }; - } else if (rule === 'starts' && wordsToMatch && wordsToMatch.some(word => word.startsWith(decodedWord))) { - return { rule, occurances: wordsToMatch.filter(element => element.startsWith(decodedWord)).length }; - } else if (rule === 'ends' && wordsToMatch && wordsToMatch.some(word => word.endsWith(decodedWord))) { - return { rule, occurances: wordsToMatch.filter(element => element.endsWith(decodedWord)).length }; - } else if (rule === 'regexp' && wordsToMatch && wordsToMatch.some(element => element.match(new RegExp(decodedWord, 'i')))) { - return { rule, occurances: wordsToMatch.filter(element => element.match(new RegExp(decodedWord, 'i'))).length }; + if (!wordsToMatch) { + return false; + } + + let occurrences; + let adjustedWordToMatch; + decodedWord = decodedWord.split(' ').join(`${delimiter}${delimiter}`); + switch (rule) { + case 'full': + adjustedWordToMatch = `${delimiter}${decodedWord}${delimiter}`; + break; + case 'partial': + adjustedWordToMatch = decodedWord; + break; + case 'starts': + adjustedWordToMatch = `${delimiter}${decodedWord}`; + break; + case 'ends': + adjustedWordToMatch = `${decodedWord}${delimiter}`; + break; + case 'combo': + const allOccurrences = []; + const paddedWordsToMatch = `${delimiter}${wordsToMatch}${delimiter}`; + const decodedWordsSplit = decodedWord.split(`${delimiter}${delimiter}`); + for (const decodedWordPart of decodedWordsSplit) { + adjustedWordToMatch = `${delimiter}${decodedWordPart}${delimiter}`; + allOccurrences.push(paddedWordsToMatch.split(adjustedWordToMatch).length - 1); + } + occurrences = Math.min(...allOccurrences); + return occurrences > 0 ? { rule, occurrences } : false; + case 'regexp': + occurrences = [...wordsToMatch.matchAll(new RegExp(decodedWord, 'gi'))].length; + return occurrences > 0 ? { rule, occurrences } : false; + default: + return false; } - return false; + + const paddedWordsToMatch = `${delimiter}${wordsToMatch}${delimiter}`; + occurrences = paddedWordsToMatch.split(adjustedWordToMatch).length - 1; + return occurrences > 0 ? { rule, occurrences } : false; }; // Default parameters if the bidder is unable to send some of them @@ -91,11 +121,11 @@ function brandSafety(badWords, maxScore) { .toLowerCase() .trim(); const content = window.top.document.body.innerText.toLowerCase(); - const contentWords = content.trim().split(/\s+/).length; // \p{L} matches a single unicode code point in the category 'letter'. Matches any kind of letter from any language. const regexp = new RegExp('[\\p{L}]+', 'gu'); - const words = content.match(regexp); - const wordsInUrl = wordsAndNumbersInUrl.match(regexp); + const wordsMatched = content.match(regexp); + const words = wordsMatched.join(`${delimiter}${delimiter}`); + const wordsInUrl = wordsAndNumbersInUrl.match(regexp).join(`${delimiter}${delimiter}`); for (const [word, rule, points] of badWords) { const decodedWord = rot13(word.toLowerCase()); @@ -110,19 +140,11 @@ function brandSafety(badWords, maxScore) { // Check if site content's words match any of our brand safety rules const matchedRule = wordsMatchedWithRule(rule, decodedWord, words); - if (matchedRule.rule === 'full') { - score += scoreCalculator(points, matchedRule.occurances); - } else if (matchedRule.rule === 'partial') { - score += scoreCalculator(points, matchedRule.occurances); - } else if (matchedRule.rule === 'starts') { - score += scoreCalculator(points, matchedRule.occurances); - } else if (matchedRule.rule === 'ends') { - score += scoreCalculator(points, matchedRule.occurances); - } else if (matchedRule.rule === 'regexp') { - score += scoreCalculator(points, matchedRule.occurances); + if (matchedRule !== false) { + score += scoreCalculator(points, matchedRule.occurrences); } } - return score < (maxScore * contentWords) / 1000; + return score < (maxScore * wordsMatched.length) / 1000; } catch (e) { return true; } @@ -183,8 +205,8 @@ export const spec = { } // Needed for the ad density calculation - var adHeight = validBidRequests[i].sizes[index][1]; - var adWidth = validBidRequests[i].sizes[index][0]; + const adHeight = validBidRequests[i].sizes[index][1]; + const adWidth = validBidRequests[i].sizes[index][0]; if (!window.adsCount) { window.adsCount = 0; } @@ -247,7 +269,7 @@ export const spec = { const bidderResponse = JSON.stringify({ responseText: JSON.stringify(responseBody) }); const requestData = JSON.stringify(request.data); - var response = { + let response = { requestId: request.bidRequest.bidId, cpm: responseBody.creatives[0].costEUR, width: request.bidRequest.sizes[0][0], diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 3e348e0795d..4db46aca3c6 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, parseSizesInput, isArray, deepSetValue, isStr, isNumber, logInfo } from '../src/utils.js'; +import {deepAccess, deepSetValue, isArray, isNumber, isStr, logInfo, parseSizesInput} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -58,11 +58,11 @@ function canonicalizeSizesArray(sizes) { } function buildRequestParams(tags, bidderRequest) { - let {auctionId, gdprConsent, uspConsent, transactionId, refererInfo} = bidderRequest; + let {gdprConsent, uspConsent, refererInfo, ortb2} = bidderRequest; let req = { - id: auctionId, - // TODO: transactionId is undefined here, should this be auctionId? see #8573 - tid: transactionId, + id: bidderRequest.bidderRequestId, + // TODO: root-level `tid` is not ORTB; is this intentional? + tid: ortb2?.source?.tid, site: buildSite(refererInfo), imp: tags }; @@ -99,7 +99,6 @@ function buildSite(refInfo) { function buildBid(tag) { let bid = { requestId: tag.impid, - bidderCode: spec.code, cpm: tag.bid, creativeId: tag.crid, currency: 'USD', diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index d5639f57c10..64567832dbd 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -6,6 +6,7 @@ import { deepAccess, deepSetValue, getAdUnitSizes, + getDefinedParams, getDNT, isArray, isArrayOfNums, @@ -14,14 +15,13 @@ import { isPlainObject, isStr, mergeDeep, - parseGPTSingleSizeArrayToRtbSize, - getDefinedParams + parseGPTSingleSizeArrayToRtbSize } from '../src/utils.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {find} from '../src/polyfill.js'; import {config} from '../src/config.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; /* * In case you're AdKernel whitelable platform's client who needs branded adapter to @@ -90,7 +90,6 @@ export const spec = { {code: 'denakop'}, {code: 'rtbanalytica'}, {code: 'unibots'}, - {code: 'catapultx'}, {code: 'ergadx'}, {code: 'turktelekom'}, {code: 'felixads'}, @@ -99,7 +98,9 @@ export const spec = { {code: 'displayioads'}, {code: 'rtbdemand_com'}, {code: 'bidbuddy'}, - {code: 'adliveconnect'} + {code: 'adliveconnect'}, + {code: 'didnadisplay'}, + {code: 'qortex'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -490,9 +491,9 @@ function makeRegulations(bidderRequest) { * @returns */ function makeBaseRequest(bidderRequest, imps, fpd) { - let {auctionId, timeout} = bidderRequest; + let {timeout} = bidderRequest; let request = { - 'id': auctionId, + 'id': bidderRequest.bidderRequestId, 'imp': imps, 'at': 1, 'tmax': parseInt(timeout) diff --git a/modules/adprimeBidAdapter.js b/modules/adprimeBidAdapter.js index 49c18c2d067..55ee1f0900c 100644 --- a/modules/adprimeBidAdapter.js +++ b/modules/adprimeBidAdapter.js @@ -3,6 +3,7 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { isFn, deepAccess, logMessage } from '../src/utils.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const BIDDER_CODE = 'adprime'; const AD_URL = 'https://delta.adprime.com/pbjs'; @@ -128,7 +129,7 @@ export const spec = { wPlayer: sizes ? sizes[0] : 0, hPlayer: sizes ? sizes[1] : 0, schain: bid.schain || {}, - keywords: bid.params.keywords || [], + keywords: getAllOrtbKeywords(bidderRequest.ortb2, bid.params.keywords), audiences: bid.params.audiences || [], identeties, bidFloor: getBidFloor(bid) diff --git a/modules/adqueryBidAdapter.js b/modules/adqueryBidAdapter.js index 63e0c7dbe22..8a953f0d97f 100644 --- a/modules/adqueryBidAdapter.js +++ b/modules/adqueryBidAdapter.js @@ -191,19 +191,26 @@ export const spec = { }; function buildRequest(validBidRequests, bidderRequest) { let bid = validBidRequests; + let pageUrl = ''; + if (bidderRequest && bidderRequest.refererInfo) { + pageUrl = bidderRequest.refererInfo.page || ''; + } + return { + v: '$prebid.version$', placementCode: bid.params.placementId, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: bid.auctionId, type: bid.params.type, adUnitCode: bid.adUnitCode, bidQid: storage.getDataFromLocalStorage('qid') || null, bidId: bid.bidId, bidder: bid.bidder, + bidPageUrl: pageUrl, bidderRequestId: bid.bidderRequestId, bidRequestsCount: bid.bidRequestsCount, bidderRequestsCount: bid.bidderRequestsCount, sizes: parseSizesInput(bid.mediaTypes.banner.sizes).toString(), - }; } diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 40cfe18f025..cf785a1fc87 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -13,8 +13,7 @@ import { isStr, logError, logMessage, - logWarn, - transformBidderParamKeywords + logWarn } from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -22,6 +21,7 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'adrelevantis'; const URL = 'https://ssp.adrelevantis.com/prebid'; @@ -194,10 +194,6 @@ export const spec = { params.use_pmt_rule = (typeof params.usePaymentRule === 'boolean') ? params.usePaymentRule : false; if (params.usePaymentRule) { delete params.usePaymentRule; } - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } - Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -211,16 +207,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function formatRequest(payload, bidderRequest) { let request = []; @@ -475,14 +461,7 @@ function bidToTag(bid) { if (bid.params.externalImpId) { tag.external_imp_id = bid.params.externalImpId; } - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - tag.keywords = keywords; - } + tag.keywords = getANKeywordParam(bid.ortb2, bid.params.keywords) if (bid.params.category) { tag.category = bid.params.category; } diff --git a/modules/adrinoBidAdapter.js b/modules/adrinoBidAdapter.js index 9825c5701d7..f5ae09934e3 100644 --- a/modules/adrinoBidAdapter.js +++ b/modules/adrinoBidAdapter.js @@ -35,6 +35,7 @@ export const spec = { let bids = []; for (let i = 0; i < validBidRequests.length; i++) { let requestData = { + adUnitCode: validBidRequests[i].adUnitCode, bidId: validBidRequests[i].bidId, placementHash: validBidRequests[i].params.hash, userId: validBidRequests[i].userId, diff --git a/modules/adriverIdSystem.js b/modules/adriverIdSystem.js index b3ab00350ea..c04ebf48028 100644 --- a/modules/adriverIdSystem.js +++ b/modules/adriverIdSystem.js @@ -42,7 +42,7 @@ export const adriverIdSubmodule = { if (!isPlainObject(config.params)) { config.params = {}; } - const url = 'https://ad.adriver.ru/cgi-bin/json.cgi?sid=1&ad=719473&bt=55&pid=3198680&bid=7189165&bn=7189165&tuid=1'; + const url = 'https://ad.adriver.ru/cgi-bin/json.cgi?sid=1&ad=719473&bt=55&pid=3198680&bid=7189165&bn=7189165&tuid=1&cfa=1'; const resp = function (callback) { let creationDate = storage.getDataFromLocalStorage('adrcid_cd') || storage.getCookie('adrcid_cd'); let cookie = storage.getDataFromLocalStorage('adrcid') || storage.getCookie('adrcid'); diff --git a/modules/adtrueBidAdapter.js b/modules/adtrueBidAdapter.js index 2ec5cd59e1d..389986eb586 100644 --- a/modules/adtrueBidAdapter.js +++ b/modules/adtrueBidAdapter.js @@ -469,7 +469,7 @@ export const spec = { conf.zoneId = conf.zoneId || bid.params.zoneId; conf.pubId = conf.pubId || bid.params.publisherId; - conf.transactionId = bid.transactionId; + conf.transactionId = bid.ortb2Imp?.ext?.tid; if (bidCurrency === '') { bidCurrency = bid.params.currency || UNDEFINED; } else if (bid.params.hasOwnProperty('currency') && bidCurrency !== bid.params.currency) { @@ -492,7 +492,7 @@ export const spec = { payload.ext.wrapper = {}; payload.ext.wrapper.transactionId = conf.transactionId; - payload.ext.wrapper.wiid = conf.wiid || bidderRequest.auctionId; + payload.ext.wrapper.wiid = conf.wiid || bidderRequest.ortb2?.ext?.tid; payload.ext.wrapper.wp = 'pbjs'; payload.user.geo = {}; diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index c7138e43cfe..1ea5f1a0096 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -255,6 +255,7 @@ export const spec = { url: internal.buildEndpointUrl(publisher), method: ENDPOINT_METHOD, data: { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: auctionId, pageUrl: pageUrl, referrer: referrer, @@ -271,7 +272,7 @@ export const spec = { groupedBidRequests[publisher].forEach(bidRequest => { const bid = { bidId: bidRequest.bidId, - transactionId: bidRequest.transactionId, + transactionId: bidRequest.ortb2Imp?.ext?.tid, adUnitCode: bidRequest.adUnitCode, params: internal.extractParams(bidRequest) }; diff --git a/modules/advangelistsBidAdapter.js b/modules/advangelistsBidAdapter.js index 4963150caed..8e5be83f166 100755 --- a/modules/advangelistsBidAdapter.js +++ b/modules/advangelistsBidAdapter.js @@ -58,7 +58,6 @@ export const spec = { if (isVideoBid(bidRequest)) { let bidResponse = { requestId: response.id, - bidderCode: BIDDER_CODE, cpm: response.seatbid[0].bid[0].price, width: response.seatbid[0].bid[0].w, height: response.seatbid[0].bid[0].h, diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index 564960cf2c9..5930f3adb67 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -98,7 +98,7 @@ export const spec = { device.dnt = getDNT() ? 1 : 0; device.language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; - const tid = bidderRequest.auctionId; + const tid = bidderRequest.ortb2?.source?.tid; const test = setOnAny(validBidRequests, 'params.test'); const currency = getConfig('currency.adServerCurrency'); const cur = currency && [ currency ]; diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 376d108e980..4080d9f25cd 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -71,7 +71,7 @@ export const spec = { let size = getSize(sizesArray); accumulator[bidReq.bidId] = {}; accumulator[bidReq.bidId].PlacementID = bidReq.params.placement; - accumulator[bidReq.bidId].TransactionID = bidReq.transactionId; + accumulator[bidReq.bidId].TransactionID = bidReq.ortb2Imp?.ext?.tid; accumulator[bidReq.bidId].Width = size.width; accumulator[bidReq.bidId].Height = size.height; accumulator[bidReq.bidId].AvailableSizes = sizesArray.join(','); diff --git a/modules/afpBidAdapter.js b/modules/afpBidAdapter.js index f690b70973d..cec61b29b82 100644 --- a/modules/afpBidAdapter.js +++ b/modules/afpBidAdapter.js @@ -100,13 +100,13 @@ export const spec = { pageUrl: IS_DEV ? TEST_PAGE_URL : refererInfo.page, gdprConsent: gdprConsent, bidRequests: validBidRequests.map(validBidRequest => { - const {bidId, transactionId, sizes, params: { + const {bidId, ortb2Imp, sizes, params: { placeId, placeType, imageUrl, imageWidth, imageHeight }} = validBidRequest bidRequestMap[bidId] = validBidRequest const bidRequest = { bidId, - transactionId, + transactionId: ortb2Imp?.ext?.tid, sizes, placeId, } diff --git a/modules/aidemBidAdapter.js b/modules/aidemBidAdapter.js index d43c07aeece..7469f26156b 100644 --- a/modules/aidemBidAdapter.js +++ b/modules/aidemBidAdapter.js @@ -1,4 +1,15 @@ -import {_each, contains, deepAccess, deepSetValue, getDNT, isBoolean, isStr, isNumber, logError, logInfo} from '../src/utils.js'; +import { + _each, + contains, + deepAccess, + deepSetValue, + getDNT, + isBoolean, + isNumber, + isStr, + logError, + logInfo +} from '../src/utils.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -164,6 +175,7 @@ function buildWinNotice(bid) { impid: deepAccess(bid, 'meta.impid'), dsp_id: deepAccess(bid, 'meta.dsp_id'), adUnitCode: bid.adUnitCode, + // TODO: fix auctionId/transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: bid.auctionId, transactionId: bid.transactionId, ttl: bid.ttl, @@ -201,7 +213,7 @@ function getMediaType(bidRequest) { function getPrebidRequestFields(bidderRequest, bidRequests) { const payload = {}; // Base Payload Data - deepSetValue(payload, 'id', bidderRequest.auctionId); + deepSetValue(payload, 'id', bidderRequest.bidderRequestId); // Impressions setPrebidImpressionObject(bidRequests, payload); // Device @@ -231,7 +243,8 @@ function setPrebidImpressionObject(bidRequests, payload) { // Placement or ad tag used to initiate the auction deepSetValue(impressionObject, 'id', bidRequest.bidId); // Transaction id - deepSetValue(impressionObject, 'tid', deepAccess(bidRequest, 'transactionId')); + // TODO: `imp.tid` is not ORTB, is this intentional? + deepSetValue(impressionObject, 'tid', deepAccess(bidRequest, 'ortb2Imp.ext.tid')); // placement id deepSetValue(impressionObject, 'tagid', deepAccess(bidRequest, 'params.placementId', null)); // Publisher id diff --git a/modules/airgridRtdProvider.js b/modules/airgridRtdProvider.js index c94a71eecde..7c6cf1f5de0 100644 --- a/modules/airgridRtdProvider.js +++ b/modules/airgridRtdProvider.js @@ -5,14 +5,10 @@ * @module modules/airgridRtdProvider * @requires module:modules/realTimeData */ -import { config } from '../src/config.js'; -import { submodule } from '../src/hook.js'; -import { - deepSetValue, - deepAccess, -} from '../src/utils.js'; -import { getGlobal } from '../src/prebidGlobal.js'; +import {submodule} from '../src/hook.js'; +import {deepAccess, deepSetValue, mergeDeep} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; +import {loadExternalScript} from '../src/adloader.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; const MODULE_NAME = 'realTimeData'; @@ -25,6 +21,11 @@ export const storage = getStorageManager({ moduleName: SUBMODULE_NAME, }); +function getModuleUrl(accountId) { + const path = accountId ?? 'sdk'; + return `https://cdn.edkt.io/${path}/edgekit.min.js`; +} + /** * Attach script tag to DOM * @param {Object} rtdConfig @@ -33,19 +34,12 @@ export const storage = getStorageManager({ export function attachScriptTagToDOM(rtdConfig) { var edktInitializor = (window.edktInitializor = window.edktInitializor || {}); if (!edktInitializor.invoked) { - edktInitializor.invoked = true; edktInitializor.accountId = rtdConfig.params.accountId; edktInitializor.publisherId = rtdConfig.params.publisherId; edktInitializor.apiKey = rtdConfig.params.apiKey; - edktInitializor.load = function (e) { - var p = e || 'sdk'; - var n = document.createElement('script'); - n.type = 'module'; - n.async = true; - n.src = 'https://cdn.edkt.io/' + p + '/edgekit.min.js'; - document.getElementsByTagName('head')[0].appendChild(n); - }; - edktInitializor.load(edktInitializor.accountId); + edktInitializor.invoked = true; + const moduleSrc = getModuleUrl(rtdConfig.params.accountId); + loadExternalScript(moduleSrc, SUBMODULE_NAME); } } @@ -63,54 +57,35 @@ export function getMatchedAudiencesFromStorage() { } } -/** - * Mutates the adUnits object - * @param {Object} adUnits - * @param {Array} audiences - * @return {void} - */ -function setAudiencesToAppNexusAdUnits(adUnits, audiences) { - adUnits.forEach((adUnit) => { - adUnit.bids.forEach((bid) => { - if (bid.bidder && bid.bidder === 'appnexus') { - deepSetValue(bid, 'params.keywords.perid', audiences || []); - } - }); - }); -} - /** * Pass audience data to configured bidders, using ORTB2 + * @param {Object} bidConfig * @param {Object} rtdConfig * @param {Array} audiences - * @return {{}} a map from bidder code to ORTB2 config + * @return {void} */ -export function setAudiencesAsBidderOrtb2(rtdConfig, audiences) { +export function setAudiencesAsBidderOrtb2(bidConfig, rtdConfig, audiences) { const bidders = deepAccess(rtdConfig, 'params.bidders'); if (!bidders || bidders.length === 0 || !audiences || audiences.length === 0) return; - const keywords = audiences.map( - (audienceId) => `perid=${audienceId}` - ).join(','); + const agOrtb2 = {}; - config.mergeBidderConfig({ - bidders: bidders, - config: { - ortb2: { - site: { - keywords, - } - } + const agUserData = [ + { + id: String(AG_TCF_ID), + ext: { + segtax: 540, + }, + name: 'airgrid', + segment: audiences.map((id) => ({id})) } - }) -} + ] + deepSetValue(agOrtb2, 'user.data', agUserData); -export function setAudiencesUsingAppNexusAuctionKeywords(audiences) { - config.setConfig({ - appnexusAuctionKeywords: { - perid: audiences, - }, - }); + const bidderConfig = Object.fromEntries( + bidders.map((bidder) => [bidder, agOrtb2]) + ) + mergeDeep(bidConfig?.ortb2Fragments?.bidder, bidderConfig) } /** @@ -138,14 +113,9 @@ export function passAudiencesToBidders( rtdConfig, userConsent ) { - const adUnits = bidConfig.adUnits || getGlobal().adUnits; const audiences = getMatchedAudiencesFromStorage(); if (audiences.length > 0) { - setAudiencesUsingAppNexusAuctionKeywords(audiences); - setAudiencesAsBidderOrtb2(rtdConfig, audiences) - if (adUnits) { - setAudiencesToAppNexusAdUnits(adUnits, audiences); - } + setAudiencesAsBidderOrtb2(bidConfig, rtdConfig, audiences) } onDone(); } diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js index 133a2fdbadf..ffab41611ef 100644 --- a/modules/ajaBidAdapter.js +++ b/modules/ajaBidAdapter.js @@ -55,7 +55,7 @@ export const spec = { const asi = getBidIdParameter('asi', bidRequest.params); queryString = tryAppendQueryString(queryString, 'asi', asi); queryString = tryAppendQueryString(queryString, 'skt', SDKType); - queryString = tryAppendQueryString(queryString, 'tid', bidRequest.transactionId) + queryString = tryAppendQueryString(queryString, 'tid', bidRequest.ortb2Imp?.ext?.tid) queryString = tryAppendQueryString(queryString, 'prebid_id', bidRequest.bidId); queryString = tryAppendQueryString(queryString, 'prebid_ver', '$prebid.version$'); diff --git a/modules/alkimiBidAdapter.js b/modules/alkimiBidAdapter.js index 69b3fdae3d8..c087b3061a0 100644 --- a/modules/alkimiBidAdapter.js +++ b/modules/alkimiBidAdapter.js @@ -1,5 +1,5 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {deepClone, deepAccess} from '../src/utils.js'; +import {deepAccess, deepClone} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; import {VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -41,6 +41,7 @@ export const spec = { const alkimiConfig = config.getConfig('alkimi'); let payload = { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 requestId: bidderRequest.auctionId, signRequest: {bids, randomUUID: alkimiConfig && alkimiConfig.randomUUID}, bidIds, diff --git a/modules/allowActivities.js b/modules/allowActivities.js new file mode 100644 index 00000000000..6af7eb36a62 --- /dev/null +++ b/modules/allowActivities.js @@ -0,0 +1,74 @@ +import {config} from '../src/config.js'; +import {registerActivityControl} from '../src/activities/rules.js'; + +const CFG_NAME = 'allowActivities'; +const RULE_NAME = `${CFG_NAME} config`; +const DEFAULT_PRIORITY = 1; + +export function updateRulesFromConfig(registerRule) { + const activeRuleHandles = new Map(); + const defaultRuleHandles = new Map(); + const rulesByActivity = new Map(); + + function clearAllRules() { + rulesByActivity.clear(); + Array.from(activeRuleHandles.values()) + .flatMap(ruleset => Array.from(ruleset.values())) + .forEach(fn => fn()); + activeRuleHandles.clear(); + Array.from(defaultRuleHandles.values()).forEach(fn => fn()); + defaultRuleHandles.clear(); + } + + function cleanParams(params) { + // remove private parameters for publisher condition checks + return Object.fromEntries(Object.entries(params).filter(([k]) => !k.startsWith('_'))) + } + + function setupRule(activity, priority) { + if (!activeRuleHandles.has(activity)) { + activeRuleHandles.set(activity, new Map()) + } + const handles = activeRuleHandles.get(activity); + if (!handles.has(priority)) { + handles.set(priority, registerRule(activity, RULE_NAME, function (params) { + for (const rule of rulesByActivity.get(activity).get(priority)) { + if (!rule.condition || rule.condition(cleanParams(params))) { + return {allow: rule.allow, reason: rule} + } + } + }, priority)); + } + } + + function setupDefaultRule(activity) { + if (!defaultRuleHandles.has(activity)) { + defaultRuleHandles.set(activity, registerRule(activity, RULE_NAME, function () { + return {allow: false, reason: 'activity denied by default'} + }, Number.POSITIVE_INFINITY)) + } + } + + config.getConfig(CFG_NAME, (cfg) => { + clearAllRules(); + Object.entries(cfg[CFG_NAME]).forEach(([activity, activityCfg]) => { + if (activityCfg.default === false) { + setupDefaultRule(activity); + } + const rules = new Map(); + rulesByActivity.set(activity, rules); + + (activityCfg.rules || []).forEach(rule => { + const priority = rule.priority == null ? DEFAULT_PRIORITY : rule.priority; + if (!rules.has(priority)) { + rules.set(priority, []) + } + rules.get(priority).push(rule); + }); + + Array.from(rules.keys()).forEach(priority => setupRule(activity, priority)); + }); + }) +} + +updateRulesFromConfig(registerActivityControl); diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index 68a3a370c01..7286001b888 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -303,6 +303,7 @@ export const spec = { }; const payload = { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 a: bidderRequest.auctionId, B: 0, b: loc.host, diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js deleted file mode 100644 index d15a5434e0c..00000000000 --- a/modules/aolBidAdapter.js +++ /dev/null @@ -1,462 +0,0 @@ -import { isInteger, logError, isEmpty, logWarn, getUniqueIdentifierStr, _each, deepSetValue } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const AOL_BIDDERS_CODES = { - AOL: 'aol', - VERIZON: 'verizon', - ONEMOBILE: 'onemobile', - ONEDISPLAY: 'onedisplay' -}; - -const AOL_ENDPOINTS = { - DISPLAY: { - GET: 'display-get' - }, - MOBILE: { - GET: 'mobile-get', - POST: 'mobile-post' - } -}; - -const SYNC_TYPES = { - IFRAME: { - TAG: 'iframe', - TYPE: 'iframe' - }, - IMAGE: { - TAG: 'img', - TYPE: 'image' - } -}; - -const SUPPORTED_USER_ID_SOURCES = [ - 'admixer.net', - 'adserver.org', - 'adtelligent.com', - 'akamai.com', - 'amxdt.net', - 'audigent.com', - 'britepool.com', - 'criteo.com', - 'crwdcntrl.net', - 'deepintent.com', - 'epsilon.com', - 'hcn.health', - 'id5-sync.com', - 'idx.lat', - 'intentiq.com', - 'intimatemerger.com', - 'liveintent.com', - 'liveramp.com', - 'mediawallahscript.com', - 'merkleinc.com', - 'netid.de', - 'neustar.biz', - 'nextroll.com', - 'novatiq.com', - 'parrable.com', - 'pubcid.org', - 'quantcast.com', - 'tapad.com', - 'uidapi.com', - 'verizonmedia.com', - 'yahoo.com', - 'zeotap.com' -]; - -const pubapiTemplate = template`${'host'}/pubapi/3.0/${'network'}/${'placement'}/${'pageid'}/${'sizeid'}/ADTECH;v=2;cmd=bid;cors=yes;alias=${'alias'};misc=${'misc'};${'dynamicParams'}`; -const nexageBaseApiTemplate = template`${'host'}/bidRequest?`; -const nexageGetApiTemplate = template`dcn=${'dcn'}&pos=${'pos'}&cmd=bid${'dynamicParams'}`; -const MP_SERVER_MAP = { - us: 'adserver-us.adtech.advertising.com', - eu: 'adserver-eu.adtech.advertising.com', - as: 'adserver-as.adtech.advertising.com' -}; -const NEXAGE_SERVER = 'c2shb.ssp.yahoo.com'; -const ONE_DISPLAY_TTL = 60; -const ONE_MOBILE_TTL = 3600; -const DEFAULT_PROTO = 'https'; - -const NUMERIC_VALUES = { - TRUE: 1, - FALSE: 0 -}; - -function template(strings, ...keys) { - return function (...values) { - let dict = values[values.length - 1] || {}; - let result = [strings[0]]; - keys.forEach(function (key, i) { - let value = isInteger(key) ? values[key] : dict[key]; - result.push(value, strings[i + 1]); - }); - return result.join(''); - }; -} - -function _isMarketplaceBidder(bidderCode) { - return bidderCode === AOL_BIDDERS_CODES.AOL || - bidderCode === AOL_BIDDERS_CODES.VERIZON || - bidderCode === AOL_BIDDERS_CODES.ONEDISPLAY; -} - -function _isOneMobileBidder(bidderCode) { - return bidderCode === AOL_BIDDERS_CODES.AOL || - bidderCode === AOL_BIDDERS_CODES.VERIZON || - bidderCode === AOL_BIDDERS_CODES.ONEMOBILE; -} - -function _isNexageRequestPost(bid) { - if (_isOneMobileBidder(bid.bidder) && bid.params.id && bid.params.imp && bid.params.imp[0]) { - let imp = bid.params.imp[0]; - return imp.id && imp.tagid && imp.banner && imp.banner.w && imp.banner.h; - } -} - -function _isNexageRequestGet(bid) { - return _isOneMobileBidder(bid.bidder) && bid.params.dcn && bid.params.pos; -} - -function isMarketplaceBid(bid) { - return _isMarketplaceBidder(bid.bidder) && bid.params.placement && bid.params.network; -} - -function isMobileBid(bid) { - return _isNexageRequestGet(bid) || _isNexageRequestPost(bid); -} - -function resolveEndpointCode(bid) { - if (_isNexageRequestGet(bid)) { - return AOL_ENDPOINTS.MOBILE.GET; - } else if (_isNexageRequestPost(bid)) { - return AOL_ENDPOINTS.MOBILE.POST; - } else if (isMarketplaceBid(bid)) { - return AOL_ENDPOINTS.DISPLAY.GET; - } -} - -function getSupportedEids(bid) { - return bid.userIdAsEids.filter(eid => { - return SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) !== -1 - }); -} - -export const spec = { - code: AOL_BIDDERS_CODES.AOL, - gvlid: 25, - aliases: [ - AOL_BIDDERS_CODES.ONEMOBILE, - AOL_BIDDERS_CODES.ONEDISPLAY, - AOL_BIDDERS_CODES.VERIZON - ], - supportedMediaTypes: [BANNER], - isBidRequestValid(bid) { - return isMarketplaceBid(bid) || isMobileBid(bid); - }, - buildRequests(bids, bidderRequest) { - const consentData = {}; - if (bidderRequest) { - consentData.gdpr = bidderRequest.gdprConsent; - consentData.uspConsent = bidderRequest.uspConsent; - consentData.gppConsent = bidderRequest.gppConsent; - if (!consentData.gppConsent && bidderRequest.ortb2?.regs?.gpp) { - consentData.gppConsent = { - gppString: bidderRequest.ortb2.regs.gpp, - applicableSections: bidderRequest.ortb2.regs.gpp_sid - } - } - } - - return bids.map(bid => { - const endpointCode = resolveEndpointCode(bid); - - if (endpointCode) { - return this.formatBidRequest(endpointCode, bid, consentData); - } - }); - }, - interpretResponse({ body }, bidRequest) { - if (!body) { - logError('Empty bid response', bidRequest.bidderCode, body); - } else { - let bid = this._parseBidResponse(body, bidRequest); - - if (bid) { - return bid; - } - } - }, - getUserSyncs(options, serverResponses) { - const bidResponse = !isEmpty(serverResponses) && serverResponses[0].body; - - if (bidResponse && bidResponse.ext && bidResponse.ext.pixels) { - return this.parsePixelItems(bidResponse.ext.pixels); - } - - return []; - }, - - formatBidRequest(endpointCode, bid, consentData) { - let bidRequest; - - switch (endpointCode) { - case AOL_ENDPOINTS.DISPLAY.GET: - bidRequest = { - url: this.buildMarketplaceUrl(bid, consentData), - method: 'GET', - ttl: ONE_DISPLAY_TTL - }; - break; - - case AOL_ENDPOINTS.MOBILE.GET: - bidRequest = { - url: this.buildOneMobileGetUrl(bid, consentData), - method: 'GET', - ttl: ONE_MOBILE_TTL - }; - break; - - case AOL_ENDPOINTS.MOBILE.POST: - bidRequest = { - url: this.buildOneMobileBaseUrl(bid), - method: 'POST', - ttl: ONE_MOBILE_TTL, - data: this.buildOpenRtbRequestData(bid, consentData), - options: { - contentType: 'application/json', - customHeaders: { - 'x-openrtb-version': '2.2' - } - } - }; - break; - } - - bidRequest.bidderCode = bid.bidder; - bidRequest.bidId = bid.bidId; - bidRequest.userSyncOn = bid.params.userSyncOn; - - return bidRequest; - }, - buildMarketplaceUrl(bid, consentData) { - const params = bid.params; - const serverParam = params.server; - let regionParam = params.region || 'us'; - let server; - - if (!MP_SERVER_MAP.hasOwnProperty(regionParam)) { - logWarn(`Unknown region '${regionParam}' for AOL bidder.`); - regionParam = 'us'; // Default region. - } - - if (serverParam) { - server = serverParam; - } else { - server = MP_SERVER_MAP[regionParam]; - } - - // Set region param, used by AOL analytics. - params.region = regionParam; - - return this.applyProtocol(pubapiTemplate({ - host: server, - network: params.network, - placement: parseInt(params.placement), - pageid: params.pageId || 0, - sizeid: params.sizeId || 0, - alias: params.alias || getUniqueIdentifierStr(), - misc: new Date().getTime(), // cache busting - dynamicParams: this.formatMarketplaceDynamicParams(params, consentData) - })); - }, - buildOneMobileGetUrl(bid, consentData) { - let { dcn, pos, ext } = bid.params; - if (typeof bid.userId === 'object') { - ext = ext || {}; - let eids = getSupportedEids(bid); - eids.forEach(eid => { - ext['eid' + eid.source] = eid.uids[0].id; - }); - } - let nexageApi = this.buildOneMobileBaseUrl(bid); - if (dcn && pos) { - let dynamicParams = this.formatOneMobileDynamicParams(ext, consentData); - nexageApi += nexageGetApiTemplate({ dcn, pos, dynamicParams }); - } - return nexageApi; - }, - buildOneMobileBaseUrl(bid) { - return this.applyProtocol(nexageBaseApiTemplate({ - host: bid.params.host || NEXAGE_SERVER - })); - }, - applyProtocol(url) { - if (/^https?:\/\//i.test(url)) { - return url; - } - return (url.indexOf('//') === 0) ? `${DEFAULT_PROTO}:${url}` : `${DEFAULT_PROTO}://${url}`; - }, - formatMarketplaceDynamicParams(params = {}, consentData = {}) { - let queryParams = {}; - - Object.assign(queryParams, this.formatKeyValues(params.keyValues)); - Object.assign(queryParams, this.formatConsentData(consentData)); - - let paramsFormatted = ''; - _each(queryParams, (value, key) => { - paramsFormatted += `${key}=${encodeURIComponent(value)};`; - }); - - return paramsFormatted; - }, - formatOneMobileDynamicParams(params = {}, consentData = {}) { - if (this.isSecureProtocol()) { - params.secure = NUMERIC_VALUES.TRUE; - } - - Object.assign(params, this.formatConsentData(consentData)); - - let paramsFormatted = ''; - _each(params, (value, key) => { - paramsFormatted += `&${key}=${encodeURIComponent(value)}`; - }); - - return paramsFormatted; - }, - buildOpenRtbRequestData(bid, consentData = {}) { - let openRtbObject = { - id: bid.params.id, - imp: bid.params.imp - }; - - if (this.isEUConsentRequired(consentData)) { - deepSetValue(openRtbObject, 'regs.ext.gdpr', NUMERIC_VALUES.TRUE); - if (consentData.gdpr.consentString) { - deepSetValue(openRtbObject, 'user.ext.consent', consentData.gdpr.consentString); - } - } - - if (consentData.uspConsent) { - deepSetValue(openRtbObject, 'regs.ext.us_privacy', consentData.uspConsent); - } - - if (typeof bid.userId === 'object') { - openRtbObject.user = openRtbObject.user || {}; - openRtbObject.user.ext = openRtbObject.user.ext || {}; - - let eids = getSupportedEids(bid); - if (eids.length > 0) { - openRtbObject.user.ext.eids = eids - } - } - - return openRtbObject; - }, - isEUConsentRequired(consentData) { - return !!(consentData && consentData.gdpr && consentData.gdpr.gdprApplies); - }, - formatKeyValues(keyValues) { - let keyValuesHash = {}; - - _each(keyValues, (value, key) => { - keyValuesHash[`kv${key}`] = value; - }); - - return keyValuesHash; - }, - formatConsentData(consentData) { - let params = {}; - - if (this.isEUConsentRequired(consentData)) { - params.gdpr = NUMERIC_VALUES.TRUE; - - if (consentData.gdpr.consentString) { - params.euconsent = consentData.gdpr.consentString; - } - } - - if (consentData.uspConsent) { - params.us_privacy = consentData.uspConsent; - } - - if (consentData.gppConsent && consentData.gppConsent.gppString) { - params.gpp = consentData.gppConsent.gppString; - params.gpp_sid = consentData.gppConsent.applicableSections; - } - - return params; - }, - parsePixelItems(pixels) { - let itemsRegExp = /(img|iframe)[\s\S]*?src\s*=\s*("|')(.*?)\2/gi; - let tagNameRegExp = /\w*(?=\s)/; - let srcRegExp = /src=("|')(.*?)\1/; - let pixelsItems = []; - - if (pixels) { - let matchedItems = pixels.match(itemsRegExp); - if (matchedItems) { - matchedItems.forEach(item => { - let tagName = item.match(tagNameRegExp)[0]; - let url = item.match(srcRegExp)[2]; - - if (tagName && url) { - pixelsItems.push({ - type: tagName === SYNC_TYPES.IMAGE.TAG ? SYNC_TYPES.IMAGE.TYPE : SYNC_TYPES.IFRAME.TYPE, - url: url - }); - } - }); - } - } - - return pixelsItems; - }, - - _parseBidResponse(response, bidRequest) { - let bidData; - - try { - bidData = response.seatbid[0].bid[0]; - } catch (e) { - return; - } - - let cpm; - - if (bidData.ext && bidData.ext.encp) { - cpm = bidData.ext.encp; - } else { - cpm = bidData.price; - - if (cpm === null || isNaN(cpm)) { - logError('Invalid price in bid response', AOL_BIDDERS_CODES.AOL, bidData); - return; - } - } - - return { - bidderCode: bidRequest.bidderCode, - requestId: bidRequest.bidId, - ad: bidData.adm, - cpm: cpm, - width: bidData.w, - height: bidData.h, - creativeId: bidData.crid || 0, - pubapiId: response.id, - currency: response.cur || 'USD', - dealId: bidData.dealid, - netRevenue: true, - meta: { - advertiserDomains: bidData && bidData.adomain ? bidData.adomain : [] - }, - ttl: bidRequest.ttl - }; - }, - isOneMobileBidder: _isOneMobileBidder, - isSecureProtocol() { - return document.location.protocol === 'https:'; - } -}; - -registerBidder(spec); diff --git a/modules/aolBidAdapter.md b/modules/aolBidAdapter.md deleted file mode 100644 index 8a9d1e3291d..00000000000 --- a/modules/aolBidAdapter.md +++ /dev/null @@ -1,46 +0,0 @@ -# Overview - -Module Name: AOL Bid Adapter - -Module Type: AOL Adapter - -Maintainer: hb-fe-tech@oath.com - -# Description - -Module that connects to AOL's demand sources - -# Test Parameters -```javascript - var adUnits = [ - { - code: 'test-ad', - sizes: [[300, 250]], - bids: [ - { - bidder: 'onedisplay', - params: { - placement: '3611253', - network: '9599.1', - keyValues: { - test: 'key' - } - } - } - ] - }, - { - code: 'test-mobile-ad', - sizes: [[300, 250]], - bids: [ - { - bidder: 'onemobile', - params: { - dcn: '2c9d2b50015a5aa95b70a9b0b5b10012', - pos: 'header' - } - } - ] - } - ]; -``` diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 10593855c59..e1557d9c6d3 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, isPlainObject, isArray, replaceAuctionPrice, isFn } from '../src/utils.js'; +import { deepAccess, isPlainObject, isArray, replaceAuctionPrice, isFn, logError } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; @@ -134,14 +134,14 @@ export const spec = { // Apply geo if (geo) { - payload.geo = geo; + logError('apacdex adapter: Precise lat and long must be set on config; not on bidder parameters'); } payload.bids = bids.map(function (bid) { return { params: bid.params, mediaTypes: bid.mediaTypes, - transactionId: bid.transactionId, + transactionId: bid.ortb2Imp?.ext?.tid, sizes: bid.sizes, bidId: bid.bidId, adUnitCode: bid.adUnitCode, diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 8347a4812c3..0660f4f4b10 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -22,21 +22,25 @@ import { logError, logInfo, logMessage, - logWarn, - mergeDeep, - transformBidderParamKeywords + logWarn } from '../src/utils.js'; import {Renderer} from '../src/Renderer.js'; import {config} from '../src/config.js'; -import {getIabSubCategory, registerBidder} from '../src/adapters/bidderFactory.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {auctionManager} from '../src/auctionManager.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {getStorageManager} from '../src/storageManager.js'; import {bidderSettings} from '../src/bidderSettings.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import {APPNEXUS_CATEGORY_MAPPING} from '../libraries/categoryTranslationMapping/index.js'; +import { + convertKeywordStringToANMap, + getANKewyordParamFromMaps, + getANKeywordParam, + transformBidderParamKeywords +} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'appnexus'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -89,7 +93,6 @@ const NATIVE_MAPPING = { }; const SOURCE = 'pbjs'; const MAX_IMPS_PER_REQUEST = 15; -const mappingFileUrl = 'https://acdn.adnxs-simple.com/prebid/appnexus-mapping/mappings.json'; const SCRIPT_TAG_START = ' { - let keyStr = deepAccess(ortb2Obj, path); - if (isStr(keyStr)) result.push(keyStr); - }); - return result; - } - // grab the ortb2 keyword data (if it exists) and convert from the comma list string format to object format let ortb2 = deepClone(bidderRequest && bidderRequest.ortb2); - let ortb2KeywordsObjList = grabOrtb2Keywords(ortb2).map(keyStr => convertStringToKeywordsObj(keyStr)); let anAuctionKeywords = deepClone(config.getConfig('appnexusAuctionKeywords')) || {}; - // need to convert the string values into array of strings, to properly merge values with other existing keys later - Object.keys(anAuctionKeywords).forEach(k => { if (isStr(anAuctionKeywords[k]) || isNumber(anAuctionKeywords[k])) anAuctionKeywords[k] = [anAuctionKeywords[k]] }); - // combine all sources of keywords (converted from string comma list to object format) into one object (that combines the values for shared keys) - let mergedAuctionKeywords = mergeDeep({}, anAuctionKeywords, ...ortb2KeywordsObjList); - - // convert to final format used by adserver - let auctionKeywords = transformBidderParamKeywords(mergedAuctionKeywords); + let auctionKeywords = getANKeywordParam(ortb2, anAuctionKeywords) if (auctionKeywords.length > 0) { - auctionKeywords.forEach(deleteValues); payload.keywords = auctionKeywords; } @@ -421,24 +405,6 @@ export const spec = { return bids; }, - /** - * @typedef {Object} mappingFileInfo - * @property {string} url mapping file json url - * @property {number} refreshInDays prebid stores mapping data in localstorage so you can return in how many days you want to update value stored in localstorage. - * @property {string} localStorageKey unique key to store your mapping json in localstorage - */ - - /** - * Returns mapping file info. This info will be used by bidderFactory to preload mapping file and store data in local storage - * @returns {mappingFileInfo} - */ - getMappingFileInfo: function () { - return { - url: mappingFileUrl, - refreshInDays: 2 - } - }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { function checkGppStatus(gppConsent) { // this is a temporary measure to supress usersync in US-based GPP regions @@ -487,10 +453,6 @@ export const spec = { }, params); if (isOpenRtb) { - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } - Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -507,16 +469,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function strIsAppnexusViewabilityScript(str) { if (!str || str === '') return false; @@ -668,7 +620,7 @@ function newBid(serverBid, rtbBid, bidderRequest) { const videoContext = deepAccess(bidRequest, 'mediaTypes.video.context'); switch (videoContext) { case ADPOD: - const primaryCatId = getIabSubCategory(bidRequest.bidder, rtbBid.brand_category_id); + const primaryCatId = (APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id]) ? APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id] : null; bid.meta = Object.assign({}, bid.meta, { primaryCatId }); const dealTier = rtbBid.deal_priority; bid.video = { @@ -831,24 +783,9 @@ function bidToTag(bid) { tag.external_imp_id = bid.params.external_imp_id; } - let ortb2ImpKwStr = deepAccess(bid, 'ortb2Imp.ext.data.keywords'); - if ((isStr(ortb2ImpKwStr) && ortb2ImpKwStr !== '') || !isEmpty(bid.params.keywords)) { - // convert ortb2 from comma list string format to bid param object format - let ortb2ImpKwObj = convertStringToKeywordsObj(ortb2ImpKwStr); - - let bidParamsKwObj = (isPlainObject(bid.params.keywords)) ? deepClone(bid.params.keywords) : {}; - // need to convert the string values into an array of strings, to properly merge values with other existing keys later - Object.keys(bidParamsKwObj).forEach(k => { if (isStr(bidParamsKwObj[k]) || isNumber(bidParamsKwObj[k])) bidParamsKwObj[k] = [bidParamsKwObj[k]] }); - - // combine both sources of keywords into one merged object (that combines the values for shared keys) - let keywordsObj = mergeDeep({}, bidParamsKwObj, ortb2ImpKwObj); - - // convert to final format used by adserver - let keywordsUt = transformBidderParamKeywords(keywordsObj); - if (keywordsUt.length > 0) { - keywordsUt.forEach(deleteValues); - tag.keywords = keywordsUt; - } + const auKeywords = getANKewyordParamFromMaps(convertKeywordStringToANMap(deepAccess(bid, 'ortb2Imp.ext.data.keywords')), bid.params?.keywords); + if (auKeywords.length > 0) { + tag.keywords = auKeywords; } let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); @@ -979,8 +916,7 @@ function bidToTag(bid) { tag['banner_frameworks'] = bid.params.frameworks; } - let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); - if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { + if (deepAccess(bid, `mediaTypes.${BANNER}`)) { tag.ad_types.push(BANNER); } @@ -1276,37 +1212,4 @@ function convertKeywordsToString(keywords) { return result; } -// converts a comma separated list of keywords into the standard keyword object format used in appnexus bid params -// 'genre=rock,genre=pop,pets=dog,music' goes to { 'genre': ['rock', 'pop'], 'pets': ['dog'], 'music': [''] } -function convertStringToKeywordsObj(keyStr) { - let result = {}; - - if (isStr(keyStr) && keyStr !== '') { - // will split based on commas and will eat white space before/after the comma - let keywordList = keyStr.split(/\s*(?:,)\s*/); - keywordList.forEach(kw => { - // if = exists, then split - if (kw.indexOf('=') !== -1) { - let kwPair = kw.split('='); - let key = kwPair[0]; - let val = kwPair[1]; - - // then check for existing key in result > if so add value to the array > if not, add new key and create value array - if (result.hasOwnProperty(key)) { - result[key].push(val); - } else { - result[key] = [val]; - } - } else { - // make a key with '' value; if key already exists > don't add - if (!result.hasOwnProperty(kw)) { - result[kw] = ['']; - } - } - }); - } - - return result; -} - registerBidder(spec); diff --git a/modules/apstreamBidAdapter.js b/modules/apstreamBidAdapter.js index 871b04442da..2856fb02087 100644 --- a/modules/apstreamBidAdapter.js +++ b/modules/apstreamBidAdapter.js @@ -421,6 +421,7 @@ function buildRequests(bidRequests, bidderRequest) { bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); const data = { med: encodeURIComponent(window.location.href), + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auid: bidderRequest.auctionId, ref: document.referrer, dnt: getDNT() ? 1 : 0, diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js index 39c42f193b2..e569f04a2a8 100644 --- a/modules/asoBidAdapter.js +++ b/modules/asoBidAdapter.js @@ -28,7 +28,8 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], aliases: [ - {code: 'bcmint'} + {code: 'bcmint'}, + {code: 'bidgency'} ], isBidRequestValid: bid => { @@ -299,7 +300,7 @@ function createBasePayload(bidRequest, bidderRequest) { const urlsInfo = getUrlsInfo(bidderRequest); const payload = { - id: bidRequest.auctionId + '_' + bidRequest.bidId, + id: bidRequest.bidId, at: 1, tmax: bidderRequest.timeout, site: { diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js index d6bfa4b93ee..d7f92bb5fac 100644 --- a/modules/astraoneBidAdapter.js +++ b/modules/astraoneBidAdapter.js @@ -11,7 +11,7 @@ function buildBidRequests(validBidRequests) { const params = validBidRequest.params; const bidRequest = { bidId: validBidRequest.bidId, - transactionId: validBidRequest.transactionId, + transactionId: validBidRequest.ortb2Imp?.ext?.tid, sizes: validBidRequest.sizes, placement: params.placement, placeId: params.placeId, diff --git a/modules/audiencerunBidAdapter.js b/modules/audiencerunBidAdapter.js index efd88aa6f58..9beb20d4f77 100644 --- a/modules/audiencerunBidAdapter.js +++ b/modules/audiencerunBidAdapter.js @@ -114,8 +114,9 @@ export const spec = { bidId: bid.bidId, bidderRequestId: getBidIdParameter('bidderRequestId', bid), adUnitCode: getBidIdParameter('adUnitCode', bid), + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: getBidIdParameter('auctionId', bid), - transactionId: getBidIdParameter('transactionId', bid), + transactionId: bid.ortb2Imp?.ext?.tid || '', }; }); diff --git a/modules/automatadBidAdapter.js b/modules/automatadBidAdapter.js index 1174c2a9f38..bea2a9df5b2 100644 --- a/modules/automatadBidAdapter.js +++ b/modules/automatadBidAdapter.js @@ -1,7 +1,7 @@ -import { logInfo } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js' -import {BANNER} from '../src/mediaTypes.js' -import {ajax} from '../src/ajax.js' +import {logInfo} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {ajax} from '../src/ajax.js'; const BIDDER = 'automatad' @@ -57,7 +57,7 @@ export const spec = { // params from bid request const openrtbRequest = { - id: validBidRequests[0].auctionId, + id: bidderRequest.bidderRequestId, imp: impressions, site: { id: siteId, diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index f80481d66c8..37de8e637a9 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -103,7 +103,6 @@ export const spec = { let responseMeta = Object.assign({ mediaType: VIDEO, advertiserDomains: [] }, response.meta); let bidResponse = { requestId: bidRequest.bidId, - bidderCode: spec.code, cpm: response.bidPrice, width: firstSize.w, height: firstSize.h, diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index 2a0a4c5fd4c..6348854ec2a 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -1,7 +1,18 @@ -import { deepAccess, isArray, isStr, logWarn, triggerPixel, buildUrl, logInfo, getValue, getBidIdParameter } from '../src/utils.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; +import { + buildUrl, + deepAccess, + getBidIdParameter, + getValue, + isArray, + logInfo, + logWarn, + triggerPixel +} from '../src/utils.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; + const BIDDER_CODE = 'beop'; const ENDPOINT_URL = 'https://hb.beop.io/bid'; const TCF_VENDOR_ID = 666; @@ -37,22 +48,14 @@ export const spec = { */ buildRequests: function(validBidRequests, bidderRequest) { const slots = validBidRequests.map(beOpRequestSlotsMaker); + const firstPartyData = bidderRequest.ortb2; + const psegs = (firstPartyData && firstPartyData.user && firstPartyData.user.ext && firstPartyData.user.ext.data) ? firstPartyData.user.ext.data.permutive : undefined; const pageUrl = getPageUrl(bidderRequest.refererInfo, window); const gdpr = bidderRequest.gdprConsent; const firstSlot = slots[0]; const kwdsFromRequest = firstSlot.kwds; - let keywords = []; - if (kwdsFromRequest) { - if (isArray(kwdsFromRequest)) { - keywords = kwdsFromRequest; - } else if (isStr(kwdsFromRequest)) { - if (kwdsFromRequest.indexOf(',') != -1) { - keywords = kwdsFromRequest.split(',').map((e) => { return e.trim() }); - } else { - keywords.push(kwdsFromRequest); - } - } - } + let keywords = getAllOrtbKeywords(bidderRequest.ortb2, kwdsFromRequest); + const payloadObject = { at: new Date().toString(), nid: firstSlot.nid, @@ -68,6 +71,10 @@ export const spec = { tc_string: (gdpr && gdpr.gdprApplies) ? gdpr.consentString : null, }; + if (psegs) { + Object.assign(payloadObject, {psegs: psegs}); + } + const payloadString = JSON.stringify(payloadObject); return { method: 'POST', @@ -122,6 +129,7 @@ function buildTrackingParams(data, info, value) { nptnid: params.networkPartnerId, bid: data.bidId || data.requestId, sl_n: data.adUnitCode, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 aid: data.auctionId, se_ca: 'bid', se_ac: info, @@ -150,8 +158,9 @@ function beOpRequestSlotsMaker(bid) { bid: getBidIdParameter('bidId', bid), brid: getBidIdParameter('bidderRequestId', bid), name: getBidIdParameter('adUnitCode', bid), + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 aid: getBidIdParameter('auctionId', bid), - tid: getBidIdParameter('transactionId', bid), + tid: bid.ortb2Imp?.ext?.tid || '', brc: getBidIdParameter('bidRequestsCount', bid), bdrc: getBidIdParameter('bidderRequestCount', bid), bwc: getBidIdParameter('bidderWinsCount', bid), diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index ea28420481d..d615e433cc0 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -43,7 +43,8 @@ export const spec = { rr: getRr(), s: i.params && i.params.s, bidid: i.bidId, - transactionid: i.transactionId, + transactionid: i.ortb2Imp?.ext?.tid, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionid: i.auctionId }; diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js index 4ef2b6dd9f8..dc7731231ab 100644 --- a/modules/bizzclickBidAdapter.js +++ b/modules/bizzclickBidAdapter.js @@ -1,8 +1,9 @@ -import { logMessage, getDNT, deepSetValue, deepAccess, _map, logWarn } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import {_map, deepAccess, deepSetValue, getDNT, logMessage, logWarn} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; + const BIDDER_CODE = 'bizzclick'; const ACCOUNTID_MACROS = '[account_id]'; const URL_ENDPOINT = `https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=${ACCOUNTID_MACROS}`; @@ -92,7 +93,7 @@ export const spec = { host: location.host }, source: { - tid: bidRequest.transactionId, + tid: bidRequest.ortb2Imp?.ext?.tid, ext: { schain: {} } @@ -234,7 +235,9 @@ const prepareImpObject = (bidRequest) => { }; const addNativeParameters = bidRequest => { let impObject = { - id: bidRequest.transactionId, + // TODO: top-level ID is not in ORTB native 1.2, is this intentional? + // (despite the name, this appears to be an ORTB native request - not an imp - object) + id: bidRequest.bidId, ver: NATIVE_VERSION, }; const assets = _map(bidRequest.mediaTypes.native, (bidParams, key) => { diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index ee814807331..9a9d74d14c1 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -154,6 +154,7 @@ export const buildRequests = (validBidRequests, bidderRequest) => { return { sizes: bid.sizes.map((size) => ({ w: size[0], h: size[1] })), id: bid.params.tagId, + // TODO: bidId is globally unique, is it a good choice for transaction ID (vs ortb2Imp.ext.tid)? transactionId: bid.bidId, mediaTypes: Object.keys(bid.mediaTypes), imageUrl: deepAccess(bid, 'params.imageUrl', ''), diff --git a/modules/bluebillywigBidAdapter.js b/modules/bluebillywigBidAdapter.js index 9022ca120af..d4bde9b3f2c 100644 --- a/modules/bluebillywigBidAdapter.js +++ b/modules/bluebillywigBidAdapter.js @@ -276,8 +276,8 @@ export const spec = { }); const request = { - id: bidderRequest.auctionId, - source: {tid: bidderRequest.auctionId}, + id: bidderRequest.bidderRequestId, + source: {tid: bidderRequest.ortb2?.source?.tid}, tmax: BB_CONSTANTS.DEFAULT_TIMEOUT, imp: imps, test: DEV_MODE ? 1 : 0, diff --git a/modules/braveBidAdapter.js b/modules/braveBidAdapter.js index ea8b4af690c..d954522ae24 100644 --- a/modules/braveBidAdapter.js +++ b/modules/braveBidAdapter.js @@ -1,8 +1,8 @@ -import { parseUrl, isEmpty, isStr, triggerPixel } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {isEmpty, isStr, parseUrl, triggerPixel} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; const BIDDER_CODE = 'brave'; const DEFAULT_CUR = 'USD'; @@ -58,7 +58,8 @@ export const spec = { impObject.video = createVideoRequest(br); } else if (br.mediaTypes.native) { impObject.native = { - id: br.transactionId, + // TODO: `id` is not part of the ORTB native spec, is this intentional? + id: br.bidId, ver: '1.2', request: createNativeRequest(br) }; @@ -229,7 +230,8 @@ const createBannerRequest = br => { }; const createVideoRequest = br => { - let videoObj = {id: br.transactionId}; + // TODO: `id` is not part of imp.video in ORTB; is this intentional? + let videoObj = {id: br.bidId}; let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'skip', 'minbitrate', 'maxbitrate', 'api', 'linearity']; for (let param of supportParamsList) { diff --git a/modules/browsiBidAdapter.js b/modules/browsiBidAdapter.js index f66b7b2c353..03b6b2a8f3d 100644 --- a/modules/browsiBidAdapter.js +++ b/modules/browsiBidAdapter.js @@ -38,7 +38,7 @@ export const spec = { const requests = []; const {refererInfo, bidderRequestId, gdprConsent, uspConsent} = bidderRequest; validBidRequests.forEach(bidRequest => { - const {bidId, adUnitCode, auctionId, transactionId, schain, params} = bidRequest; + const {bidId, adUnitCode, auctionId, ortb2Imp, schain, params} = bidRequest; const video = getVideoMediaType(bidRequest); const request = { @@ -55,8 +55,9 @@ export const spec = { sizes: video.playerSize, video: video, aUCode: adUnitCode, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 aID: auctionId, - tID: transactionId, + tID: ortb2Imp?.ext?.tid, schain: schain, params: params } @@ -98,7 +99,6 @@ export const spec = { width: w, height: h, currency: cur, - bidderCode: BIDDER_CODE, ...extraParams }; bidResponses.push(bidResponse); diff --git a/modules/emx_digitalBidAdapter.js b/modules/cadentApertureMXBidAdapter.js similarity index 77% rename from modules/emx_digitalBidAdapter.js rename to modules/cadentApertureMXBidAdapter.js index 99f313b9484..26e8639154c 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/cadentApertureMXBidAdapter.js @@ -15,18 +15,22 @@ import {Renderer} from '../src/Renderer.js'; import {find, includes} from '../src/polyfill.js'; import {parseDomain} from '../src/refererDetection.js'; -const BIDDER_CODE = 'emx_digital'; +const BIDDER_CODE = 'cadent_aperture_mx'; const ENDPOINT = 'hb.emxdgt.com'; const RENDERER_URL = 'https://js.brealtime.com/outstream/1.30.0/bundle.js'; const ADAPTER_VERSION = '1.5.1'; const DEFAULT_CUR = 'USD'; +const ALIASES = [ + { code: 'emx_digital', gvlid: 183 }, + { code: 'cadent', gvlid: 183 }, +]; const EIDS_SUPPORTED = [ { key: 'idl_env', source: 'liveramp.com', rtiPartner: 'idl', queryParam: 'idl' }, { key: 'uid2.id', source: 'uidapi.com', rtiPartner: 'UID2', queryParam: 'uid2' } ]; -export const emxAdapter = { +export const cadentAdapter = { validateSizes: (sizes) => { if (!isArray(sizes) || typeof sizes[0] === 'undefined') { logWarn(BIDDER_CODE + ': Sizes should be an array'); @@ -40,7 +44,7 @@ export const emxAdapter = { buildBanner: (bid) => { let sizes = []; bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes ? sizes = bid.mediaTypes.banner.sizes : sizes = bid.sizes; - if (!emxAdapter.validateSizes(sizes)) { + if (!cadentAdapter.validateSizes(sizes)) { logWarn(BIDDER_CODE + ': could not detect mediaType banner sizes. Assigning to bid sizes instead'); sizes = bid.sizes } @@ -55,13 +59,13 @@ export const emxAdapter = { h: sizes[0][1] }; }, - formatVideoResponse: (bidResponse, emxBid, bidRequest) => { - bidResponse.vastXml = emxBid.adm; + formatVideoResponse: (bidResponse, cadentBid, bidRequest) => { + bidResponse.vastXml = cadentBid.adm; if (bidRequest.bidderRequest && bidRequest.bidderRequest.bids && bidRequest.bidderRequest.bids.length > 0) { const matchingBid = find(bidRequest.bidderRequest.bids, bid => bidResponse.requestId && bid.bidId && bidResponse.requestId === bid.bidId && bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context === 'outstream'); if (matchingBid) { - bidResponse.renderer = emxAdapter.createRenderer(bidResponse, { - id: emxBid.id, + bidResponse.renderer = cadentAdapter.createRenderer(bidResponse, { + id: cadentBid.id, url: RENDERER_URL }); } @@ -81,7 +85,7 @@ export const emxAdapter = { dnt: (navigator.doNotTrack === 'yes' || navigator.doNotTrack === '1' || navigator.msDoNotTrack === '1') ? 1 : 0, h: screen.height, w: screen.width, - devicetype: emxAdapter.isMobile() ? 1 : emxAdapter.isConnectedTV() ? 3 : 2, + devicetype: cadentAdapter.isMobile() ? 1 : cadentAdapter.isConnectedTV() ? 3 : 2, language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), }; }, @@ -114,7 +118,7 @@ export const emxAdapter = { loaded: false }); try { - renderer.setRender(emxAdapter.outstreamRender); + renderer.setRender(cadentAdapter.outstreamRender); } catch (err) { logWarn('Prebid Error calling setRender on renderer', err); } @@ -131,13 +135,13 @@ export const emxAdapter = { videoObj['w'] = bid.mediaTypes.video.playerSize[0]; videoObj['h'] = bid.mediaTypes.video.playerSize[1]; } - return emxAdapter.cleanProtocols(videoObj); + return cadentAdapter.cleanProtocols(videoObj); }, parseResponse: (bidResponseAdm) => { try { return decodeURIComponent(bidResponseAdm.replace(/%(?![0-9][0-9a-fA-F]+)/g, '%25')); } catch (err) { - logError('emx_digitalBidAdapter', 'error', err); + logError('cadent_aperture_mxBidAdapter', 'error', err); } }, getSite: (refInfo) => { @@ -148,45 +152,45 @@ export const emxAdapter = { ref: refInfo.ref || window.document.referrer } }, - getGdpr: (bidRequests, emxData) => { + getGdpr: (bidRequests, cadentData) => { if (bidRequests.gdprConsent) { - emxData.regs = { + cadentData.regs = { ext: { gdpr: bidRequests.gdprConsent.gdprApplies === true ? 1 : 0 } }; } if (bidRequests.gdprConsent && bidRequests.gdprConsent.gdprApplies) { - emxData.user = { + cadentData.user = { ext: { consent: bidRequests.gdprConsent.consentString } }; } - return emxData; + return cadentData; }, - getSupplyChain: (bidderRequest, emxData) => { + getSupplyChain: (bidderRequest, cadentData) => { if (bidderRequest.bids[0] && bidderRequest.bids[0].schain) { - emxData.source = { + cadentData.source = { ext: { schain: bidderRequest.bids[0].schain } }; } - return emxData; + return cadentData; }, // supporting eids getEids(bidRequests) { return EIDS_SUPPORTED - .map(emxAdapter.getUserId(bidRequests)) + .map(cadentAdapter.getUserId(bidRequests)) .filter(x => x); }, getUserId(bidRequests) { return ({ key, source, rtiPartner }) => { let id = deepAccess(bidRequests, `userId.${key}`); - return id ? emxAdapter.formatEid(id, source, rtiPartner) : null; + return id ? cadentAdapter.formatEid(id, source, rtiPartner) : null; }; }, formatEid(id, source, rtiPartner) { @@ -203,6 +207,7 @@ export const emxAdapter = { export const spec = { code: BIDDER_CODE, gvlid: 183, + alias: ALIASES, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { if (!bid || !bid.params) { @@ -211,7 +216,7 @@ export const spec = { } if (bid.bidder !== BIDDER_CODE) { - logWarn(BIDDER_CODE + ': Must use "emx_digital" as bidder code.'); + logWarn(BIDDER_CODE + ': Must use "cadent_aperture_mx" as bidder code.'); return false; } @@ -223,12 +228,12 @@ export const spec = { if (bid.mediaTypes && bid.mediaTypes.banner) { let sizes; bid.mediaTypes.banner.sizes ? sizes = bid.mediaTypes.banner.sizes : sizes = bid.sizes; - if (!emxAdapter.validateSizes(sizes)) { + if (!cadentAdapter.validateSizes(sizes)) { logWarn(BIDDER_CODE + ': Missing sizes in bid'); return false; } } else if (bid.mediaTypes && bid.mediaTypes.video) { - if (!emxAdapter.checkVideoContext(bid)) { + if (!cadentAdapter.checkVideoContext(bid)) { logWarn(BIDDER_CODE + ': Missing video context: instream or outstream'); return false; } @@ -242,13 +247,13 @@ export const spec = { return true; }, buildRequests: function (validBidRequests, bidderRequest) { - const emxImps = []; + const cadentImps = []; const timeout = bidderRequest.timeout || ''; const timestamp = Date.now(); const url = 'https://' + ENDPOINT + ('?t=' + timeout + '&ts=' + timestamp + '&src=pbjs'); const secure = location.protocol.indexOf('https') > -1 ? 1 : 0; - const device = emxAdapter.getDevice(); - const site = emxAdapter.getSite(bidderRequest.refererInfo); + const device = cadentAdapter.getDevice(); + const site = cadentAdapter.getSite(bidderRequest.refererInfo); _each(validBidRequests, function (bid) { let tagid = getBidIdParameter('tagid', bid.params); @@ -269,35 +274,35 @@ export const spec = { if (gpid) { data.ext = {gpid: gpid.toString()}; } - let typeSpecifics = isVideo ? { video: emxAdapter.buildVideo(bid) } : { banner: emxAdapter.buildBanner(bid) }; + let typeSpecifics = isVideo ? { video: cadentAdapter.buildVideo(bid) } : { banner: cadentAdapter.buildBanner(bid) }; let bidfloorObj = bidfloor > 0 ? { bidfloor, bidfloorcur: DEFAULT_CUR } : {}; - let emxBid = Object.assign(data, typeSpecifics, bidfloorObj); - emxImps.push(emxBid); + let cadentBid = Object.assign(data, typeSpecifics, bidfloorObj); + cadentImps.push(cadentBid); }); - let emxData = { + let cadentData = { id: bidderRequest.auctionId, - imp: emxImps, + imp: cadentImps, device, site, cur: DEFAULT_CUR, version: ADAPTER_VERSION }; - emxData = emxAdapter.getGdpr(bidderRequest, Object.assign({}, emxData)); - emxData = emxAdapter.getSupplyChain(bidderRequest, Object.assign({}, emxData)); + cadentData = cadentAdapter.getGdpr(bidderRequest, Object.assign({}, cadentData)); + cadentData = cadentAdapter.getSupplyChain(bidderRequest, Object.assign({}, cadentData)); if (bidderRequest && bidderRequest.uspConsent) { - emxData.us_privacy = bidderRequest.uspConsent; + cadentData.us_privacy = bidderRequest.uspConsent; } // adding eid support if (bidderRequest.userId) { - let eids = emxAdapter.getEids(bidderRequest); + let eids = cadentAdapter.getEids(bidderRequest); if (eids.length > 0) { - if (emxData.user && emxData.user.ext) { - emxData.user.ext.eids = eids; + if (cadentData.user && cadentData.user.ext) { + cadentData.user.ext.eids = eids; } else { - emxData.user = { + cadentData.user = { ext: {eids} }; } @@ -307,7 +312,7 @@ export const spec = { return { method: 'POST', url, - data: JSON.stringify(emxData), + data: JSON.stringify(cadentData), options: { withCredentials: true }, @@ -315,42 +320,42 @@ export const spec = { }; }, interpretResponse: function (serverResponse, bidRequest) { - let emxBidResponses = []; + let cadentBidResponses = []; let response = serverResponse.body || {}; if (response.seatbid && response.seatbid.length > 0 && response.seatbid[0].bid) { - response.seatbid.forEach(function (emxBid) { - emxBid = emxBid.bid[0]; + response.seatbid.forEach(function (cadentBid) { + cadentBid = cadentBid.bid[0]; let isVideo = false; - let adm = emxAdapter.parseResponse(emxBid.adm) || ''; + let adm = cadentAdapter.parseResponse(cadentBid.adm) || ''; let bidResponse = { - requestId: emxBid.id, - cpm: emxBid.price, - width: emxBid.w, - height: emxBid.h, - creativeId: emxBid.crid || emxBid.id, - dealId: emxBid.dealid || null, + requestId: cadentBid.id, + cpm: cadentBid.price, + width: cadentBid.w, + height: cadentBid.h, + creativeId: cadentBid.crid || cadentBid.id, + dealId: cadentBid.dealid || null, currency: 'USD', netRevenue: true, - ttl: emxBid.ttl, + ttl: cadentBid.ttl, ad: adm }; - if (emxBid.adm && emxBid.adm.indexOf(' -1) { + if (cadentBid.adm && cadentBid.adm.indexOf(' -1) { isVideo = true; - bidResponse = emxAdapter.formatVideoResponse(bidResponse, Object.assign({}, emxBid), bidRequest); + bidResponse = cadentAdapter.formatVideoResponse(bidResponse, Object.assign({}, cadentBid), bidRequest); } bidResponse.mediaType = (isVideo ? VIDEO : BANNER); // support for adomain in prebid 5.0 - if (emxBid.adomain && emxBid.adomain.length) { + if (cadentBid.adomain && cadentBid.adomain.length) { bidResponse.meta = { - advertiserDomains: emxBid.adomain + advertiserDomains: cadentBid.adomain }; } - emxBidResponses.push(bidResponse); + cadentBidResponses.push(bidResponse); }); } - return emxBidResponses; + return cadentBidResponses; }, getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { const syncs = []; diff --git a/modules/emx_digitalBidAdapter.md b/modules/cadentApertureMXBidAdapter.md similarity index 58% rename from modules/emx_digitalBidAdapter.md rename to modules/cadentApertureMXBidAdapter.md index 03ba554c5ad..11d63134587 100644 --- a/modules/emx_digitalBidAdapter.md +++ b/modules/cadentApertureMXBidAdapter.md @@ -1,18 +1,18 @@ # Overview ``` -Module Name: EMX Digital Adapter +Module Name: Cadent Aperture MX Adapter Module Type: Bidder Adapter Maintainer: git@emxdigital.com ``` # Description -The EMX Digital adapter provides publishers with access to the EMX Marketplace. The adapter is GDPR compliant. Please note that the adapter supports Banner and Video (Instream & Outstream) media types. +The Cadent Aperture MX adapter provides publishers with access to the Cadent Aperture MX SSP. The adapter is GDPR compliant. Please note that the adapter supports Banner and Video (Instream & Outstream) media types. -Note: The EMX Digital adapter requires approval and implementation guidelines from the EMX team, including existing publishers that work with EMX Digital. Please reach out to your account manager or prebid@emxdigital.com for more information. +Note: The Cadent Aperture MX adapter requires approval and implementation guidelines from the Cadent team, including existing publishers that work with Cadent. Please reach out to your account manager or prebid@emxdigital.com for more information. -The bidder code should be ```emx_digital``` +The bidder code should be ```cadent_aperture_mx``` The params used by the bidder are : ```tagid``` - string (mandatory) ```bidfloor``` - string (optional) @@ -29,7 +29,7 @@ var adUnits = [{ }, bids: [ { - bidder: 'emx_digital', + bidder: 'cadent_aperture_mx', params: { tagid: '25251', } @@ -49,7 +49,7 @@ var adUnits = [{ }, bids: [ { - bidder: 'emx_digital', + bidder: 'cadent_aperture_mx', params: { tagid: '25251', video: { diff --git a/modules/captifyRtdProvider.js b/modules/captifyRtdProvider.js deleted file mode 100644 index b97aa49a48e..00000000000 --- a/modules/captifyRtdProvider.js +++ /dev/null @@ -1,146 +0,0 @@ -/** - * This module adds Captify real time data provider module - * The {@link module:modules/realTimeData} module is required - * The module will fetch segments (page-centric) from Captify live-classification server - * @module modules/captifyRtdProvider - * @requires module:modules/realTimeData - */ -import { submodule } from '../src/hook.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { ajax } from '../src/ajax.js'; -import { config } from '../src/config.js'; -import {deepAccess, isArray, isEmptyStr, isNumber, logError} from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; - -const MODULE_NAME = 'realTimeData'; -const SUBMODULE_NAME = 'CaptifyRTDModule'; -const DEFAULT_LC_URL = 'https://live-classification.cpx.to/prebid-segments'; - -const STATUS = { - SUCCESS: 200, - ACCEPTED: 202, -}; - -/** - * Set `appnexusAuctionKeywords` that appnexus bidder will read and send in request to Xandr - * @param {Array} segments captify segments for Appnexus(Xandr) system, in form of [id1, id2, id3] - * where id1, id2, id3 - actual Xandr segment ids with keywords enabled - */ -export function setAppnexusSegments(segments) { - config.setConfig({ - appnexusAuctionKeywords: { - 'captify_segments': segments, - }, - }) -} - -/** - * Function returns only bidders that contained both, in moduleConfig and at least one adUnit. - * @param {Array} bidders Contains list of bidders, to set targeting for - * @param {Object} data Response of live-classification service - */ -export function addSegmentData(bidders, data) { - for (const bidder of bidders) { - if (bidder === 'appnexus') { setAppnexusSegments(data['xandr']) } - } -} - -/** - * Function returns only bidders that contained in both, moduleConfig and at least one adUnit. - * @param {Object} moduleConfig Config object passed to the module - * @param {Object} reqBidsConfigObj Config object for the bidders; each adapter has its own entry - */ -export function getMatchingBidders(moduleConfig, reqBidsConfigObj) { - const biddersFromConf = deepAccess(moduleConfig, 'params.bidders'); - - const adUnitBidders = reqBidsConfigObj.adUnits - .flatMap(({bids}) => bids.map(({bidder}) => bidder)) - .filter((e, i, a) => a.indexOf(e) === i); - - if (!isArray(adUnitBidders) || !adUnitBidders.length) { - logError(SUBMODULE_NAME, 'Missing parameter bidders in bidRequestConfig'); - return []; - } - - return biddersFromConf.filter(bidder => adUnitBidders.includes(bidder)); -} - -/** - * Main function that sets Captify targeting for various bidders - * @param {Object} moduleConfig Config object passed to the module - * @param {Function} onDone callback function that executed when everything is done - * @param {Object} reqBidsConfigObj Config object for the bidders; each adapter has its own entry - * @param {Object} gcv contains data related to user consent, if applies - */ -export function setCaptifyTargeting(reqBidsConfigObj, onDone, moduleConfig, gcv) { - const pbjsVer = getGlobal(); - const ref = getRefererInfo().referer; - const url = document.URL; - const pubId = moduleConfig.params.pubId; - const bidders = getMatchingBidders(moduleConfig, reqBidsConfigObj) - const requestBody = { - pubId, - ref, - url, - pbjsVer, - gcv - }; - let requestUrl = moduleConfig.params.url; - if (!requestUrl || isEmptyStr(requestUrl)) { - requestUrl = DEFAULT_LC_URL; - } - - if (!bidders.length) { - logError(SUBMODULE_NAME, 'There are no matched bidders to work with'); - return; - } - - ajax(requestUrl, { - success: function (response, req) { - if (req.status === STATUS.SUCCESS) { - try { - const data = JSON.parse(response); - if (data) { - addSegmentData(bidders, data); - } - } catch (e) { - logError(SUBMODULE_NAME, 'Unable to parse live-classification data' + e); - } - } - onDone(); - }, - error: function () { - onDone(); - logError(SUBMODULE_NAME, 'Unable to get live-classification data'); - } - }, - JSON.stringify(requestBody), - { - contentType: 'application/json', - method: 'POST', - }); -} - -export function init(moduleConfig, userConsent) { - // Validate bidders - const biddersFromConf = deepAccess(moduleConfig, 'params.bidders'); - if (!isArray(biddersFromConf) || !biddersFromConf.length) { - logError(SUBMODULE_NAME, 'Missing parameter bidders in moduleConfig'); - return false - } - const publisherId = deepAccess(moduleConfig, 'params.pubId'); - // Publisher Id - if (!isNumber(publisherId)) { - logError(SUBMODULE_NAME, 'Missing parameter pubId in moduleConfig'); - return false - } - return true -} - -export const captifySubmodule = { - name: SUBMODULE_NAME, - init: init, - setCaptifyTargeting -}; - -submodule(MODULE_NAME, captifySubmodule); diff --git a/modules/captifyRtdProvider.md b/modules/captifyRtdProvider.md deleted file mode 100644 index a1a8e9c273f..00000000000 --- a/modules/captifyRtdProvider.md +++ /dev/null @@ -1,68 +0,0 @@ -# Captify Real-Time Data Submodule - -# Overview - - Module Name: Captify Rtd Provider - Module Type: Rtd Provider - Layout: integrationExamples/gpt/captifyRtdProvider_example.html - Maintainer: prebid@captify.tech - -# Description - -Captify uses publisher first-party on-site search data to power machine learning algorithms to create a suite of -contextual based targeting solutions that activate in a cookieless environment. - -The RTD submodule allows bid requests to be classified by our live-classification service on the first ad call, -maximising value for publishers by increasing scale for advertisers. - -Segments will be attached to bid request objects sent to different SSPs in order to optimize targeting. - -Contact prebid@captify.tech for information. - -### Publisher Usage - -Compile the Captify RTD module into your Prebid build: - -`npm ci && gulp build --modules=rtdModule,appnexusBidAdapter,captifyRtdProvider` - -Add the Captify RTD provider to your Prebid config. - -```javascript -pbjs.setConfig({ - realTimeData: { - auctionDelay: 1000, - dataProviders: [ - { - name: "CaptifyRTDModule", - waitForIt: true, - params: { - pubId: 123456, - bidders: ['appnexus'], - } - } - ] - } -}); -``` - -### Parameter Description -This module is configured as part of the `realTimeData.dataProviders` object. - -| Name |Type | Description |Mandatory | Notes | -| :------------- | :------------ | :------------------------------------------------------------------ |:---------|:------------ | -| name | String | Real time data module name | yes | Always 'CaptifyRTDModule' | -| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (recommended) | no | Default `false` | -| params | Object | | | | -| params.pubId | Integer | Partner ID, required to get results and provided by Captify | yes | Use 123456 for tests and speak to your Captify account manager to receive your pubId | -| params.bidders | Array | List of bidders for which you would like data to be set | yes | Currently only 'appnexus' supported | -| params.url | String | Captify live-classification service url | no | Defaults to `https://live-classification.cpx.to/prebid-segments` - -### Testing - -To view an example of available segments returned by Captify: - -`gulp serve --modules=rtdModule,captifyRtdProvider,appnexusBidAdapter` - -and then point your browser at: - -`http://localhost:9999/integrationExamples/gpt/captifyRtdProvider_example.html?pbjs_debug=true` diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 7267452dd4c..cb7b5fbe7c5 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -48,6 +48,7 @@ export const spec = { const eids = getFirstWithKey(validBidRequests, 'userIdAsEids'); const schain = getFirstWithKey(validBidRequests, 'schain'); const request = { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: bidderRequest.auctionId, currency, hb_version: '$prebid.version$', diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 28f3fe3a166..b1fcb29e3d0 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -178,7 +178,7 @@ export const spec = { requestBody.imp = [] requestBody.site = _getSiteObj(bidderRequest) requestBody.device = _getDeviceObj() - requestBody.id = bidderRequest.bids[0].auctionId + requestBody.id = bidderRequest.bidderRequestId; requestBody.ext = {'ce': (storage.cookiesAreEnabled() ? 1 : 0)} // Attaching GDPR Consent Params diff --git a/modules/cleanmedianetBidAdapter.js b/modules/cleanmedianetBidAdapter.js index 46253d7af69..601a237baa8 100644 --- a/modules/cleanmedianetBidAdapter.js +++ b/modules/cleanmedianetBidAdapter.js @@ -80,11 +80,11 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map(bidRequest => { - const {adUnitCode, auctionId, mediaTypes, params, sizes, transactionId} = bidRequest; + const {adUnitCode, bidId, mediaTypes, params, sizes} = bidRequest; const baseEndpoint = (params['rtbEndpoint'] || ENDPOINTS['cleanmedianet']).replace(/^http:/, 'https:'); const rtbEndpoint = `${baseEndpoint}/r/${params.supplyPartnerId}/bidr?rformat=open_rtb&reqformat=rtb_json&bidder=prebid` + (params.query ? '&' + params.query : ''); const rtbBidRequest = { - id: auctionId, + id: bidId, site: { domain: bidderRequest.refererInfo.domain, page: bidderRequest.refererInfo.page, @@ -118,7 +118,7 @@ export const spec = { } const imp = { - id: transactionId, + id: bidId, instl: deepAccess(bidderRequest.ortb2Imp, 'instl') === 1 || params.instl === 1 ? 1 : 0, tagid: adUnitCode, bidfloor: helper.getBidFloor(bidRequest) || 0, diff --git a/modules/codefuelBidAdapter.js b/modules/codefuelBidAdapter.js index bde168a79e3..2548b20189b 100644 --- a/modules/codefuelBidAdapter.js +++ b/modules/codefuelBidAdapter.js @@ -58,7 +58,7 @@ export const spec = { }); const request = { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, site: { page, domain, publisher }, device: { ua, devicetype }, source: { fd: 1 }, diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index 082fb0ac4db..b1ee8875422 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -116,6 +116,15 @@ export const spec = { request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL'; request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; } + + // Add GPP consent + if (bidderRequest.gppConsent) { + request.gpp = bidderRequest.gppConsent.gppString; + request.gpp_sid = bidderRequest.gppConsent.applicableSections; + } else if (bidderRequest.ortb2?.regs?.gpp) { + request.gpp = bidderRequest.ortb2.regs.gpp; + request.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; + } } for (let i = 0; i < validBidRequests.length; i++) { @@ -125,7 +134,7 @@ export const spec = { placementId: bid.params.placement_id, groupId: bid.params.group_id, bidId: bid.bidId, - tid: bid.transactionId, + tid: bid.ortb2Imp?.ext?.tid, eids: [], floor: {} }; diff --git a/modules/conceptxBidAdapter.js b/modules/conceptxBidAdapter.js new file mode 100644 index 00000000000..1147a50e71f --- /dev/null +++ b/modules/conceptxBidAdapter.js @@ -0,0 +1,70 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +// import { logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; + +const BIDDER_CODE = 'conceptx'; +let ENDPOINT_URL = 'https://conceptx.cncpt-central.com/openrtb'; +// const LOG_PREFIX = 'ConceptX: '; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!(bid.bidId); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + // logWarn(LOG_PREFIX + 'all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)'); + const requests = []; + + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + ENDPOINT_URL += '?gdpr_applies=' + bidderRequest.gdprConsent.gdprApplies; + ENDPOINT_URL += '&consentString=' + bidderRequest.gdprConsent.consentString; + } + for (var i = 0; i < validBidRequests.length; i++) { + const requestParent = { adUnits: [], meta: {} }; + const bid = validBidRequests[i] + const { adUnitCode, auctionId, bidId, bidder, bidderRequestId, ortb2 } = bid + requestParent.meta = { adUnitCode, auctionId, bidId, bidder, bidderRequestId, ortb2 } + + const { site, adunit } = bid.params + const adUnit = { site, adunit, targetId: bid.bidId } + if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes + requestParent.adUnits.push(adUnit); + requests.push({ + method: 'POST', + url: ENDPOINT_URL, + options: { + withCredentials: false, + }, + data: JSON.stringify(requestParent), + }); + } + + return requests; + }, + + interpretResponse: function (serverResponse, bidRequest) { + const bidResponsesFromServer = serverResponse.body.bidResponses; + const firstDummy = bidResponsesFromServer[0] + const firstSeat = firstDummy.ads[0] + const bidResponses = []; + const bidResponse = { + requestId: firstSeat.requestId, + cpm: firstSeat.cpm, + width: firstSeat.width, + height: firstSeat.height, + creativeId: firstSeat.creativeId, + dealId: firstSeat.dealId, + currency: firstSeat.currency, + netRevenue: true, + ttl: firstSeat.ttl, + referrer: firstSeat.referrer, + ad: firstSeat.html + }; + bidResponses.push(bidResponse); + return bidResponses; + }, + +} +registerBidder(spec); diff --git a/modules/conceptxBidAdapter.md b/modules/conceptxBidAdapter.md new file mode 100644 index 00000000000..1464c04025a --- /dev/null +++ b/modules/conceptxBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +``` +Module Name: ConceptX Bidder Adapter +Module Type: Bidder Adapter +Maintainer: info@concept.dk +``` + +# Description + +ConceptX Bidder Adapter for Prebid.js. +Only Banner format is supported. + +# Test Parameters +``` + var adUnits = [ + { + code: "test-div", + mediaTypes: { + banner: { + sizes: [[980, 180]] + } + }, + bids: [ + { + bidder: "conceptx", + params: { + site: "example", + adunit: "some-id-3", + } + }, + ] + }, + + ]; +``` diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js index a25d9086446..bf4079322ff 100644 --- a/modules/concertBidAdapter.js +++ b/modules/concertBidAdapter.js @@ -66,7 +66,7 @@ export const spec = { let slot = { name: bidRequest.adUnitCode, bidId: bidRequest.bidId, - transactionId: bidRequest.transactionId, + transactionId: bidRequest.ortb2Imp?.ext?.tid, sizes: bidRequest.params.sizes || bidRequest.sizes, partnerId: bidRequest.params.partnerId, slotType: bidRequest.params.slotType, diff --git a/modules/connectIdSystem.js b/modules/connectIdSystem.js index d4bf0356242..1caff49199d 100644 --- a/modules/connectIdSystem.js +++ b/modules/connectIdSystem.js @@ -11,11 +11,16 @@ import {includes} from '../src/polyfill.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {getStorageManager} from '../src/storageManager.js'; import {formatQS, isPlainObject, logError, parseUrl} from '../src/utils.js'; -import {uspDataHandler} from '../src/adapterManager.js'; +import {uspDataHandler, gppDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; const MODULE_NAME = 'connectId'; -const STORAGE_EXPIRY_DAYS = 14; +const STORAGE_EXPIRY_DAYS = 365; +const STORAGE_DURATION = 60 * 60 * 24 * 1000 * STORAGE_EXPIRY_DAYS; +const ID_EXPIRY_DAYS = 14; +const VALID_ID_DURATION = 60 * 60 * 24 * 1000 * ID_EXPIRY_DAYS; +const PUID_EXPIRY_DAYS = 30; +const PUID_EXPIRY = 60 * 60 * 24 * 1000 * PUID_EXPIRY_DAYS; const VENDOR_ID = 25; const PLACEHOLDER = '__PIXEL_ID__'; const UPS_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`; @@ -28,11 +33,11 @@ export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleNam * @param {Object} obj */ function storeObject(obj) { - const expires = Date.now() + (60 * 60 * 24 * 1000 * STORAGE_EXPIRY_DAYS); + const expires = Date.now() + STORAGE_DURATION; if (storage.cookiesAreEnabled()) { setEtldPlusOneCookie(MODULE_NAME, JSON.stringify(obj), new Date(expires), getSiteHostname()); - } else if (storage.localStorageIsEnabled()) { - obj.__expires = expires; + } + if (storage.localStorageIsEnabled()) { storage.setDataInLocalStorage(MODULE_NAME, JSON.stringify(obj)); } } @@ -75,7 +80,9 @@ function getIdFromLocalStorage() { if (storedIdData) { try { storedIdData = JSON.parse(storedIdData); - } catch {} + } catch (e) { + logError(`${MODULE_NAME} module: error while reading the local storage data.`); + } if (isPlainObject(storedIdData) && storedIdData.__expires && storedIdData.__expires <= Date.now()) { storage.removeDataFromLocalStorage(MODULE_NAME); @@ -87,6 +94,34 @@ function getIdFromLocalStorage() { return null; } +function syncLocalStorageToCookie() { + if (!storage.cookiesAreEnabled()) { + return; + } + const value = getIdFromLocalStorage(); + const newCookieExpireTime = Date.now() + STORAGE_DURATION; + setEtldPlusOneCookie(MODULE_NAME, JSON.stringify(value), new Date(newCookieExpireTime), getSiteHostname()); +} + +function isStale(storedIdData) { + if (isPlainObject(storedIdData) && storedIdData.lastSynced && + (storedIdData.lastSynced + VALID_ID_DURATION) <= Date.now()) { + return true; + } + return false; +} + +function getStoredId() { + let storedId = getIdFromCookie(); + if (!storedId) { + storedId = getIdFromLocalStorage(); + if (storedId && !isStale(storedId)) { + syncLocalStorageToCookie(); + } + } + return storedId; +} + function getSiteHostname() { const pageInfo = parseUrl(getRefererInfo().page); return pageInfo.hostname; @@ -127,16 +162,31 @@ export const connectIdSubmodule = { return; } const params = config.params || {}; - if (!params || (typeof params.he !== 'string' && typeof params.puid !== 'string') || + if (!params || (typeof params.pixelId === 'undefined' && typeof params.endpoint === 'undefined')) { - logError(`${MODULE_NAME} module: configurataion requires the 'pixelId' and at ` + - `least one of the 'he' or 'puid' parameters to be defined.`); + logError(`${MODULE_NAME} module: configuration requires the 'pixelId'.`); return; } - const storedId = getIdFromCookie() || getIdFromLocalStorage(); + const storedId = getStoredId(); + + let shouldResync = isStale(storedId); + if (storedId) { - return {id: storedId}; + if (isPlainObject(storedId) && storedId.puid && storedId.lastUsed && !params.puid && + (storedId.lastUsed + PUID_EXPIRY) <= Date.now()) { + delete storedId.puid; + shouldResync = true; + } + if ((params.he && params.he !== storedId.he) || + (params.puid && params.puid !== storedId.puid)) { + shouldResync = true; + } + if (!shouldResync) { + storedId.lastUsed = Date.now(); + storeObject(storedId); + return {id: storedId}; + } } const uspString = uspDataHandler.getConsentData() || ''; @@ -148,6 +198,14 @@ export const connectIdSubmodule = { us_privacy: uspString }; + const gppConsent = gppDataHandler.getConsentData(); + if (gppConsent) { + data.gpp = `${gppConsent.gppString ? gppConsent.gppString : ''}`; + if (Array.isArray(gppConsent.applicableSections)) { + data.gpp_sid = gppConsent.applicableSections.join(','); + } + } + let topmostLocation = getRefererInfo().topmostLocation; if (typeof topmostLocation === 'string') { data.url = topmostLocation.split('?')[0]; @@ -159,6 +217,14 @@ export const connectIdSubmodule = { } }); + const hashedEmail = params.he || storedId?.he; + if (hashedEmail) { + data.he = hashedEmail; + } + if (!data.puid && storedId?.puid) { + data.puid = storedId.puid; + } + const resp = function (callback) { const callbacks = { success: response => { @@ -166,7 +232,12 @@ export const connectIdSubmodule = { if (response) { try { responseObj = JSON.parse(response); - if (isPlainObject(responseObj) && Object.keys(responseObj).length > 0) { + if (isPlainObject(responseObj) && Object.keys(responseObj).length > 0 && + (!!responseObj.connectId || !!responseObj.connectid)) { + responseObj.he = params.he; + responseObj.puid = params.puid || responseObj.puid; + responseObj.lastSynced = Date.now(); + responseObj.lastUsed = Date.now(); storeObject(responseObj); } else { logError(`${MODULE_NAME} module: UPS response returned an invalid payload ${response}`); @@ -186,7 +257,12 @@ export const connectIdSubmodule = { let url = `${params.endpoint || endpoint}?${formatQS(data)}`; connectIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true}); }; - return {callback: resp}; + const result = {callback: resp}; + if (shouldResync && storedId) { + result.id = storedId; + } + + return result; }, /** diff --git a/modules/connectIdSystem.md b/modules/connectIdSystem.md index a3b69a0082c..bf5ac8a0e8b 100644 --- a/modules/connectIdSystem.md +++ b/modules/connectIdSystem.md @@ -2,6 +2,8 @@ Yahoo ConnectID user ID Module. +*Note: The storage config should be ommited as the module handles the storage of the needed information. + ### Prebid Params ``` @@ -9,11 +11,6 @@ pbjs.setConfig({ userSync: { userIds: [{ name: 'connectId', - storage: { - name: 'connectId', - type: 'html5', - expires: 15 - }, params: { pixelId: 58776, he: '0bef996248d63cea1529cb86de31e9547a712d9f380146e98bbd39beec70355a' @@ -31,5 +28,5 @@ The below parameters apply only to the Yahoo ConnectID user ID Module. | params | Required | Object | Container of all module params. || | params.pixelId | Required | Number | The Yahoo-supplied publisher-specific pixel ID. | `"0000"` | -| params.he | Optional | String | The SHA-256 hashed user email address which has been lowercased prior to hashing. Pass both `he` and `puid` params if present, otherwise pass either of the two that is available. |`"ed8ddbf5a171981db8ef938596ca297d5e3f84bcc280041c5880dba3baf9c1d4"`| -| params.puid | Optional | String | The publisher supplied user identifier such as a first-party cookie. Pass both `he` and `puid` params if present, otherwise pass either of the two that is available. | `"ab9iibf5a231ii1db8ef911596ca297d5e3f84biii00041c5880dba3baf9c1da"` | +| params.he | Optional | String | The SHA-256 hashed user email address which has been lowercased prior to hashing. |`"ed8ddbf5a171981db8ef938596ca297d5e3f84bcc280041c5880dba3baf9c1d4"`| +| params.puid | Optional | String | A domain-specific user identifier such as a first-party cookie. If not passed, a puid value will be auto-generated and stored in local and / or cookie storage. | `"ab9iibf5a231ii1db8ef911596ca297d5e3f84biii00041c5880dba3baf9c1da"` | diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index d53e3b28ab5..d5665b318be 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -77,6 +77,7 @@ export const spec = { validBidRequests.map(bid => { const placement = Object.assign({ + // TODO: fix transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 id: bid.transactionId, divName: bid.bidId, pisze: bid.mediaTypes.banner.sizes[0] || bid.sizes[0], diff --git a/modules/conversantAnalyticsAdapter.js b/modules/conversantAnalyticsAdapter.js index ce4597eecfe..0c58402ca87 100644 --- a/modules/conversantAnalyticsAdapter.js +++ b/modules/conversantAnalyticsAdapter.js @@ -6,6 +6,8 @@ import adapterManager from '../src/adapterManager.js'; import {logInfo, logWarn, logError, logMessage, deepAccess, isInteger} from '../src/utils.js'; import {getRefererInfo} from '../src/refererDetection.js'; +// Maintainer: mediapsr@epsilon.com + const { EVENTS: { AUCTION_END, AD_RENDER_FAILED, BID_TIMEOUT, BID_WON, BIDDER_ERROR } } = CONSTANTS; diff --git a/modules/conversantAnalyticsAdapter.md b/modules/conversantAnalyticsAdapter.md deleted file mode 100644 index 2f026cbcbb9..00000000000 --- a/modules/conversantAnalyticsAdapter.md +++ /dev/null @@ -1,46 +0,0 @@ -# Overview -- Module Name: Epsilon Analytics Adapter -- Module Type: Analytics Adapter -- Maintainer: mediapsr@epsilon.com - -## Description - -Analytics adapter for Epsilon (formerly Conversant) is used to track performance of Prebid auctions. See the usage below for how to -configure the adapter for your webpage. To enable analytics and gain access to the data publishers will need - to contact their Epsilon representative (publishersupport@epsilon.com). - -## Setup - -Before any analytics are recorded for your website you will need to have an Epsilon representative turn -on Prebid analytics for your website. - -The simplest configuration to add Epsilon Prebid analytics to your page is as follows: - -``` - pbjs.que.push(function() { - pbjs.enableAnalytics({ - provider: 'conversant', - options: { - site_id: - } - }) - }); -``` - -Additionally, the following options are supported: - -- **cnvr_sampling**: Sample rate for analytics data. Value should be between 0 and 1 (inclusive), 0 == never sample, -1 == always sample, 0.5 == send analytics 50% of the time. - -### Complete Example -``` - pbjs.que.push(function() { - pbjs.enableAnalytics({ - provider: 'conversant', - options: { - site_id: 1234, - cnvr_sampling: 0.9 - } - }) - }); -``` diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index b5a1424dcba..fd436e51461 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -1,8 +1,27 @@ -import { logWarn, isStr, deepAccess, isArray, getBidIdParameter, deepSetValue, isEmpty, _each, convertTypes, parseUrl, mergeDeep, buildUrl, _map, logError, isFn, isPlainObject } from '../src/utils.js'; +import { + logWarn, + isStr, + deepAccess, + isArray, + getBidIdParameter, + deepSetValue, + isEmpty, + _each, + convertTypes, + parseUrl, + mergeDeep, + buildUrl, + _map, + logError, + isFn, + isPlainObject, +} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; +// Maintainer: mediapsr@epsilon.com + const GVLID = 24; const BIDDER_CODE = 'conversant'; @@ -56,7 +75,6 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { const page = (bidderRequest && bidderRequest.refererInfo) ? bidderRequest.refererInfo.page : ''; let siteId = ''; - let requestId = ''; let pubcid = null; let pubcidName = '_pubcid'; let bidurl = URL; @@ -67,8 +85,6 @@ export const spec = { siteId = getBidIdParameter('site_id', bid.params) || siteId; pubcidName = getBidIdParameter('pubcid_name', bid.params) || pubcidName; - requestId = bid.auctionId; - const imp = { id: bid.bidId, secure: 1, @@ -122,10 +138,10 @@ export const spec = { }); const payload = { - id: requestId, + id: bidderRequest.bidderRequestId, imp: conversantImps, source: { - tid: requestId + tid: bidderRequest.ortb2?.source?.tid, }, site: { id: siteId, diff --git a/modules/conversantBidAdapter.md b/modules/conversantBidAdapter.md deleted file mode 100644 index baf8b756aca..00000000000 --- a/modules/conversantBidAdapter.md +++ /dev/null @@ -1,46 +0,0 @@ -# Overview - -- Module Name: Epsilon Bidder Adapter -- Module Type: Bidder Adapter -- Maintainer: mediapsr@epsilon.com - -# Description - -Module that connects to Epsilon's (formerly Conversant) demand sources. Supports banners and videos. - -# Test Parameters -``` -var adUnits = [ - { - code: 'banner-test-div', - mediaTypes: { - banner: { - sizes: [[300, 250],[300,600]] - } - }, - bids: [{ - bidder: "conversant", - params: { - site_id: '108060' - } - }] - },{ - code: 'video-test-div', - mediaTypes: { - video: { - context: 'instream', - playerSize: [640, 480], - api: [2], - protocols: [1, 2], - mimes: ['video/mp4'] - } - }, - bids: [{ - bidder: "conversant", - params: { - site_id: '108060', - white_label_url: 'https://web.hb.ad.cpe.dotomi.com/s2s/header/24' - } - }] - }]; -``` diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index 8ffdabcb597..8f7821173c1 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -1,12 +1,4 @@ -import { - convertCamelToUnderscore, - convertTypes, - getBidRequest, - isArray, - isEmpty, - logError, - transformBidderParamKeywords -} from '../src/utils.js'; +import {convertCamelToUnderscore, convertTypes, getBidRequest, logError} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {auctionManager} from '../src/auctionManager.js'; @@ -14,7 +6,8 @@ import {find, includes} from '../src/polyfill.js'; import {getStorageManager} from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'craft'; const URL_BASE = 'https://gacraft.jp/prebid-v3'; @@ -114,9 +107,6 @@ export const spec = { 'keywords': transformBidderParamKeywords, }, params); if (isOpenRtb) { - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -136,16 +126,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function formatRequest(payload, bidderRequest) { let options = {}; if (!hasPurpose1Consent(bidderRequest?.gdprConsent)) { @@ -200,13 +180,11 @@ function bidToTag(bid) { tag.primary_size = tag.sizes[0]; tag.ad_types = []; tag.uuid = bid.bidId; - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } + const keywords = getANKeywordParam(bid.ortb2, bid.params.keywords); + if (keywords.length) { tag.keywords = keywords; } + // TODO: why does this need to iterate through every ad unit? let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { tag.ad_types.push(BANNER); diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 8b17d0372b5..993346df849 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, isArray, logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; +import { deepAccess, generateUUID, isArray, logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; import { loadExternalScript } from '../src/adloader.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; @@ -28,7 +28,7 @@ const LOG_PREFIX = 'Criteo: '; Unminified source code can be found in the privately shared repo: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js */ const FAST_BID_VERSION_PLACEHOLDER = '%FAST_BID_VERSION%'; -export const FAST_BID_VERSION_CURRENT = 135; +export const FAST_BID_VERSION_CURRENT = 136; const FAST_BID_VERSION_LATEST = 'latest'; const FAST_BID_VERSION_NONE = 'none'; const PUBLISHER_TAG_URL_TEMPLATE = 'https://static.criteo.net/js/ld/publishertag.prebid' + FAST_BID_VERSION_PLACEHOLDER + '.js'; @@ -177,7 +177,14 @@ export const spec = { if (publisherTagAvailable()) { // eslint-disable-next-line no-undef - const adapter = new Criteo.PubTag.Adapters.Prebid(PROFILE_ID_PUBLISHERTAG, ADAPTER_VERSION, bidRequests, bidderRequest, '$prebid.version$'); + const adapter = new Criteo.PubTag.Adapters.Prebid( + PROFILE_ID_PUBLISHERTAG, + ADAPTER_VERSION, + bidRequests, + bidderRequest, + '$prebid.version$', + { createOutstreamVideoRenderer: createOutstreamVideoRenderer } + ); url = adapter.buildCdbUrl(); data = adapter.buildCdbRequest(); } else { @@ -235,7 +242,7 @@ export const spec = { bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [slot.adomain].flat() }); } if (slot.ext?.meta?.networkName) { - bid.meta = Object.assign({}, bid.meta, {networkName: slot.ext.meta.networkName}) + bid.meta = Object.assign({}, bid.meta, { networkName: slot.ext.meta.networkName }) } if (slot.native) { if (bidRequest.params.nativeCallback) { @@ -420,6 +427,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { let schain; let userIdAsEids; const request = { + id: generateUUID(), publisher: { url: context.url, ext: bidderRequest.publisherExt, @@ -437,8 +445,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { schain = bidRequest.schain || schain; const slot = { impid: bidRequest.adUnitCode, - transactionid: bidRequest.transactionId, - auctionId: bidRequest.auctionId, + transactionid: bidRequest.ortb2Imp?.ext?.tid }; if (bidRequest.params.zoneId) { slot.zoneid = bidRequest.params.zoneId; @@ -508,11 +515,14 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (networkId) { request.publisher.networkid = networkId; } + + request.source = { + tid: bidderRequest.ortb2?.source?.tid + }; + if (schain) { - request.source = { - ext: { - schain: schain - } + request.source.ext = { + schain: schain }; }; request.user = bidderRequest.ortb2?.user || {}; @@ -541,6 +551,15 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { request.user.ext = request.user.ext || {}; request.user.ext.eids = [...userIdAsEids]; } + if (bidderRequest && bidderRequest.ortb2?.bcat) { + request.bcat = bidderRequest.ortb2.bcat; + } + if (bidderRequest && bidderRequest.ortb2?.badv) { + request.badv = bidderRequest.ortb2.badv; + } + if (bidderRequest && bidderRequest.ortb2?.bapp) { + request.bapp = bidderRequest.ortb2.bapp; + } return request; } @@ -744,7 +763,7 @@ function createOutstreamVideoRenderer(slot) { window.CriteoOutStream[slot.ext.videoPlayerType].play(payload, outstreamConfig) }; - const renderer = Renderer.install({url: PUBLISHER_TAG_OUTSTREAM_SRC, config: config}); + const renderer = Renderer.install({ url: PUBLISHER_TAG_OUTSTREAM_SRC, config: config }); renderer.setRender(render); return renderer; } diff --git a/modules/currency.js b/modules/currency.js index 9272a507d05..3da0cfe73e8 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -238,9 +238,9 @@ function wrapFunction(fn, context, params) { bid.currency = adServerCurrency; } } catch (e) { - logWarn('Returning NO_BID, getCurrencyConversion threw error: ', e); - // TODO: in v8, this should not continue with a "NO_BID" - params[1] = params[2](CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY); + logWarn('getCurrencyConversion threw error: ', e); + params[2](CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY); + return; } } return fn.apply(context, params); diff --git a/modules/dailyhuntBidAdapter.js b/modules/dailyhuntBidAdapter.js index 5ae8ee3ea21..f96e07b71bf 100644 --- a/modules/dailyhuntBidAdapter.js +++ b/modules/dailyhuntBidAdapter.js @@ -4,7 +4,7 @@ import {_map, deepAccess, isEmpty} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; import {find} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; const BIDDER_CODE = 'dailyhunt'; const BIDDER_ALIAS = 'dh'; @@ -99,7 +99,7 @@ const createOrtbRequest = (validBidRequests, bidderRequest) => { let user = createOrtbUserObj(validBidRequests) let site = createOrtbSiteObj(validBidRequests, bidderRequest.refererInfo.page) return { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, imp: [], site, device, diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index 4e21e08ba57..11d3ebb1589 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -1,10 +1,11 @@ -import { getWindowTop, isGptPubadsDefined, deepAccess, getAdUnitSizes, isEmpty } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { ajax } from '../src/ajax.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {deepAccess, getAdUnitSizes, getWindowTop, isEmpty, isGptPubadsDefined} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {ajax} from '../src/ajax.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; + export const storage = getStorageManager({bidderCode: 'datablocks'}); const NATIVE_ID_MAP = {}; @@ -229,6 +230,7 @@ export const spec = { let scope = this; if (isGptPubadsDefined()) { if (typeof window['googletag'].pubads().addEventListener == 'function') { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 window['googletag'].pubads().addEventListener('impressionViewable', function(event) { scope.queue_metric({type: 'slot_view', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); }); @@ -400,7 +402,7 @@ export const spec = { method: 'POST', url: `https://${host}/openrtb/?sid=${sourceId}`, data: { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, imp: imps, site: site, device: device diff --git a/modules/datawrkzBidAdapter.js b/modules/datawrkzBidAdapter.js index 193a1723403..2cf28c36330 100644 --- a/modules/datawrkzBidAdapter.js +++ b/modules/datawrkzBidAdapter.js @@ -414,8 +414,6 @@ function buildBannerResponse(bidRequest, bidResponse) { } let bidSizes = (deepAccess(bidRequest, 'mediaTypes.banner.sizes')) ? deepAccess(bidRequest, 'mediaTypes.banner.sizes') : bidRequest.sizes; bidResponse.requestId = bidRequest.bidId; - bidResponse.auctionId = bidRequest.auctionId; - bidResponse.transactionId = bidRequest.transactionId; bidResponse.placementCode = placementCode; bidResponse.cpm = responseCPM; bidResponse.size = bidSizes; @@ -456,8 +454,6 @@ function buildNativeResponse(bidRequest, response) { return; } bidResponse.requestId = bidRequest.bidId; - bidResponse.auctionId = bidRequest.auctionId; - bidResponse.transactionId = bidRequest.transactionId; bidResponse.placementCode = placementCode; bidResponse.cpm = responseCPM; @@ -509,8 +505,6 @@ function buildVideoResponse(bidRequest, response) { let context = bidRequest.mediaTypes.video.context; bidResponse.requestId = bidRequest.bidId; - bidResponse.auctionId = bidRequest.auctionId; - bidResponse.transactionId = bidRequest.transactionId; bidResponse.placementCode = placementCode; bidResponse.cpm = responseCPM; diff --git a/modules/deltaprojectsBidAdapter.js b/modules/deltaprojectsBidAdapter.js index e40ec58461c..c66e381b8f1 100644 --- a/modules/deltaprojectsBidAdapter.js +++ b/modules/deltaprojectsBidAdapter.js @@ -1,6 +1,15 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -import {_each, _map, createTrackPixelHtml, deepAccess, isFn, isNumber, logError, logWarn} from '../src/utils.js'; +import { + _each, + _map, + createTrackPixelHtml, + deepAccess, + isFn, + isNumber, + logError, + logWarn +} from '../src/utils.js'; import {config} from '../src/config.js'; export const BIDDER_CODE = 'deltaprojects'; @@ -27,7 +36,7 @@ function isBidRequestValid(bid) { function buildRequests(validBidRequests, bidderRequest) { /** == shared ==**/ // -- build id - const id = bidderRequest.auctionId; + const id = bidderRequest.bidderRequestId; // -- build site const publisherId = setOnAny(validBidRequests, 'params.publisherId'); @@ -90,7 +99,7 @@ function buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs // build source const source = { - tid: validBidRequest.transactionId, + tid: validBidRequest.auctionId, fd: 1, } diff --git a/modules/dianomiBidAdapter.js b/modules/dianomiBidAdapter.js index f8f47df1239..d4b2a4a5da5 100644 --- a/modules/dianomiBidAdapter.js +++ b/modules/dianomiBidAdapter.js @@ -112,7 +112,7 @@ export const spec = { setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; - const tid = bidderRequest.auctionId; + const tid = bidderRequest.ortb2?.source?.tid; const currency = getConfig('currency.adServerCurrency'); const cur = currency && [currency]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index a0f864d529f..675ede273a6 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -301,6 +301,7 @@ function getParam(validBidRequests, bidderRequest) { if (items && items.length) { let c = { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 id: 'pp_hbjs_' + auctionId, test: +isTest, at: 1, diff --git a/modules/displayioBidAdapter.js b/modules/displayioBidAdapter.js index d46cc8ee309..3d34f2c8b26 100644 --- a/modules/displayioBidAdapter.js +++ b/modules/displayioBidAdapter.js @@ -3,6 +3,7 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {getWindowFromDocument, logWarn} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const ADAPTER_VERSION = '1.1.0'; const BIDDER_CODE = 'displayio'; @@ -105,7 +106,7 @@ function getPayload (bid, bidderRequest) { renderURL, data: { pagecat: pageCategory ? pageCategory.split(',').map(k => k.trim()) : [], - keywords: keywords ? keywords.split(',').map(k => k.trim()) : [], + keywords: getAllOrtbKeywords(bidderRequest.ortb2, keywords), lang_content: document.documentElement.lang, lang: window.navigator.language, domain: refererInfo.domain, diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index 74a93ce086e..b8e812f581a 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -29,6 +29,7 @@ export const spec = { const referrer = bidderRequest.refererInfo.page; const bidId = bidRequest.bidId; const pbcode = bidRequest.adUnitCode || false; // div id + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 const auctionId = bidRequest.auctionId || false; const isDev = params.devMode || false; diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js index a7b1991df9b..a03a1ec12ca 100644 --- a/modules/ebdrBidAdapter.js +++ b/modules/ebdrBidAdapter.js @@ -36,8 +36,9 @@ export const spec = { w: whArr[0], h: whArr[1] }; - ebdrParams['latitude'] = getBidIdParameter('latitude', bid.params); - ebdrParams['longitude'] = getBidIdParameter('longitude', bid.params); + // TODO: fix lat and long to only come from request + ebdrParams['latitude'] = '0'; + ebdrParams['longitude'] = '0'; ebdrParams['ifa'] = (getBidIdParameter('IDFA', bid.params).length > getBidIdParameter('ADID', bid.params).length) ? getBidIdParameter('IDFA', bid.params) : getBidIdParameter('ADID', bid.params); }); let ebdrBidReq = { diff --git a/modules/eskimiBidAdapter.js b/modules/eskimiBidAdapter.js index 4a00b97614b..88d8f95b859 100644 --- a/modules/eskimiBidAdapter.js +++ b/modules/eskimiBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' const BIDDER_CODE = 'eskimi'; // const ENDPOINT = 'https://hb.eskimi.com/bids' @@ -12,43 +12,35 @@ const DEFAULT_CURRENCY = 'USD'; const DEFAULT_NET_REVENUE = true; const GVLID = 814; +const VIDEO_ORTB_PARAMS = [ + 'mimes', + 'minduration', + 'maxduration', + 'placement', + 'protocols', + 'startdelay', + 'skip', + 'skipafter', + 'minbitrate', + 'maxbitrate', + 'delivery', + 'playbackmethod', + 'api', + 'linearity', + 'battr' +]; + +const BANNER_ORTB_PARAMS = [ + 'battr' +] + export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [BANNER], - - isBidRequestValid: function (bid) { - return !!bid.params.placementId; - }, - - buildRequests(bidRequests, bidderRequest) { - const data = converter.toORTB({bidRequests, bidderRequest}) - - let bid = bidRequests.find((b) => b.params.placementId) - if (!data.site) data.site = {} - data.site.ext = {placementId: bid.params.placementId} - - if (bidderRequest.gdprConsent) { - if (!data.user) data.user = {}; - if (!data.user.ext) data.user.ext = {}; - if (!data.regs) data.regs = {}; - if (!data.regs.ext) data.regs.ext = {}; - data.user.ext.consent = bidderRequest.gdprConsent.consentString; - data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - - return [{ - method: 'POST', - url: ENDPOINT, - data, - options: {contentType: 'application/json;charset=UTF-8', withCredentials: false} - }] - }, - - interpretResponse(response, request) { - return converter.fromORTB({response: response.body, request: request.data}).bids; - }, - + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, /** * Register bidder specific code, which will execute if a bid from this bidder won the auction * @param {Bid} bid The bid that won the auction @@ -60,13 +52,151 @@ export const spec = { } } -const converter = ortbConverter({ +registerBidder(spec); + +const CONVERTER = ortbConverter({ context: { netRevenue: DEFAULT_NET_REVENUE, ttl: DEFAULT_BID_TTL, - currency: DEFAULT_CURRENCY, - mediaType: BANNER // TODO: support more types, we should set mtype on the winning bid + currency: DEFAULT_CURRENCY + }, + imp(buildImp, bidRequest, context) { + let imp = buildImp(bidRequest, context); + imp.secure = Number(window.location.protocol === 'https:'); + if (!imp.bidfloor && bidRequest.params.bidFloor) { + imp.bidfloor = bidRequest.params.bidFloor; + imp.bidfloorcur = utils.getBidIdParameter('bidFloorCur', bidRequest.params).toUpperCase() || 'USD' + } + + if (bidRequest.mediaTypes[VIDEO]) { + imp = buildVideoImp(bidRequest, imp); + } else if (bidRequest.mediaTypes[BANNER]) { + imp = buildBannerImp(bidRequest, imp); + } + + return imp; } }); -registerBidder(spec); +function isBidRequestValid(bidRequest) { + return (isPlacementIdValid(bidRequest) && (isValidBannerRequest(bidRequest) || isValidVideoRequest(bidRequest))); +} + +function isPlacementIdValid(bidRequest) { + return utils.isNumber(bidRequest.params.placementId); +} + +function isValidBannerRequest(bidRequest) { + const bannerSizes = utils.deepAccess(bidRequest, `mediaTypes.${BANNER}.sizes`); + return utils.isArray(bannerSizes) && bannerSizes.length > 0 && bannerSizes.every(size => utils.isNumber(size[0]) && utils.isNumber(size[1])); +} + +function isValidVideoRequest(bidRequest) { + const videoSizes = utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}.playerSize`); + + return utils.isArray(videoSizes) && videoSizes.length > 0 && videoSizes.every(size => utils.isNumber(size[0]) && utils.isNumber(size[1])); +} + +function buildRequests(validBids, bidderRequest) { + let videoBids = validBids.filter(bid => isVideoBid(bid)); + let bannerBids = validBids.filter(bid => isBannerBid(bid)); + let requests = []; + + bannerBids.forEach(bid => { + requests.push(createRequest([bid], bidderRequest, BANNER)); + }); + + videoBids.forEach(bid => { + requests.push(createRequest([bid], bidderRequest, VIDEO)); + }); + + return requests; +} + +function interpretResponse(response, request) { + return CONVERTER.fromORTB({ request: request.data, response: response.body }).bids; +} + +function buildVideoImp(bidRequest, imp) { + const videoAdUnitParams = utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`, {}); + const videoBidderParams = utils.deepAccess(bidRequest, `params.${VIDEO}`, {}); + + const videoParams = { ...videoAdUnitParams, ...videoBidderParams }; + + const videoSizes = (videoAdUnitParams && videoAdUnitParams.playerSize) || []; + + if (videoSizes && videoSizes.length > 0) { + utils.deepSetValue(imp, 'video.w', videoSizes[0][0]); + utils.deepSetValue(imp, 'video.h', videoSizes[0][1]); + } + + VIDEO_ORTB_PARAMS.forEach((param) => { + if (videoParams.hasOwnProperty(param)) { + utils.deepSetValue(imp, `video.${param}`, videoParams[param]); + } + }); + + if (imp.video && videoParams?.context === 'outstream') { + imp.video.placement = imp.video.placement || 4; + } + + return { ...imp }; +} + +function buildBannerImp(bidRequest, imp) { + const bannerAdUnitParams = utils.deepAccess(bidRequest, `mediaTypes.${BANNER}`, {}); + const bannerBidderParams = utils.deepAccess(bidRequest, `params.${BANNER}`, {}); + + const bannerParams = { ...bannerAdUnitParams, ...bannerBidderParams }; + + let sizes = bidRequest.mediaTypes.banner.sizes; + + if (sizes) { + utils.deepSetValue(imp, 'banner.w', sizes[0][0]); + utils.deepSetValue(imp, 'banner.h', sizes[0][1]); + } + + BANNER_ORTB_PARAMS.forEach((param) => { + if (bannerParams.hasOwnProperty(param)) { + utils.deepSetValue(imp, `banner.${param}`, bannerParams[param]); + } + }); + + return { ...imp }; +} + +function createRequest(bidRequests, bidderRequest, mediaType) { + const data = CONVERTER.toORTB({ bidRequests, bidderRequest, context: { mediaType } }) + + const bid = bidRequests.find((b) => b.params.placementId) + if (!data.site) data.site = {} + data.site.ext = { placementId: bid.params.placementId } + + if (bidderRequest.gdprConsent) { + if (!data.user) data.user = {}; + if (!data.user.ext) data.user.ext = {}; + if (!data.regs) data.regs = {}; + if (!data.regs.ext) data.regs.ext = {}; + data.user.ext.consent = bidderRequest.gdprConsent.consentString; + data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + + if (bid.params.bcat) data.bcat = bid.params.bcat; + if (bid.params.badv) data.badv = bid.params.badv; + if (bid.params.bapp) data.bapp = bid.params.bapp; + + return { + method: 'POST', + url: ENDPOINT, + data: data, + options: { contentType: 'application/json;charset=UTF-8', withCredentials: false } + } +} + +function isVideoBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.video'); +} + +function isBannerBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); +} diff --git a/modules/eskimiBidAdapter.md b/modules/eskimiBidAdapter.md index 83ae87fd01b..b3494a217eb 100644 --- a/modules/eskimiBidAdapter.md +++ b/modules/eskimiBidAdapter.md @@ -6,29 +6,48 @@ Maintainer: tech@eskimi.com # Description -An adapter to get a bid from Eskimi DSP. +Module that connects to Eskimi demand sources to fetch bids using OpenRTB standard. +Banner and video formats are supported. # Test Parameters ```javascript var adUnits = [{ - code: 'div-gpt-ad-1460505748561-0', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - - bids: [{ - bidder: 'eskimi', - params: { - placementId: 612 - } - }] - - }]; + code: '/19968336/prebid_banner_example_1', + mediaTypes: { + banner: { + sizes: [[ 300, 250 ]], + ... // battr + } + }, + bids: [{ + bidder: 'eskimi', + params: { + placementId: 612, + ... // bcat, badv, bapp + } + }] + }, { + code: '/19968336/prebid_video_example_1', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'], + api: [1, 2, 4, 6], + ... // Aditional ORTB video params (including battr) + } + }, + bids: [{ + bidder: 'eskimi', + params: { + placementId: 612, + ... // bcat, badv, bapp + } + }] + }]; ``` Where: * placementId - Placement ID of the ad unit (required) +* bcat, badv, bapp, battr - ORTB blocking parameters as specified by OpenRTB 2.5 diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index b290a62420c..cced180e061 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -35,7 +35,7 @@ export const spec = { lastCountry = countryMap[bid.params.country]; } reqParams = bid.params; - reqParams.transactionId = bid.transactionId; + reqParams.transactionId = bid.ortb2Imp?.ext?.tid; request.push(formRequestUrl(reqParams)); floors[i] = getBidFloor(bid); } @@ -137,7 +137,6 @@ export const spec = { vastXml: data.vast_content, vastUrl: data.vast_link, mediaType: data.response, - transactionId: bid.transactionId }; if (bidRequest.gdpr) { bidObject.gdpr = bidRequest.gdpr.gdpr; diff --git a/modules/euidIdSystem.js b/modules/euidIdSystem.js new file mode 100644 index 00000000000..c4e15b11d15 --- /dev/null +++ b/modules/euidIdSystem.js @@ -0,0 +1,121 @@ +/** + * This module adds EUID ID support to the User ID module. It shares significant functionality with the UID2 module. + * The {@link module:modules/userId} module is required. + * @module modules/euidIdSystem + * @requires module:modules/userId + */ + +import { logInfo, logWarn, deepAccess } from '../src/utils.js'; +import {submodule} from '../src/hook.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {MODULE_TYPE_UID} from '../src/activities/modules.js'; + +// RE below lint exception: UID2 and EUID are separate modules, but the protocol is the same and shared code makes sense here. +// eslint-disable-next-line prebid/validate-imports +import { Uid2GetId, Uid2CodeVersion } from './uid2IdSystem_shared.js'; + +const MODULE_NAME = 'euid'; +const MODULE_REVISION = Uid2CodeVersion; +const PREBID_VERSION = '$prebid.version$'; +const EUID_CLIENT_ID = `PrebidJS-${PREBID_VERSION}-EUIDModule-${MODULE_REVISION}`; +const GVLID_TTD = 21; // The Trade Desk +const LOG_PRE_FIX = 'EUID: '; +const ADVERTISING_COOKIE = '__euid_advertising_token'; + +// eslint-disable-next-line no-unused-vars +const EUID_TEST_URL = 'https://integ.euid.eu'; +const EUID_PROD_URL = 'https://prod.euid.eu'; +const EUID_BASE_URL = EUID_PROD_URL; + +function createLogger(logger, prefix) { + return function (...strings) { + logger(prefix + ' ', ...strings); + } +} +const _logInfo = createLogger(logInfo, LOG_PRE_FIX); +const _logWarn = createLogger(logWarn, LOG_PRE_FIX); + +export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); + +function hasWriteToDeviceConsent(consentData) { + const gdprApplies = consentData?.gdprApplies === true; + const localStorageConsent = deepAccess(consentData, `vendorData.purpose.consents.1`) + const prebidVendorConsent = deepAccess(consentData, `vendorData.vendor.consents.${GVLID_TTD.toString()}`) + if (gdprApplies && (!localStorageConsent || !prebidVendorConsent)) { + return false; + } + return true; +} + +/** @type {Submodule} */ +export const euidIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + + /** + * Vendor id of The Trade Desk + * @type {Number} + */ + gvlid: GVLID_TTD, + /** + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{euid:{ id: string } }} or undefined if value doesn't exists + */ + decode(value) { + const result = decodeImpl(value); + _logInfo('EUID decode returned', result); + return result; + }, + + /** + * performs action to obtain id and return a value. + * @function + * @param {SubmoduleConfig} [configparams] + * @param {ConsentData|undefined} consentData + * @returns {euidId} + */ + getId(config, consentData) { + if (consentData?.gdprApplies !== true) { + logWarn('EUID is intended for use within the EU. The module will not run when GDPR does not apply.'); + return; + } + if (!hasWriteToDeviceConsent(consentData)) { + // The module cannot operate without this permission. + _logWarn(`Unable to use EUID module due to insufficient consent. The EUID module requires storage permission.`) + return; + } + + const mappedConfig = { + apiBaseUrl: config?.params?.euidApiBase ?? EUID_BASE_URL, + paramToken: config?.params?.euidToken, + serverCookieName: config?.params?.euidCookie, + storage: config?.params?.storage ?? 'localStorage', + clientId: EUID_CLIENT_ID, + internalStorage: ADVERTISING_COOKIE + }; + + const result = Uid2GetId(mappedConfig, storage, _logInfo, _logWarn); + _logInfo(`EUID getId returned`, result); + return result; + }, +}; + +function decodeImpl(value) { + if (typeof value === 'string') { + _logInfo('Found server-only token. Refresh is unavailable for this token.'); + const result = { euid: { id: value } }; + return result; + } + if (Date.now() < value.latestToken.identity_expires) { + return { euid: { id: value.latestToken.advertising_token } }; + } + return null; +} + +// Register submodule for userId +submodule('userId', euidIdSubmodule); diff --git a/modules/euidIdSystem.md b/modules/euidIdSystem.md new file mode 100644 index 00000000000..e3e16bce89d --- /dev/null +++ b/modules/euidIdSystem.md @@ -0,0 +1,131 @@ +## EUID User ID Submodule + +EUID requires initial tokens to be generated server-side. The EUID module handles storing, providing, and optionally refreshing them. The module can operate in one of two different modes: *Client Refresh* mode or *Server Only* mode. + +*Server Only* mode was originally referred to as *legacy mode*, but it is a popular mode for new integrations where publishers prefer to handle token refresh server-side. + +## Client Refresh mode + +This is the recommended mode for most scenarios. In this mode, the full response body from the EUID Token Generate or Token Refresh endpoint must be provided to the module. As long as the refresh token remains valid, the module will refresh the advertising token as needed. + +To configure the module to use this mode, you must **either**: +1. Set `params.euidCookie` to the name of the cookie which contains the response body as a JSON string, **or** +2. Set `params.euidToken` to the response body as a JavaScript object. + +### Client refresh cookie example + +In this example, the cookie is called `euid_pub_cookie`. + +Cookie: +``` +euid_pub_cookie={"advertising_token":"...advertising token...","refresh_token":"...refresh token...","identity_expires":1684741472161,"refresh_from":1684741425653,"refresh_expires":1684784643668,"refresh_response_key":"...response key..."} +``` + +Configuration: +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'euid', + params: { + euidCookie: 'euid_pub_cookie' + } + }] + } +}); +``` + +### Client refresh euidToken example + +Configuration: +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'euid', + params: { + euidToken: { + 'advertising_token': '...advertising token...', + 'refresh_token': '...refresh token...', + // etc. - see the Sample Token below for contents of this object + } + } + }] + } +}); +``` + +## Server-Only Mode + +In this mode, only the advertising token is provided to the module. The module will not be able to refresh the token. The publisher is responsible for implementing some other way to refresh the token. + +To configure the module to use this mode, you must **either**: +1. Set a cookie named `__euid_advertising_token` to the advertising token, **or** +2. Set `value` to an ID block containing the advertising token. + +### Server only cookie example + +Cookie: +``` +__euid_advertising_token=...advertising token... +``` + +Configuration: +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'euid' + }] + } +}); +``` + +### Server only value example + +Configuration: +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'euid' + value: { + 'euid': { + 'id': '...advertising token...' + } + } + }] + } +}); +``` + +## Storage + +The module stores a number of internal values. By default, all values are stored in HTML5 local storage. You can switch to cookie storage by setting `params.storage` to `cookie`. The cookie size can be significant and this is not recommended, but is provided as an option if local storage is not an option. + +## Sample token + +`{`
  `"advertising_token": "...",`
  `"refresh_token": "...",`
  `"identity_expires": 1633643601000,`
  `"refresh_from": 1633643001000,`
  `"refresh_expires": 1636322000000,`
  `"refresh_response_key": "wR5t6HKMfJ2r4J7fEGX9Gw=="`
`}` + +### Notes + +If you are trying to limit the size of cookies, provide the token in configuration and use the default option of local storage. + +If you provide an expired identity and the module has a valid identity which was refreshed from the identity you provide, it will use the refreshed identity. The module stores the original token used for refreshing the token, and it will use the refreshed tokens as long as the original token matches the one supplied. + +If a new token is supplied which does not match the original token used to generate any refreshed tokens, all stored tokens will be discarded and the new token used instead (refreshed if necessary). + +You can set `params.euidApiBase` to `"https://integ.euid.eu"` during integration testing. Be aware that you must use the same environment (production or integration) here as you use for generating tokens. + +## Parameter Descriptions for the `usersync` Configuration Section + +The below parameters apply only to the EUID User ID Module integration. + +| Param under userSync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | ID value for the EUID module - `"euid"` | `"euid"` | +| value | Optional, Server only | Object | An object containing the value for the advertising token. | See the example above. | +| params.euidToken | Optional, Client refresh | Object | The initial EUID token. This should be `body` element of the decrypted response from a call to the `/token/generate` or `/token/refresh` endpoint. | See the sample token above. | +| params.euidCookie | Optional, Client refresh | String | The name of a cookie which holds the initial EUID token, set by the server. The cookie should contain JSON in the same format as the euidToken param. **If euidToken is supplied, this param is ignored.** | See the sample token above. | +| params.euidApiBase | Optional, Client refresh | String | Overrides the default EUID API endpoint. | `"https://prod.euid.eu"` _(default)_| +| params.storage | Optional, Client refresh | String | Specify whether to use `cookie` or `localStorage` for module-internal storage. It is recommended to not provide this and allow the module to use the default. | `localStorage` _(default)_ | diff --git a/modules/feedadBidAdapter.js b/modules/feedadBidAdapter.js index 34e14cd674a..7b41f0fcc03 100644 --- a/modules/feedadBidAdapter.js +++ b/modules/feedadBidAdapter.js @@ -234,7 +234,7 @@ function buildRequests(validBidRequests, bidderRequest) { }); data.bids.forEach(bid => BID_METADATA[bid.bidId] = { referer: data.refererInfo.page, - transactionId: bid.transactionId + transactionId: bid.ortb2Imp?.ext?.tid, }); if (bidderRequest.gdprConsent) { data.consentIabTcf = bidderRequest.gdprConsent.consentString; diff --git a/modules/finativeBidAdapter.js b/modules/finativeBidAdapter.js index b236ddb3d95..87580a209bb 100644 --- a/modules/finativeBidAdapter.js +++ b/modules/finativeBidAdapter.js @@ -1,11 +1,11 @@ // jshint esversion: 6, es3: false, node: true 'use strict'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { NATIVE } from '../src/mediaTypes.js'; -import { _map, deepSetValue, isEmpty, deepAccess } from '../src/utils.js'; -import { config } from '../src/config.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {NATIVE} from '../src/mediaTypes.js'; +import {_map, deepAccess, deepSetValue, isEmpty} from '../src/utils.js'; +import {config} from '../src/config.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; const BIDDER_CODE = 'finative'; const DEFAULT_CUR = 'EUR'; @@ -63,7 +63,7 @@ export const spec = { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; - const tid = bidderRequest.auctionId; + const tid = bidderRequest.ortb2?.source?.tid; const cur = [config.getConfig('currency.adServerCurrency') || DEFAULT_CUR]; let url = bidderRequest.refererInfo.referer; @@ -104,6 +104,7 @@ export const spec = { return { id: String(id + 1), tagid: bid.params.adUnitId, + // TODO: `tid` is not under `imp` in ORTB, is this intentional? tid: tid, pt: pt, native: { @@ -115,7 +116,7 @@ export const spec = { }); const request = { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, site: { page: url }, @@ -174,7 +175,6 @@ export const spec = { netRevenue: (!bid.netRevenue || bid.netRevenue === 'net'), currency: cur, mediaType: NATIVE, - bidderCode: BIDDER_CODE, native: parseNative(bidResponse), meta: { advertiserDomains: bidResponse.adomain && bidResponse.adomain.length > 0 ? bidResponse.adomain : [] diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index 97b9847eae4..edb750a6b90 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -48,7 +48,7 @@ export const spec = { data.page = page; data.adUnitCode = request.adUnitCode; data.bidId = request.bidId; - data.transactionId = request.transactionId; + data.transactionId = request.ortb2Imp?.ext?.tid; data.user = { eids: (request.userIdAsEids || []).filter((eid) => SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) !== -1) }; @@ -125,7 +125,6 @@ export const spec = { `(function() { var img = new Image(); img.src = "${beaconUrl}"})()` + ``; let data = { - bidderCode: BIDDER_CODE, requestId: res.id, currency: res.cur, cpm: parseFloat(bid.price) || 0, diff --git a/modules/freepassIdSystem.js b/modules/freepassIdSystem.js new file mode 100644 index 00000000000..d52c537e800 --- /dev/null +++ b/modules/freepassIdSystem.js @@ -0,0 +1,61 @@ +import { submodule } from '../src/hook.js'; +import {generateUUID, logMessage} from '../src/utils.js'; + +const MODULE_NAME = 'freepassId'; + +export const freepassIdSubmodule = { + name: MODULE_NAME, + decode: function (value, config) { + logMessage('Decoding FreePass ID: ', value); + + return { [MODULE_NAME]: value }; + }, + + getId: function (config, consent, cachedIdObject) { + logMessage('Getting FreePass ID using config: ' + JSON.stringify(config)); + + const freepassData = config.params !== undefined ? (config.params.freepassData || {}) : {} + let idObject = {userId: generateUUID()}; + + if (freepassData.commonId !== undefined) { + idObject.commonId = config.params.freepassData.commonId; + } + + if (freepassData.userIp !== undefined) { + idObject.userIp = config.params.freepassData.userIp; + } + + return {id: idObject}; + }, + + extendId: function (config, consent, cachedIdObject) { + let freepassData = config.params.freepassData; + let hasFreepassData = freepassData !== undefined; + if (!hasFreepassData) { + logMessage('No Freepass Data. CachedIdObject will not be extended: ' + JSON.stringify(cachedIdObject)); + return { + id: cachedIdObject + }; + } + + if (freepassData.commonId === cachedIdObject.commonId && freepassData.userIp === cachedIdObject.userIp) { + logMessage('FreePass ID is already up-to-date: ' + JSON.stringify(cachedIdObject)); + return { + id: cachedIdObject + }; + } + + logMessage('Extending FreePass ID object: ' + JSON.stringify(cachedIdObject)); + logMessage('Extending FreePass ID using config: ' + JSON.stringify(config)); + + return { + id: { + commonId: freepassData.commonId, + userIp: freepassData.userIp, + userId: cachedIdObject.userId, + }, + }; + } +}; + +submodule('userId', freepassIdSubmodule); diff --git a/modules/freepassIdSystem.md b/modules/freepassIdSystem.md new file mode 100644 index 00000000000..de0cdf23f68 --- /dev/null +++ b/modules/freepassIdSystem.md @@ -0,0 +1,47 @@ +## FreePass User ID Submodule + +[FreePass](https://freepass-login.com/introduction.html) is a common authentication service operated by Freebit Co., Ltd. Users with a FreePass account do not need to create a new account to use partner services. + +# General Information + +Please contact FreePass before using this ID. + +``` +Module Name: FreePass Id System +Module Type: User Id System +Maintainer: freepass-headerbidding@craid-inc.com +``` + +## Building Prebid with FreePass ID Support + +First, make sure to add the FreePass ID submodule to your Prebid.js package with: + +```shell +gulp build --modules=freepassIdSystem,userId +``` + +The following configuration parameters are available: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'freepassId', + params: { + freepassData: { + commonId: 'fpcommonid123', + userIp: '127.0.0.1' + } + } + }] + } +}); +``` + +| Param under userSync.userIds[] | Scope | Type | Description | Example | +|--------------------------------|----------|--------|------------------------------------------------------|----------------| +| name | Required | String | The name of this module | `"freepassId"` | +| freepassData | Optional | Object | FreePass data | `{}` | +| freepassData.commonId | Optional | String | Common ID obtained from FreePass | `"abcd1234"` | +| freepassData.userIp | Optional | String | User IP obtained in cooperation with partner service | `"127.0.0.1"` | + diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index ae320db1251..1c279cdb9b8 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -80,11 +80,11 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map(bidRequest => { - const {adUnitCode, auctionId, mediaTypes, params, sizes, transactionId} = bidRequest; + const {adUnitCode, mediaTypes, params, sizes, bidId} = bidRequest; const baseEndpoint = params['rtbEndpoint'] || ENDPOINTS['gamoshi']; const rtbEndpoint = `${baseEndpoint}/r/${params.supplyPartnerId}/bidr?rformat=open_rtb&reqformat=rtb_json&bidder=prebid` + (params.query ? '&' + params.query : ''); const rtbBidRequest = { - id: auctionId, + id: bidderRequest.bidderRequestId, site: { domain: bidderRequest.refererInfo.domain, page: bidderRequest.refererInfo.page, @@ -118,7 +118,7 @@ export const spec = { } const imp = { - id: transactionId, + id: bidId, instl: deepAccess(bidderRequest.ortb2Imp, 'instl') === 1 || params.instl === 1 ? 1 : 0, tagid: adUnitCode, bidfloor: helper.getBidFloor(bidRequest) || 0, diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 798dfc848da..4bac4b0cf74 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -2,30 +2,41 @@ * This module gives publishers extra set of features to enforce individual purposes of TCF v2 */ -import {deepAccess, hasDeviceAccess, isArray, logError, logWarn} from '../src/utils.js'; +import {deepAccess, logError, logWarn} from '../src/utils.js'; import {config} from '../src/config.js'; import adapterManager, {gdprDataHandler} from '../src/adapterManager.js'; -import {find, includes} from '../src/polyfill.js'; -import {registerSyncInner} from '../src/adapters/bidderFactory.js'; -import {getHook} from '../src/hook.js'; -import {validateStorageEnforcement} from '../src/storageManager.js'; +import {find} from '../src/polyfill.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../src/consentHandler.js'; import { MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, - MODULE_TYPE_CORE, MODULE_TYPE_RTD, + MODULE_TYPE_PREBID, + MODULE_TYPE_RTD, MODULE_TYPE_UID } from '../src/activities/modules.js'; +import { + ACTIVITY_PARAM_ANL_CONFIG, + ACTIVITY_PARAM_COMPONENT_NAME, + ACTIVITY_PARAM_COMPONENT_TYPE +} from '../src/activities/params.js'; +import {registerActivityControl} from '../src/activities/rules.js'; +import { + ACTIVITY_ACCESS_DEVICE, + ACTIVITY_ENRICH_EIDS, + ACTIVITY_FETCH_BIDS, + ACTIVITY_REPORT_ANALYTICS, + ACTIVITY_SYNC_USER +} from '../src/activities/activities.js'; export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; const TCF2 = { - 'purpose1': { id: 1, name: 'storage' }, - 'purpose2': { id: 2, name: 'basicAds' }, - 'purpose7': { id: 7, name: 'measurement' } -} + 'purpose1': {id: 1, name: 'storage'}, + 'purpose2': {id: 2, name: 'basicAds'}, + 'purpose7': {id: 7, name: 'measurement'} +}; /* These rules would be used if `consentManagement.gdpr.rules` is undefined by the publisher. @@ -48,9 +59,9 @@ export let purpose7Rule; export let enforcementRules; -const storageBlocked = []; -const biddersBlocked = []; -const analyticsBlocked = []; +const storageBlocked = new Set(); +const biddersBlocked = new Set(); +const analyticsBlocked = new Set(); let hooksAdded = false; let strictStorageEnforcement = false; @@ -62,6 +73,9 @@ const GVLID_LOOKUP_PRIORITY = [ MODULE_TYPE_RTD ]; +const RULE_NAME = 'TCF2'; +const RULE_HANDLES = []; + /** * Retrieve a module's GVL ID. */ @@ -73,7 +87,7 @@ export function getGvlid(moduleType, moduleName, fallbackFn) { // Return GVL ID from user defined gvlMapping if (gvlMapping && gvlMapping[moduleName]) { return gvlMapping[moduleName]; - } else if (moduleType === MODULE_TYPE_CORE) { + } else if (moduleType === MODULE_TYPE_PREBID) { return VENDORLESS_GVLID; } else { let {gvlid, modules} = GDPR_GVLIDS.get(moduleName); @@ -83,8 +97,8 @@ export function getGvlid(moduleType, moduleName, fallbackFn) { for (const type of GVLID_LOOKUP_PRIORITY) { if (modules.hasOwnProperty(type)) { gvlid = modules[type]; - if (type !== moduleType && !fallbackFn) { - logWarn(`Multiple GVL IDs found for module '${moduleName}'; using the ${type} module's ID (${gvlid}) instead of the ${moduleType}'s ID (${modules[moduleType]})`) + if (type !== moduleType) { + logWarn(`Multiple GVL IDs found for module '${moduleName}'; using the ${type} module's ID (${gvlid}) instead of the ${moduleType}'s ID (${modules[moduleType]})`); } break; } @@ -109,9 +123,9 @@ export function getGvlidFromAnalyticsAdapter(code, config) { try { return gvlid.call(adapter.adapter, config); } catch (e) { - logError(`Error invoking ${code} adapter.gvlid()`, e) + logError(`Error invoking ${code} adapter.gvlid()`, e); } - })(adapter?.adapter?.gvlid) + })(adapter?.adapter?.gvlid); } export function shouldEnforce(consentData, purpose, name) { @@ -120,7 +134,7 @@ export function shouldEnforce(consentData, purpose, name) { // NOTE: this check is not foolproof, as when Prebid first loads, enforcement hooks have not been attached yet // This piece of code would not run at all, and `gdprDataHandler.enabled` would be false, until the first // `setConfig({consentManagement})` - logWarn(`Attempting operation that requires purpose ${purpose} consent while consent data is not available${name ? ` (module: ${name})` : ''}. Assuming no consent was given.`) + logWarn(`Attempting operation that requires purpose ${purpose} consent while consent data is not available${name ? ` (module: ${name})` : ''}. Assuming no consent was given.`); return true; } return consentData && consentData.gdprApplies; @@ -142,7 +156,7 @@ export function validateRules(rule, consentData, currentModule, gvlId) { if ((rule.vendorExceptions || []).includes(currentModule)) { return true; } - const vendorConsentRequred = !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule))) + const vendorConsentRequred = !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule))); // get data from the consent string const purposeConsent = deepAccess(consentData, `vendorData.purpose.consents.${purposeId}`); @@ -169,170 +183,71 @@ export function validateRules(rule, consentData, currentModule, gvlId) { } /** - * This hook checks whether module has permission to access device or not. Device access include cookie and local storage + * all activity rules follow the same structure: + * if GDPR is in scope, check configuration for a particular purpose, and if that enables enforcement, + * check against consent data for that purpose and vendor * - * @param {Function} fn reference to original function (used by hook logic) - * @param {string} moduleType type of the module - * @param {string=} moduleName name of the module - * @param result - * @param validate + * @param purposeNo TCF purpose number to check for this activity + * @param getEnforcementRule getter for gdprEnforcement rule definition to use + * @param blocked optional set to use for collecting denied vendors + * @param gvlidFallback optional factory function for a gvlid falllback function */ -export function deviceAccessHook(fn, moduleType, moduleName, result, {validate = validateRules} = {}) { - result = Object.assign({}, { - hasEnforcementHook: true - }); - if (!hasDeviceAccess()) { - logWarn('Device access is disabled by Publisher'); - result.valid = false; - } else if (moduleType === MODULE_TYPE_CORE && !strictStorageEnforcement) { - // for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set - result.valid = true; - } else { +function gdprRule(purposeNo, getEnforcementRule, blocked = null, gvlidFallback = () => null) { + return function (params) { const consentData = gdprDataHandler.getConsentData(); - let gvlid; - if (shouldEnforce(consentData, 1, moduleName)) { - const curBidder = config.getCurrentBidder(); - // Bidders have a copy of storage object with bidder code binded. Aliases will also pass the same bidder code when invoking storage functions and hence if alias tries to access device we will try to grab the gvl id for alias instead of original bidder - if (curBidder && (curBidder !== moduleName) && adapterManager.aliasRegistry[curBidder] === moduleName) { - gvlid = getGvlid(moduleType, curBidder); - } else { - gvlid = getGvlid(moduleType, moduleName) + const modName = params[ACTIVITY_PARAM_COMPONENT_NAME]; + if (shouldEnforce(consentData, purposeNo, modName)) { + const gvlid = getGvlid(params[ACTIVITY_PARAM_COMPONENT_TYPE], modName, gvlidFallback(params)); + let allow = !!validateRules(getEnforcementRule(), consentData, modName, gvlid); + if (!allow) { + blocked && blocked.add(modName); + return {allow}; } - const curModule = moduleName || curBidder; - let isAllowed = validate(purpose1Rule, consentData, curModule, gvlid,); - if (isAllowed) { - result.valid = true; - } else { - curModule && logWarn(`TCF2 denied device access for ${curModule}`); - result.valid = false; - storageBlocked.push(curModule); - } - } else { - result.valid = true; } - } - fn.call(this, moduleType, moduleName, result); + }; } -/** - * This hook checks if a bidder has consent for user sync or not - * @param {Function} fn reference to original function (used by hook logic) - * @param {...any} args args - */ -export function userSyncHook(fn, ...args) { - const consentData = gdprDataHandler.getConsentData(); - const curBidder = config.getCurrentBidder(); - if (shouldEnforce(consentData, 1, curBidder)) { - const gvlid = getGvlid(MODULE_TYPE_BIDDER, curBidder); - let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid); - if (isAllowed) { - fn.call(this, ...args); - } else { - logWarn(`User sync not allowed for ${curBidder}`); - storageBlocked.push(curBidder); +export const accessDeviceRule = ((rule) => { + return function (params) { + // for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set + if (params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_PREBID && !strictStorageEnforcement) return; + return rule(params); + }; +})(gdprRule(1, () => purpose1Rule, storageBlocked)); + +export const syncUserRule = gdprRule(1, () => purpose1Rule, storageBlocked); +export const enrichEidsRule = gdprRule(1, () => purpose1Rule, storageBlocked); + +export const fetchBidsRule = ((rule) => { + return function (params) { + if (params[ACTIVITY_PARAM_COMPONENT_TYPE] !== MODULE_TYPE_BIDDER) { + // TODO: this special case is for the PBS adapter (componentType is 'prebid') + // we should check for generic purpose 2 consent & vendor consent based on the PBS vendor's GVL ID; + // that is, however, a breaking change and skipped for now + return; } - } else { - fn.call(this, ...args); - } -} - -/** - * This hook checks if user id module is given consent or not - * @param {Function} fn reference to original function (used by hook logic) - * @param {Submodule[]} submodules Array of user id submodules - * @param {Object} consentData GDPR consent data - */ -export function userIdHook(fn, submodules, consentData) { - if (shouldEnforce(consentData, 1, 'User ID')) { - let userIdModules = submodules.map((submodule) => { - const moduleName = submodule.submodule.name; - const gvlid = getGvlid(MODULE_TYPE_UID, moduleName); - let isAllowed = validateRules(purpose1Rule, consentData, moduleName, gvlid); - if (isAllowed) { - return submodule; - } else { - logWarn(`User denied permission to fetch user id for ${moduleName} User id module`); - storageBlocked.push(moduleName); - } - return undefined; - }).filter(module => module) - fn.call(this, userIdModules, { ...consentData, hasValidated: true }); - } else { - fn.call(this, submodules, consentData); - } -} - -/** - * Checks if bidders are allowed in the auction. - * Enforces "purpose 2 (Basic Ads)" of TCF v2.0 spec - * @param {Function} fn - Function reference to the original function. - * @param {Array} adUnits - */ -export function makeBidRequestsHook(fn, adUnits, ...args) { - const consentData = gdprDataHandler.getConsentData(); - if (shouldEnforce(consentData, 2)) { - adUnits.forEach(adUnit => { - adUnit.bids = adUnit.bids.filter(bid => { - const currBidder = bid.bidder; - const gvlId = getGvlid(MODULE_TYPE_BIDDER, currBidder); - if (includes(biddersBlocked, currBidder)) return false; - const isAllowed = !!validateRules(purpose2Rule, consentData, currBidder, gvlId); - if (!isAllowed) { - logWarn(`TCF2 blocked auction for ${currBidder}`); - biddersBlocked.push(currBidder); - } - return isAllowed; - }); - }); - fn.call(this, adUnits, ...args); - } else { - fn.call(this, adUnits, ...args); - } -} + return rule(params); + }; +})(gdprRule(2, () => purpose2Rule, biddersBlocked)); -/** - * Checks if Analytics adapters are allowed to send data to their servers for furhter processing. - * Enforces "purpose 7 (Measurement)" of TCF v2.0 spec - * @param {Function} fn - Function reference to the original function. - * @param {Array} config - Configuration object passed to pbjs.enableAnalytics() - */ -export function enableAnalyticsHook(fn, config) { - const consentData = gdprDataHandler.getConsentData(); - if (shouldEnforce(consentData, 7, 'Analytics')) { - if (!isArray(config)) { - config = [config] - } - config = config.filter(conf => { - const analyticsAdapterCode = conf.provider; - const gvlid = getGvlid(MODULE_TYPE_ANALYTICS, analyticsAdapterCode, () => getGvlidFromAnalyticsAdapter(analyticsAdapterCode, conf)); - const isAllowed = !!validateRules(purpose7Rule, consentData, analyticsAdapterCode, gvlid); - if (!isAllowed) { - analyticsBlocked.push(analyticsAdapterCode); - logWarn(`TCF2 blocked analytics adapter ${conf.provider}`); - } - return isAllowed; - }); - fn.call(this, config); - } else { - fn.call(this, config); - } -} +export const reportAnalyticsRule = gdprRule(7, () => purpose7Rule, analyticsBlocked, (params) => getGvlidFromAnalyticsAdapter(params[ACTIVITY_PARAM_COMPONENT_NAME], params[ACTIVITY_PARAM_ANL_CONFIG])); /** * Compiles the TCF2.0 enforcement results into an object, which is emitted as an event payload to "tcf2Enforcement" event. */ function emitTCF2FinalResults() { // remove null and duplicate values - const formatArray = function (arr) { - return arr.filter((i, k) => i !== null && arr.indexOf(i) === k); - } + const formatSet = function (st) { + return Array.from(st.keys()).filter(el => el != null); + }; const tcf2FinalResults = { - storageBlocked: formatArray(storageBlocked), - biddersBlocked: formatArray(biddersBlocked), - analyticsBlocked: formatArray(analyticsBlocked) + storageBlocked: formatSet(storageBlocked), + biddersBlocked: formatSet(biddersBlocked), + analyticsBlocked: formatSet(analyticsBlocked) }; events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults); + [storageBlocked, biddersBlocked, analyticsBlocked].forEach(el => el.clear()); } events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults); @@ -340,9 +255,15 @@ events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults); /* Set of callback functions used to detect presence of a TCF rule, passed as the second argument to find(). */ -const hasPurpose1 = (rule) => { return rule.purpose === TCF2.purpose1.name } -const hasPurpose2 = (rule) => { return rule.purpose === TCF2.purpose2.name } -const hasPurpose7 = (rule) => { return rule.purpose === TCF2.purpose7.name } +const hasPurpose1 = (rule) => { + return rule.purpose === TCF2.purpose1.name; +}; +const hasPurpose2 = (rule) => { + return rule.purpose === TCF2.purpose2.name; +}; +const hasPurpose7 = (rule) => { + return rule.purpose === TCF2.purpose7.name; +}; /** * A configuration function that initializes some module variables, as well as adds hooks @@ -373,28 +294,21 @@ export function setEnforcementConfig(config) { if (!hooksAdded) { if (purpose1Rule) { hooksAdded = true; - validateStorageEnforcement.before(deviceAccessHook, 49); - registerSyncInner.before(userSyncHook, 48); - // Using getHook as user id and gdprEnforcement are both optional modules. Using import will auto include the file in build - getHook('validateGdprEnforcement').before(userIdHook, 47); + RULE_HANDLES.push(registerActivityControl(ACTIVITY_ACCESS_DEVICE, RULE_NAME, accessDeviceRule)); + RULE_HANDLES.push(registerActivityControl(ACTIVITY_SYNC_USER, RULE_NAME, syncUserRule)); + RULE_HANDLES.push(registerActivityControl(ACTIVITY_ENRICH_EIDS, RULE_NAME, enrichEidsRule)); } if (purpose2Rule) { - getHook('makeBidRequests').before(makeBidRequestsHook); + RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule)); } if (purpose7Rule) { - getHook('enableAnalyticsCb').before(enableAnalyticsHook); + RULE_HANDLES.push(registerActivityControl(ACTIVITY_REPORT_ANALYTICS, RULE_NAME, reportAnalyticsRule)); } } } export function uninstall() { - [ - validateStorageEnforcement.getHooks({hook: deviceAccessHook}), - registerSyncInner.getHooks({hook: userSyncHook}), - getHook('validateGdprEnforcement').getHooks({hook: userIdHook}), - getHook('makeBidRequests').getHooks({hook: makeBidRequestsHook}), - getHook('enableAnalyticsCb').getHooks({hook: enableAnalyticsHook}), - ].forEach(hook => hook.remove()); + while (RULE_HANDLES.length) RULE_HANDLES.pop()(); hooksAdded = false; } diff --git a/modules/genericAnalyticsAdapter.js b/modules/genericAnalyticsAdapter.js index 4909745e72b..b52cb7e5464 100644 --- a/modules/genericAnalyticsAdapter.js +++ b/modules/genericAnalyticsAdapter.js @@ -115,11 +115,6 @@ export function GenericAnalytics() { } }, track(event) { - if (event.eventType === CONSTANTS.EVENTS.AUCTION_INIT && event.args.hasOwnProperty('config')) { - // clean up auctionInit event - // TODO: remove this special case in v8 - delete event.args.config; - } const datum = translate(event); if (datum != null) { batch.push(datum); diff --git a/modules/glimpseBidAdapter.js b/modules/glimpseBidAdapter.js deleted file mode 100644 index 53dc0bd3e1a..00000000000 --- a/modules/glimpseBidAdapter.js +++ /dev/null @@ -1,198 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { - isArray, - isEmpty, - isEmptyStr, - isStr, - isPlainObject, -} from '../src/utils.js'; - -const GVLID = 1012; -const BIDDER_CODE = 'glimpse'; -const storageManager = getStorageManager({bidderCode: BIDDER_CODE}); -const ENDPOINT = 'https://market.glimpsevault.io/public/v1/prebid'; -const LOCAL_STORAGE_KEY = { - vault: { - jwt: 'gp_vault_jwt', - }, -}; - -export const spec = { - gvlid: GVLID, - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - - /** - * Determines if the bid request is valid - * @param bid {BidRequest} The bid to validate - * @return {boolean} - */ - isBidRequestValid: (bid) => { - const pid = bid?.params?.pid; - return isStr(pid) && !isEmptyStr(pid); - }, - - /** - * Builds the http request - * @param validBidRequests {BidRequest[]} - * @param bidderRequest {BidderRequest} - * @returns {ServerRequest} - */ - buildRequests: (validBidRequests, bidderRequest) => { - const url = buildQuery(bidderRequest); - const auth = getVaultJwt(); - const referer = getReferer(bidderRequest); - const imp = validBidRequests.map(processBidRequest); - const fpd = getFirstPartyData(bidderRequest.ortb2); - - const data = { - auth, - data: { - referer, - imp, - fpd, - }, - }; - - return { - method: 'POST', - url, - data: JSON.stringify(data), - options: {}, - }; - }, - - /** - * Parse http response - * @param response {ServerResponse} - * @returns {Bid[]} - */ - interpretResponse: (response) => { - if (isValidResponse(response)) { - const { auth, data } = response.body; - setVaultJwt(auth); - const bids = data.bids.map(processBidResponse); - return bids; - } - return []; - }, -}; - -function setVaultJwt(auth) { - storageManager.setDataInLocalStorage(LOCAL_STORAGE_KEY.vault.jwt, auth); -} - -function getVaultJwt() { - return ( - storageManager.getDataFromLocalStorage(LOCAL_STORAGE_KEY.vault.jwt) || '' - ); -} - -function getReferer(bidderRequest) { - // TODO: is 'page' the right value here? - return bidderRequest?.refererInfo?.page || ''; -} - -function buildQuery(bidderRequest) { - let url = appendQueryParam(ENDPOINT, 'ver', '$prebid.version$'); - - const timeout = bidderRequest.timeout; - url = appendQueryParam(url, 'tmax', timeout); - - if (gdprApplies(bidderRequest)) { - const consentString = bidderRequest.gdprConsent.consentString; - url = appendQueryParam(url, 'gdpr', consentString); - } - - if (ccpaApplies(bidderRequest)) { - url = appendQueryParam(url, 'ccpa', bidderRequest.uspConsent); - } - - return url; -} - -function appendQueryParam(url, key, value) { - if (!value) { - return url; - } - const prefix = url.includes('?') ? '&' : '?'; - return `${url}${prefix}${key}=${encodeURIComponent(value)}`; -} - -function gdprApplies(bidderRequest) { - return Boolean(bidderRequest?.gdprConsent?.gdprApplies); -} - -function ccpaApplies(bidderRequest) { - return ( - isStr(bidderRequest.uspConsent) && - !isEmptyStr(bidderRequest.uspConsent) && - bidderRequest.uspConsent?.substr(1, 3) !== '---' - ); -} - -function processBidRequest(bid) { - const sizes = normalizeSizes(bid.sizes); - - return { - bid: bid.bidId, - pid: bid.params.pid, - sizes, - }; -} - -function normalizeSizes(sizes) { - const isSingleSize = - isArray(sizes) && - sizes.length === 2 && - !isArray(sizes[0]) && - !isArray(sizes[1]); - - if (isSingleSize) { - return [sizes]; - } - - return sizes; -} - -function getFirstPartyData(ortb2) { - let fpd = ortb2 || {}; - optimizeObject(fpd); - return fpd; -} - -function optimizeObject(obj) { - if (!isPlainObject(obj)) { - return; - } - for (const [key, value] of Object.entries(obj)) { - optimizeObject(value); - // only delete empty object, array, or string - if ( - (isPlainObject(value) || isArray(value) || isStr(value)) && - isEmpty(value) - ) { - delete obj[key]; - } - } -} - -function isValidResponse(bidResponse) { - const auth = bidResponse?.body?.auth; - const bids = bidResponse?.body?.data?.bids; - return isStr(auth) && isArray(bids) && !isEmpty(bids); -} - -function processBidResponse(bid) { - const meta = bid.meta || {}; - meta.advertiserDomains = bid.meta?.advertiserDomains || []; - - return { - ...bid, - meta, - }; -} - -registerBidder(spec); diff --git a/modules/glimpseBidAdapter.md b/modules/glimpseBidAdapter.md deleted file mode 100644 index e82c5d8f32e..00000000000 --- a/modules/glimpseBidAdapter.md +++ /dev/null @@ -1,37 +0,0 @@ -# Overview - -``` -Module Name: Glimpse Protocol Bid Adapter -Module Type: Bidder Adapter -Maintainer: publisher@glimpseprotocol.io -``` - -# Description - -This module connects publishers to Glimpse Protocol's demand sources via Prebid.js. Our innovative marketplace protects -consumer privacy while allowing precise targeting. It is compliant with GDPR, DPA and CCPA. - -The Glimpse Adapter supports banner formats. - -# Test Parameters - -```javascript -const adUnits = [ - { - code: 'banner-div-a', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'glimpse', - params: { - pid: 'e53a7f564f8f44cc913b', - }, - }, - ], - }, -]; -``` diff --git a/modules/glomexBidAdapter.js b/modules/glomexBidAdapter.js index 5c9b3c1fa28..10f5593940e 100644 --- a/modules/glomexBidAdapter.js +++ b/modules/glomexBidAdapter.js @@ -26,6 +26,7 @@ export const spec = { method: 'POST', url: `${ENDPOINT}`, data: { + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: bidderRequest.auctionId, refererInfo: { // TODO: this collects everything it finds, except for canonicalUrl diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index 9bc1a15b60b..8c90d0cccfe 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -47,7 +47,7 @@ export const spec = { let queryString = ''; const request = validBidRequests[i]; - const tid = request.transactionId; + const tid = request.ortb2Imp?.ext?.tid; const bid = request.bidId; const imuid = deepAccess(request, 'userId.imuid'); const sharedId = deepAccess(request, 'userId.pubcid'); diff --git a/modules/gnetBidAdapter.js b/modules/gnetBidAdapter.js index 0b02a29c0d4..38e96c183b9 100644 --- a/modules/gnetBidAdapter.js +++ b/modules/gnetBidAdapter.js @@ -39,7 +39,7 @@ export const spec = { data.referer = referer; data.adUnitCode = request.adUnitCode; data.bidId = request.bidId; - data.transactionId = request.transactionId; + data.transactionId = request.ortb2Imp?.ext?.tid; data.gftuid = _getCookie(); data.sizes = parseSizesInput(request.sizes); diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index 21c56643353..4768931950c 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -20,17 +20,18 @@ import { isStr, logError, logInfo, - logMessage, - transformBidderParamKeywords + logMessage } from '../src/utils.js'; import {config} from '../src/config.js'; -import {getIabSubCategory, registerBidder} from '../src/adapters/bidderFactory.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {auctionManager} from '../src/auctionManager.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { APPNEXUS_CATEGORY_MAPPING } from '../libraries/categoryTranslationMapping/index.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'goldbach'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -86,7 +87,6 @@ const NATIVE_MAPPING = { }; const SOURCE = 'pbjs'; const MAX_IMPS_PER_REQUEST = 15; -const mappingFileUrl = 'https://acdn.adnxs-simple.com/prebid/appnexus-mapping/mappings.json'; const SCRIPT_TAG_START = ' { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -443,16 +421,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function reloadViewabilityScriptWithCorrectParameters(bid) { let viewJsPayload = getAppnexusViewabilityScriptFromJsTrackers(bid.native.javascriptTrackers); @@ -656,7 +624,7 @@ function newBid(serverBid, rtbBid, bidderRequest) { const videoContext = deepAccess(bidRequest, 'mediaTypes.video.context'); switch (videoContext) { case ADPOD: - const primaryCatId = getIabSubCategory(bidRequest.bidder, rtbBid.brand_category_id); + const primaryCatId = (APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id]) ? APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id] : null; bid.meta = Object.assign({}, bid.meta, { primaryCatId }); const dealTier = rtbBid.deal_priority; bid.video = { @@ -797,14 +765,7 @@ function bidToTag(bid) { if (bid.params.externalImpId) { tag.external_imp_id = bid.params.externalImpId; } - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - tag.keywords = keywords; - } + tag.keywords = getANKeywordParam(bid.ortb2, bid.params.keywords); let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { @@ -919,6 +880,7 @@ function bidToTag(bid) { tag['banner_frameworks'] = bid.params.frameworks; } + // TODO: why does this need to iterate through every ad unit? let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { tag.ad_types.push(BANNER); diff --git a/modules/googleAnalyticsAdapter.js b/modules/googleAnalyticsAdapter.js deleted file mode 100644 index 88ce20d07fa..00000000000 --- a/modules/googleAnalyticsAdapter.js +++ /dev/null @@ -1,298 +0,0 @@ -/** - * ga.js - analytics adapter for google analytics - */ - -import { _each, logMessage } from '../src/utils.js'; -import * as events from '../src/events.js'; -import CONSTANTS from '../src/constants.json'; -import adapterManager from '../src/adapterManager.js'; - -var BID_REQUESTED = CONSTANTS.EVENTS.BID_REQUESTED; -var BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; -var BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; -var BID_WON = CONSTANTS.EVENTS.BID_WON; - -var _disableInteraction = { nonInteraction: true }; -var _analyticsQueue = []; -var _gaGlobal = null; -var _enableCheck = true; -var _category = 'Prebid.js Bids'; -var _eventCount = 0; -var _enableDistribution = false; -var _cpmDistribution = null; -var _trackerSend = null; -var _sampled = true; -var _sendFloors = false; - -let adapter = {}; - -/** - * This will enable sending data to google analytics. Only call once, or duplicate data will be sent! - * @param {object} provider use to set GA global (if renamed); - * @param {object} options use to configure adapter; - * @return {[type]} [description] - */ -adapter.enableAnalytics = function ({ provider, options }) { - _gaGlobal = provider || 'ga'; - _trackerSend = options && options.trackerName ? options.trackerName + '.send' : 'send'; - _sampled = typeof options === 'undefined' || typeof options.sampling === 'undefined' || - Math.random() < parseFloat(options.sampling); - - if (options && typeof options.global !== 'undefined') { - _gaGlobal = options.global; - } - if (options && typeof options.enableDistribution !== 'undefined') { - _enableDistribution = options.enableDistribution; - } - if (options && typeof options.cpmDistribution === 'function') { - _cpmDistribution = options.cpmDistribution; - } - if (options && typeof options.sendFloors !== 'undefined') { - _sendFloors = options.sendFloors; - } - - var bid = null; - - if (_sampled) { - // first send all events fired before enableAnalytics called - - var existingEvents = events.getEvents(); - - _each(existingEvents, function (eventObj) { - if (typeof eventObj !== 'object') { - return; - } - var args = eventObj.args; - - if (eventObj.eventType === BID_REQUESTED) { - bid = args; - sendBidRequestToGa(bid); - } else if (eventObj.eventType === BID_RESPONSE) { - // bid is 2nd args - bid = args; - sendBidResponseToGa(bid); - } else if (eventObj.eventType === BID_TIMEOUT) { - const bidderArray = args; - sendBidTimeouts(bidderArray); - } else if (eventObj.eventType === BID_WON) { - bid = args; - sendBidWonToGa(bid); - } - }); - - // Next register event listeners to send data immediately - - // bidRequests - events.on(BID_REQUESTED, function (bidRequestObj) { - sendBidRequestToGa(bidRequestObj); - }); - - // bidResponses - events.on(BID_RESPONSE, function (bid) { - sendBidResponseToGa(bid); - }); - - // bidTimeouts - events.on(BID_TIMEOUT, function (bidderArray) { - sendBidTimeouts(bidderArray); - }); - - // wins - events.on(BID_WON, function (bid) { - sendBidWonToGa(bid); - }); - } else { - logMessage('Prebid.js google analytics disabled by sampling'); - } - - // finally set this function to return log message, prevents multiple adapter listeners - this.enableAnalytics = function _enable() { - return logMessage(`Analytics adapter already enabled, unnecessary call to \`enableAnalytics\`.`); - }; -}; - -adapter.getTrackerSend = function getTrackerSend() { - return _trackerSend; -}; - -/** - * Check if gaGlobal or window.ga is defined on page. If defined execute all the GA commands - */ -function checkAnalytics() { - if (_enableCheck && typeof window[_gaGlobal] === 'function') { - for (var i = 0; i < _analyticsQueue.length; i++) { - _analyticsQueue[i].call(); - } - - // override push to execute the command immediately from now on - _analyticsQueue.push = function (fn) { - fn.call(); - }; - - // turn check into NOOP - _enableCheck = false; - } - - logMessage('event count sent to GA: ' + _eventCount); -} - -function convertToCents(dollars) { - if (dollars) { - return Math.floor(dollars * 100); - } - - return 0; -} - -function getLoadTimeDistribution(time) { - var distribution; - if (time >= 0 && time < 200) { - distribution = '0-200ms'; - } else if (time >= 200 && time < 300) { - distribution = '0200-300ms'; - } else if (time >= 300 && time < 400) { - distribution = '0300-400ms'; - } else if (time >= 400 && time < 500) { - distribution = '0400-500ms'; - } else if (time >= 500 && time < 600) { - distribution = '0500-600ms'; - } else if (time >= 600 && time < 800) { - distribution = '0600-800ms'; - } else if (time >= 800 && time < 1000) { - distribution = '0800-1000ms'; - } else if (time >= 1000 && time < 1200) { - distribution = '1000-1200ms'; - } else if (time >= 1200 && time < 1500) { - distribution = '1200-1500ms'; - } else if (time >= 1500 && time < 2000) { - distribution = '1500-2000ms'; - } else if (time >= 2000) { - distribution = '2000ms above'; - } - - return distribution; -} - -function getCpmDistribution(cpm) { - if (_cpmDistribution) { - return _cpmDistribution(cpm); - } - var distribution; - if (cpm >= 0 && cpm < 0.5) { - distribution = '$0-0.5'; - } else if (cpm >= 0.5 && cpm < 1) { - distribution = '$0.5-1'; - } else if (cpm >= 1 && cpm < 1.5) { - distribution = '$1-1.5'; - } else if (cpm >= 1.5 && cpm < 2) { - distribution = '$1.5-2'; - } else if (cpm >= 2 && cpm < 2.5) { - distribution = '$2-2.5'; - } else if (cpm >= 2.5 && cpm < 3) { - distribution = '$2.5-3'; - } else if (cpm >= 3 && cpm < 4) { - distribution = '$3-4'; - } else if (cpm >= 4 && cpm < 6) { - distribution = '$4-6'; - } else if (cpm >= 6 && cpm < 8) { - distribution = '$6-8'; - } else if (cpm >= 8) { - distribution = '$8 above'; - } - - return distribution; -} - -function sendBidRequestToGa(bid) { - if (bid && bid.bidderCode) { - _analyticsQueue.push(function () { - _eventCount++; - if (_sendFloors) { - var floor = 'No Floor'; - if (bid.floorData) { - floor = bid.floorData.floorValue; - } else if (bid.bids.length) { - floor = bid.bids[0].getFloor().floor; - } - window[_gaGlobal](_trackerSend, 'event', _category, 'Requests by Floor=' + floor, bid.bidderCode, 1, _disableInteraction); - } else { - window[_gaGlobal](_trackerSend, 'event', _category, 'Requests', bid.bidderCode, 1, _disableInteraction); - } - }); - } - - // check the queue - checkAnalytics(); -} - -function sendBidResponseToGa(bid) { - if (bid && bid.bidderCode) { - _analyticsQueue.push(function () { - var cpmCents = convertToCents(bid.cpm); - var bidder = bid.bidderCode; - if (typeof bid.timeToRespond !== 'undefined' && _enableDistribution) { - _eventCount++; - var dis = getLoadTimeDistribution(bid.timeToRespond); - window[_gaGlobal](_trackerSend, 'event', 'Prebid.js Load Time Distribution', dis, bidder, 1, _disableInteraction); - } - - if (bid.cpm > 0) { - _eventCount = _eventCount + 2; - var cpmDis = getCpmDistribution(bid.cpm); - if (_enableDistribution) { - _eventCount++; - window[_gaGlobal](_trackerSend, 'event', 'Prebid.js CPM Distribution', cpmDis, bidder, 1, _disableInteraction); - } - if (_sendFloors) { - var floor = (bid.floorData) ? bid.floorData.floorValue : 'No Floor'; - window[_gaGlobal](_trackerSend, 'event', _category, 'Bids by Floor=' + floor, 'Size=' + bid.size + ',' + bidder, cpmCents, _disableInteraction); - } else { - window[_gaGlobal](_trackerSend, 'event', _category, 'Bids', bidder, cpmCents, _disableInteraction); - } - window[_gaGlobal](_trackerSend, 'event', _category, 'Bid Load Time', bidder, bid.timeToRespond, _disableInteraction); - } - }); - } - - // check the queue - checkAnalytics(); -} - -function sendBidTimeouts(timedOutBidders) { - _analyticsQueue.push(function () { - _each(timedOutBidders, function (bidderCode) { - _eventCount++; - var bidderName = bidderCode.bidder; - window[_gaGlobal](_trackerSend, 'event', _category, 'Timeouts', bidderName, _disableInteraction); - }); - }); - - checkAnalytics(); -} - -function sendBidWonToGa(bid) { - var cpmCents = convertToCents(bid.cpm); - _analyticsQueue.push(function () { - _eventCount++; - if (_sendFloors) { - var floor = (bid.floorData) ? bid.floorData.floorValue : 'No Floor'; - window[_gaGlobal](_trackerSend, 'event', _category, 'Wins by Floor=' + floor, 'Size=' + bid.size + ',' + bid.bidderCode, cpmCents, _disableInteraction); - } else { - window[_gaGlobal](_trackerSend, 'event', _category, 'Wins', bid.bidderCode, cpmCents, _disableInteraction); - } - }); - - checkAnalytics(); -} - -/** - * Exposed for testing purposes - */ -adapter.getCpmDistribution = getCpmDistribution; - -adapterManager.registerAnalyticsAdapter({ - adapter, - code: 'ga' -}); - -export default adapter; diff --git a/modules/googleAnalyticsAdapter.md b/modules/googleAnalyticsAdapter.md deleted file mode 100644 index 8c31b07b811..00000000000 --- a/modules/googleAnalyticsAdapter.md +++ /dev/null @@ -1,38 +0,0 @@ -# Google Analytics Adapter - -The google analytics adapter pushes prebid events into google analytics. - -## Usage - -The simplest way to enable the analytics adapter is this - -```javascript -pbjs.enableAnalytics([{ - provider: 'ga' -}]); -``` - -Defaults will be used and you should see events being pushed to analytics. - -You can customize the adapter with various `options` like this - -```javascript -pbjs.enableAnalytics([{ - provider: 'ga', - options: { ... } -}]); - -Here is a full list of settings available - -- `global` (string) - name of the global analytics object. Default is `ga` -- `trackerName` (string) - use another tracker for prebid events. Default is the default tracker -- `sampling` (number) - choose a value from `0` to `1`, where `0` means 0% and `1` means 100% tracked -- `enableDistribution` (boolean) - enables additional events that track load time and cpm distribution - by creating buckets for load time and cpm -- `cpmDistribution` (cpm: number => string) - customize the cpm buckets for the cpm distribution -- `sendFloors` (boolean) - if set, will include floor data in the eventCategory field and include ad unit code in eventAction field - - -## Additional resources - -- [Prebid GA Analytics](http://prebid.org/overview/ga-analytics.html) diff --git a/modules/gothamadsBidAdapter.js b/modules/gothamadsBidAdapter.js index 6b549f347bb..9f44a54460f 100644 --- a/modules/gothamadsBidAdapter.js +++ b/modules/gothamadsBidAdapter.js @@ -104,7 +104,7 @@ export const spec = { host: location.host }, source: { - tid: bidRequest.transactionId + tid: bidderRequest?.ortb2?.source?.tid, }, regs: { coppa: config.getConfig('coppa') === true ? 1 : 0, @@ -224,7 +224,7 @@ const parseNative = admObject => { const prepareImpObject = (bidRequest) => { let impObject = { - id: bidRequest.transactionId, + id: bidRequest.bidId, secure: 1, ext: { placementId: bidRequest.params.placementId @@ -247,7 +247,8 @@ const prepareImpObject = (bidRequest) => { const addNativeParameters = bidRequest => { let impObject = { - id: bidRequest.transactionId, + // TODO: this is not an "impObject", and `id` is not part of the ORTB native spec + id: bidRequest.bidId, ver: NATIVE_VERSION, }; diff --git a/modules/greenbidsAnalyticsAdapter.js b/modules/greenbidsAnalyticsAdapter.js index 38ed14dd295..d5aff5ab32a 100644 --- a/modules/greenbidsAnalyticsAdapter.js +++ b/modules/greenbidsAnalyticsAdapter.js @@ -8,7 +8,7 @@ const analyticsType = 'endpoint'; export const ANALYTICS_VERSION = '1.0.0'; -const ANALYTICS_SERVER = 'https://europe-west2-greenbids-357713.cloudfunctions.net/publisher-analytics-endpoint'; +const ANALYTICS_SERVER = 'https://a.greenbids.ai'; const { EVENTS: { diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 130eac9512d..9c63e871356 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -6,8 +6,6 @@ import { generateUUID, mergeDeep, logWarn, - parseUrl, - isArray, isNumber, isStr } from '../src/utils.js'; @@ -16,25 +14,16 @@ import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; -import { find } from '../src/polyfill.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; -const ADAPTER_VERSION_FOR_CRITEO_MODE = 34; -const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; -const PROFILE_ID_INLINE = 207; -const SID_COOKIE_NAME = 'cto_sid'; -const IDCPY_COOKIE_NAME = 'cto_idcpy'; -const OPTOUT_COOKIE_NAME = 'cto_optout'; -const BUNDLE_COOKIE_NAME = 'cto_bundle'; - const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const USER_ID_KEY = 'tmguid'; const GVLID = 686; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', @@ -98,33 +87,24 @@ export const spec = { let userExt = null; let endpoint = null; let forceBidderName = false; - let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo, gppConsent} = bidderRequest || {}; + let {bidderRequestId, gdprConsent, uspConsent, timeout, refererInfo, gppConsent} = bidderRequest || {}; const referer = refererInfo ? encodeURIComponent(refererInfo.page) : ''; const tmax = timeout; const imp = []; const bidsMap = {}; const requests = []; - const criteoBidsMap = {}; - const criteoBidRequests = []; const sources = []; const bidsArray = []; validBidRequests.forEach((bid) => { const bidObject = { bid, savedPrebidBid: null }; - if (bid.params.withCriteo && criteoSpec.isBidRequestValid(bid)) { - criteoBidsMap[bid.bidId] = bidObject; - criteoBidRequests.push(bid); - } if (!bid.params.uid && !bid.params.secid) { return; } if (!bidderRequestId) { bidderRequestId = bid.bidderRequestId; } - if (!auctionId) { - auctionId = bid.auctionId; - } if (!schain) { schain = bid.schain; } @@ -196,7 +176,7 @@ export const spec = { if (impObj.banner || impObj.video) { if (multiRequest) { const reqSource = { - tid: bid.auctionId && bid.auctionId.toString(), + tid: bidderRequest?.ortb2?.source?.tid?.toString?.(), ext: { wrapper: 'Prebid_js', wrapper_version: '$prebid.version$' @@ -247,7 +227,7 @@ export const spec = { } const reqSource = { - tid: auctionId && auctionId.toString(), + tid: bidderRequest?.ortb2?.source?.tid?.toString?.(), ext: { wrapper: 'Prebid_js', wrapper_version: '$prebid.version$' @@ -405,11 +385,6 @@ export const spec = { } }); - const criteoRequest = criteoBidRequests.length && criteoSpec.buildRequests(criteoBidRequests, bidderRequest); - if (criteoRequest) { - criteoRequest.criteoBidsMap = criteoBidsMap; - } - return [...requests.map((req, i) => { let sp; const url = (endpoint || ENDPOINT_URL).replace(/[?&]sp=([^?&=]+)/, (i, found) => { @@ -431,7 +406,7 @@ export const spec = { url: endpoint || ENDPOINT_URL, data: JSON.stringify(mainRequest), bidsMap - }] : []), ...(criteoRequest ? [criteoRequest] : [])]; + }] : [])]; }, /** * Unpack the response from the server into a list of bids. @@ -441,34 +416,26 @@ export const spec = { * @param {*} RendererConst * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, bidRequest, RendererConst = Renderer) { - if (bidRequest.criteoBidsMap && bidRequest.bidRequests) { - const criteoBids = criteoSpec.interpretResponse(serverResponse, bidRequest); - return criteoBids.filter((bid) => { - const { savedPrebidBid } = bidRequest.criteoBidsMap[bid.requestId] || {}; - return canPublishResponse(bid.cpm, savedPrebidBid && savedPrebidBid.cpm); - }); - } else { - serverResponse = serverResponse && serverResponse.body; - const bidResponses = []; + interpretResponse: function (serverResponse, bidRequest, RendererConst = Renderer) { + serverResponse = serverResponse && serverResponse.body; + const bidResponses = []; - let errorMessage; + let errorMessage; - if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; - else if (serverResponse.seatbid && !serverResponse.seatbid.length) { - errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; - } + if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; + else if (serverResponse.seatbid && !serverResponse.seatbid.length) { + errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; + } - const bidderCode = this.forceBidderName || this.code; + const bidderCode = this.forceBidderName || this.code; - if (!errorMessage && serverResponse.seatbid) { - serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidRequest, bidResponses, RendererConst, bidderCode); - }); - } - if (errorMessage) logError(errorMessage); - return bidResponses; + if (!errorMessage && serverResponse.seatbid) { + serverResponse.seatbid.forEach(respItem => { + _addBidResponse(_getBidFromResponse(respItem), bidRequest, bidResponses, RendererConst, bidderCode); + }); } + if (errorMessage) logError(errorMessage); + return bidResponses; }, getUserSyncs: function (...args) { const [syncOptions,, gdprConsent, uspConsent] = args; @@ -746,372 +713,4 @@ export function getSyncUrl() { return SYNC_URL; } -// ================ Criteo methods ================== - -const criteoSpec = { - /** f - * @param {object} bid - * @return {boolean} - */ - isBidRequestValid: (bid) => { - // either one of zoneId or networkId should be set - if (!(bid && bid.params && (bid.params.zoneId || bid.params.networkId))) { - return false; - } - - // video media types requires some mandatory params - if (hasVideoMediaType(bid)) { - if (!hasValidVideoMediaType(bid)) { - return false; - } - } - - return true; - }, - - /** - * @param {BidRequest[]} bidRequests - * @param {*} bidderRequest - * @return {ServerRequest} - */ - buildRequests: (bidRequests, bidderRequest) => { - let url; - let data; - let fpd = bidderRequest.ortb2 || {}; - - Object.assign(bidderRequest, { - publisherExt: fpd.site?.ext, - userExt: fpd.user?.ext, - ceh: config.getConfig('criteo.ceh'), - coppa: config.getConfig('coppa') - }); - - const context = buildContext(bidRequests, bidderRequest); - url = buildCdbUrl(context); - data = buildCdbRequest(context, bidRequests, bidderRequest); - - if (data) { - return { method: 'POST', url, data, bidRequests }; - } - }, - - /** - * @param {*} response - * @param {ServerRequest} request - * @return {Bid[]} - */ - interpretResponse: (response, request) => { - const body = response.body || response; - const bids = []; - - if (body && body.slots && isArray(body.slots)) { - body.slots.forEach(slot => { - const bidRequest = find(request.bidRequests, b => b.adUnitCode === slot.impid && (!b.params.zoneId || parseInt(b.params.zoneId) === slot.zoneid)); - const bidId = bidRequest.bidId; - const bid = { - requestId: bidId, - cpm: slot.cpm, - currency: slot.currency, - netRevenue: true, - ttl: slot.ttl || 60, - creativeId: slot.creativecode, - width: slot.width, - height: slot.height, - dealId: slot.dealCode, - }; - if (body.ext?.paf?.transmission && slot.ext?.paf?.content_id) { - const pafResponseMeta = { - content_id: slot.ext.paf.content_id, - transmission: response.ext.paf.transmission - }; - bid.meta = Object.assign({}, bid.meta, { paf: pafResponseMeta }); - } - if (slot.adomain) { - bid.meta = Object.assign({}, bid.meta, { advertiserDomains: slot.adomain }); - } - if (slot.video) { - bid.vastUrl = slot.displayurl; - bid.mediaType = VIDEO; - } else { - bid.ad = slot.creative; - } - bids.push(bid); - }); - } - - return bids; - } -}; - -function readFromAllStorages(name) { - const fromCookie = storage.getCookie(name); - const fromLocalStorage = storage.getDataFromLocalStorage(name); - - return fromCookie || fromLocalStorage || undefined; -} - -/** - * @param {BidRequest[]} bidRequests - * @param bidderRequest - */ -function buildContext(bidRequests, bidderRequest) { - let referrer = ''; - if (bidderRequest && bidderRequest.refererInfo) { - referrer = bidderRequest.refererInfo.page; - } - const queryString = parseUrl(bidderRequest?.refererInfo?.topmostLocation).search; - - const context = { - url: referrer, - debug: queryString['pbt_debug'] === '1', - noLog: queryString['pbt_nolog'] === '1', - amp: false, - }; - - bidRequests.forEach(bidRequest => { - if (bidRequest.params.integrationMode === 'amp') { - context.amp = true; - } - }); - - return context; -} - -/** - * @param {CriteoContext} context - * @return {string} - */ -function buildCdbUrl(context) { - let url = CDB_ENDPOINT; - url += '?profileId=' + PROFILE_ID_INLINE; - url += '&av=' + String(ADAPTER_VERSION_FOR_CRITEO_MODE); - url += '&wv=' + encodeURIComponent('$prebid.version$'); - url += '&cb=' + String(Math.floor(Math.random() * 99999999999)); - - if (storage.localStorageIsEnabled()) { - url += '&lsavail=1'; - } else { - url += '&lsavail=0'; - } - - if (context.amp) { - url += '&im=1'; - } - if (context.debug) { - url += '&debug=1'; - } - if (context.noLog) { - url += '&nolog=1'; - } - - const bundle = readFromAllStorages(BUNDLE_COOKIE_NAME); - if (bundle) { - url += `&bundle=${bundle}`; - } - - const optout = readFromAllStorages(OPTOUT_COOKIE_NAME); - if (optout) { - url += `&optout=1`; - } - - const sid = readFromAllStorages(SID_COOKIE_NAME); - if (sid) { - url += `&sid=${sid}`; - } - - const idcpy = readFromAllStorages(IDCPY_COOKIE_NAME); - if (idcpy) { - url += `&idcpy=${idcpy}`; - } - - return url; -} - -/** - * @param {CriteoContext} context - * @param {BidRequest[]} bidRequests - * @param bidderRequest - * @return {*} - */ -function buildCdbRequest(context, bidRequests, bidderRequest) { - let networkId; - let schain; - const request = { - publisher: { - url: context.url, - ext: bidderRequest.publisherExt, - }, - regs: { - coppa: bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined) - }, - slots: bidRequests.map(bidRequest => { - networkId = bidRequest.params.networkId || networkId; - schain = bidRequest.schain || schain; - const slot = { - impid: bidRequest.adUnitCode, - transactionid: bidRequest.transactionId, - auctionId: bidRequest.auctionId, - }; - if (bidRequest.params.zoneId) { - slot.zoneid = bidRequest.params.zoneId; - } - if (deepAccess(bidRequest, 'ortb2Imp.ext')) { - slot.ext = bidRequest.ortb2Imp.ext; - } - if (bidRequest.params.ext) { - slot.ext = Object.assign({}, slot.ext, bidRequest.params.ext); - } - if (bidRequest.params.publisherSubId) { - slot.publishersubid = bidRequest.params.publisherSubId; - } - - if (hasBannerMediaType(bidRequest)) { - slot.sizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'), parseSize); - } else { - slot.sizes = []; - } - - if (hasVideoMediaType(bidRequest)) { - const video = { - playersizes: parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize'), parseSize), - mimes: bidRequest.mediaTypes.video.mimes, - protocols: bidRequest.mediaTypes.video.protocols, - maxduration: bidRequest.mediaTypes.video.maxduration, - api: bidRequest.mediaTypes.video.api, - skip: bidRequest.mediaTypes.video.skip, - placement: bidRequest.mediaTypes.video.placement, - minduration: bidRequest.mediaTypes.video.minduration, - playbackmethod: bidRequest.mediaTypes.video.playbackmethod, - startdelay: bidRequest.mediaTypes.video.startdelay - }; - if ('plcmt' in bidRequest.mediaTypes.video) { - video.plcmt = bidRequest.mediaTypes.video.plcmt; - } - const paramsVideo = bidRequest.params.video; - if (paramsVideo !== undefined) { - video.skip = video.skip || paramsVideo.skip || 0; - video.placement = video.placement || paramsVideo.placement; - video.minduration = video.minduration || paramsVideo.minduration; - video.playbackmethod = video.playbackmethod || paramsVideo.playbackmethod; - video.startdelay = video.startdelay || paramsVideo.startdelay || 0; - } - - slot.video = video; - } - - enrichSlotWithFloors(slot, bidRequest); - - return slot; - }), - }; - if (networkId) { - request.publisher.networkid = networkId; - } - if (schain) { - request.source = { - ext: { - schain: schain - } - }; - } - request.user = { - ext: bidderRequest.userExt - }; - if (bidderRequest && bidderRequest.ceh) { - request.user.ceh = bidderRequest.ceh; - } - if (bidderRequest && bidderRequest.gdprConsent) { - request.gdprConsent = {}; - if (typeof bidderRequest.gdprConsent.gdprApplies !== 'undefined') { - request.gdprConsent.gdprApplies = !!(bidderRequest.gdprConsent.gdprApplies); - } - request.gdprConsent.version = bidderRequest.gdprConsent.apiVersion; - if (typeof bidderRequest.gdprConsent.consentString !== 'undefined') { - request.gdprConsent.consentData = bidderRequest.gdprConsent.consentString; - } - } - if (bidderRequest && bidderRequest.uspConsent) { - request.user.uspIab = bidderRequest.uspConsent; - } - return request; -} - -function parseSizes(sizes, parser = s => s) { - if (sizes === undefined) { - return []; - } - if (Array.isArray(sizes[0])) { // is there several sizes ? (ie. [[728,90],[200,300]]) - return sizes.map(size => parser(size)); - } - return [parser(sizes)]; // or a single one ? (ie. [728,90]) -} - -function parseSize(size) { - return size[0] + 'x' + size[1]; -} - -function hasVideoMediaType(bidRequest) { - return deepAccess(bidRequest, 'mediaTypes.video') !== undefined; -} - -function hasBannerMediaType(bidRequest) { - return deepAccess(bidRequest, 'mediaTypes.banner') !== undefined; -} - -function hasValidVideoMediaType(bidRequest) { - let isValid = true; - - var requiredMediaTypesParams = ['mimes', 'playerSize', 'maxduration', 'protocols', 'api', 'skip', 'placement', 'playbackmethod']; - - requiredMediaTypesParams.forEach(function (param) { - if (deepAccess(bidRequest, 'mediaTypes.video.' + param) === undefined && deepAccess(bidRequest, 'params.video.' + param) === undefined) { - isValid = false; - logError('TheMediaGrid Bid Adapter (withCriteo mode): mediaTypes.video.' + param + ' is required'); - } - }); - - if (isValid) { - const videoPlacement = bidRequest.mediaTypes.video.placement || bidRequest.params.video.placement; - // We do not support long form for now, also we have to check that context & placement are consistent - if (bidRequest.mediaTypes.video.context === 'instream' && videoPlacement === 1) { - return true; - } else if (bidRequest.mediaTypes.video.context === 'outstream' && videoPlacement !== 1) { - return true; - } - } - - return false; -} - -function enrichSlotWithFloors(slot, bidRequest) { - try { - const slotFloors = {}; - - if (bidRequest.getFloor) { - if (bidRequest.mediaTypes?.banner) { - slotFloors.banner = {}; - const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) - bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({ size: bannerSize, mediaType: BANNER })); - } - - if (bidRequest.mediaTypes?.video) { - slotFloors.video = {}; - const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) - videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({ size: videoSize, mediaType: VIDEO })); - } - - if (Object.keys(slotFloors).length > 0) { - if (!slot.ext) { - slot.ext = {} - } - Object.assign(slot.ext, { - floors: slotFloors - }); - } - } - } catch (e) { - logError('Could not parse floors from Prebid: ' + e); - } -} - registerBidder(spec); diff --git a/modules/growthCodeRtdProvider.js b/modules/growthCodeRtdProvider.js new file mode 100644 index 00000000000..370ace9a203 --- /dev/null +++ b/modules/growthCodeRtdProvider.js @@ -0,0 +1,131 @@ +/** + * This module adds GrowthCode HEM and other Data to Bid Requests + * @module modules/growthCodeRtdProvider + */ +import { submodule } from '../src/hook.js' +import { getStorageManager } from '../src/storageManager.js'; +import { + logMessage, logError, tryAppendQueryString, mergeDeep +} from '../src/utils.js'; +import * as ajax from '../src/ajax.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; + +const MODULE_NAME = 'growthCodeRtd'; +const LOG_PREFIX = 'GrowthCodeRtd: '; +const ENDPOINT_URL = 'https://p2.gcprivacy.com/v2/rtd?' +const RTD_EXPIRE_KEY = 'gc_rtd_expires_at' +const RTD_CACHE_KEY = 'gc_rtd_items' + +export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME }); +let items + +export const growthCodeRtdProvider = { + name: MODULE_NAME, + init: init, + getBidRequestData: alterBidRequests, + addData: addData, + callServer: callServer +}; + +/** + * Parse json if possible, else return null + * @param data + * @returns {any|null} + */ +function tryParse(data) { + try { + return JSON.parse(data); + } catch (err) { + logError(err); + return null; + } +} + +/** + * Init The RTD Module + * @param config + * @param userConsent + * @returns {boolean} + */ +function init(config, userConsent) { + logMessage(LOG_PREFIX + 'Init RTB'); + + if (config == null) { + return false + } + + const configParams = (config && config.params) || {}; + let expiresAt = parseInt(storage.getDataFromLocalStorage(RTD_EXPIRE_KEY, null)); + + items = tryParse(storage.getDataFromLocalStorage(RTD_CACHE_KEY, null)); + + return callServer(configParams, items, expiresAt, userConsent); +} +function callServer(configParams, items, expiresAt, userConsent) { + // Expire Cache + let now = Math.trunc(Date.now() / 1000); + if ((!isNaN(expiresAt)) && (now > expiresAt)) { + expiresAt = NaN; + storage.removeDataFromLocalStorage(RTD_CACHE_KEY, null) + storage.removeDataFromLocalStorage(RTD_EXPIRE_KEY, null) + } + if ((items === null) && (isNaN(expiresAt))) { + let gcid = localStorage.getItem('gcid') + + let url = configParams.url ? configParams.url : ENDPOINT_URL; + url = tryAppendQueryString(url, 'pid', configParams.pid); + url = tryAppendQueryString(url, 'u', window.location.href); + url = tryAppendQueryString(url, 'gcid', gcid); + if ((userConsent !== null) && (userConsent.gdpr !== null) && (userConsent.gdpr.consentData.getTCData.tcString)) { + url = tryAppendQueryString(url, 'tcf', userConsent.gdpr.consentData.getTCData.tcString) + } + + ajax.ajaxBuilder()(url, { + success: response => { + let respJson = tryParse(response); + // If response is a valid json and should save is true + if (respJson && respJson.results >= 1) { + storage.setDataInLocalStorage(RTD_CACHE_KEY, JSON.stringify(respJson.items), null); + storage.setDataInLocalStorage(RTD_EXPIRE_KEY, respJson.expires_at, null) + } else { + storage.setDataInLocalStorage(RTD_EXPIRE_KEY, respJson.expires_at, null) + } + }, + error: error => { + logError(LOG_PREFIX + 'ID fetch encountered an error', error); + } + }, undefined, {method: 'GET', withCredentials: true}) + } + + return true; +} + +function addData(reqBidsConfigObj, items) { + let merge = false + + for (let j = 0; j < items.length; j++) { + let item = items[j] + let data = JSON.parse(item.parameters); + if (item['attachment_point'] === 'data') { + mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, data) + merge = true + } + } + return merge +} + +/** + * Alter the Bid Request for additional information such as HEM or 3rd Party Ids + * @param reqBidsConfigObj + * @param callback + * @param config + * @param userConsent + */ +function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) { + if (items != null) { + addData(reqBidsConfigObj, items) + } + callback(); +} + +submodule('realTimeData', growthCodeRtdProvider); diff --git a/modules/growthCodeRtdProvider.md b/modules/growthCodeRtdProvider.md new file mode 100644 index 00000000000..274b94da7bc --- /dev/null +++ b/modules/growthCodeRtdProvider.md @@ -0,0 +1,55 @@ +## GrowthCode Real-time Data Submodule + +The [GrowthCode](https://growthcode.io) real-time data module in Prebid enables publishers to fully +leverage the potential of their first-party audiences and contextual data. +With an integrated cookieless GrowthCode identity, this module offers real-time +contextual and audience segmentation (IAB Taxonomy 2.2, cattax: 6) capabilities, and HEMs that can seamlessly +integrate into your existing Prebid deployment, making it easy to maximize +your advertising strategies. + +## Building Prebid with GrowthCode Support + +Compile the GrowthCode RTD module into your Prebid build: + +`gulp serve --modules=userId,rtdModule,appnexusBidAdapter,growthCodeRtdProvider,sharedIdSystem,criteoBidAdapter` + +Please visit https://growthcode.io/ for more information. + +``` +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: 'growthCodeRtd', + waitForIt: true, + params: { + pid: 'TEST01', + } + } + ] + } + ... +} +``` + +### Parameter Descriptions for the GrowthCode Configuration Section + +| Name | Type | Description | Notes | +|:---------------------------------|:--------|:--------------------------------------------------------------------------|:----------------------------| +| name | String | Real time data module name | Always 'growthCodeRtd' | +| waitForIt | Boolean | Required to ensure that the auction is delayed until prefetch is complete | Optional. Defaults to false | +| params | Object | | | +| params.pid | String | This is the Parter ID value obtained from GrowthCode | `TEST01` | +| params.url | String | Custom URL for server | Optional | + +## Testing + +To view an example of GrowthCode backends: + +`gulp serve --modules=userId,rtdModule,appnexusBidAdapter,growthCodeRtdProvider,sharedIdSystem,criteoBidAdapter` + +and then point your browser at: + +`http://localhost:9999/integrationExamples/gpt/growthcode.html` diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index fdd3030bb7a..d050af4ac8f 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -293,7 +293,6 @@ function buildRequests(validBidRequests, bidderRequest) { mediaTypes = {}, params = {}, schain, - transactionId, userId = {}, ortb2Imp, adUnitCode = '' @@ -404,7 +403,7 @@ function buildRequests(validBidRequests, bidderRequest) { bids.push({ id: bidId, tmax: timeout, - tId: transactionId, + tId: ortb2Imp?.ext?.tid, pi: data.pi, selector: params.selector, sizes, diff --git a/modules/h12mediaBidAdapter.js b/modules/h12mediaBidAdapter.js index c58f15c9a81..7b1ba9ee286 100644 --- a/modules/h12mediaBidAdapter.js +++ b/modules/h12mediaBidAdapter.js @@ -41,7 +41,7 @@ export const spec = { const bidrequest = { bidId: bidRequest.bidId, - transactionId: bidRequest.transactionId, + transactionId: bidRequest.ortb2Imp?.ext?.tid, adunitId: bidRequest.adUnitCode, pubid: bidderParams.pubid, placementid: bidderParams.placementid || '', diff --git a/modules/holidBidAdapter.js b/modules/holidBidAdapter.js index 92b0a1c18db..f41829bd123 100644 --- a/modules/holidBidAdapter.js +++ b/modules/holidBidAdapter.js @@ -1,15 +1,9 @@ -import { - deepAccess, - getBidIdParameter, - isStr, - logMessage, - triggerPixel, -} from '../src/utils.js' -import * as events from '../src/events.js' -import CONSTANTS from '../src/constants.json' -import { BANNER } from '../src/mediaTypes.js' - -import { registerBidder } from '../src/adapters/bidderFactory.js' +import {deepAccess, getBidIdParameter, isStr, logMessage, triggerPixel, } from '../src/utils.js'; +import * as events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; +import {BANNER} from '../src/mediaTypes.js'; + +import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'holid' const GVLID = 1177 @@ -33,7 +27,7 @@ export const spec = { return validBidRequests.map((bid) => { const requestData = { ...bid.ortb2, - id: bid.auctionId, + id: _bidderRequest.bidderRequestId, imp: [getImp(bid)], } @@ -56,6 +50,7 @@ export const spec = { serverResponse.body.seatbid.map((response) => { response.bid.map((bid) => { const requestId = bidRequest.bidId + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 const auctionId = bidRequest.auctionId const wurl = deepAccess(bid, 'ext.prebid.events.win') const bidResponse = { diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index 6cedb094444..f746e69cbba 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -25,7 +25,7 @@ function buildBidRequests(validBidRequests) { const params = validBidRequest.params; const bidRequest = { bidId: validBidRequest.bidId, - transactionId: validBidRequest.transactionId, + transactionId: validBidRequest.ortb2Imp?.ext?.tid, sizes: validBidRequest.sizes, placement: placementTypes[params.placement], placeId: params.placeId, @@ -88,6 +88,7 @@ function buildBid(bidData) { bid.vastXml = bidData.content; bid.mediaType = VIDEO; + // TODO: why does this need to iterate through every ad unit? let adUnit = find(auctionManager.getAdUnits(), function (unit) { return unit.transactionId === bidData.transactionId; }); @@ -244,7 +245,6 @@ export const spec = { return item.bidId === bid.bidId; }); bid.placement = rawBid.placement; - bid.transactionId = rawBid.transactionId; bid.placeId = rawBid.placeId; return buildBid(bid); }); diff --git a/modules/hypelabBidAdapter.js b/modules/hypelabBidAdapter.js new file mode 100644 index 00000000000..a625c7299a6 --- /dev/null +++ b/modules/hypelabBidAdapter.js @@ -0,0 +1,153 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { generateUUID } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; + +export const BIDDER_CODE = 'hypelab'; +export const ENDPOINT_URL = 'https://api.hypelab.com'; + +export const REQUEST_ROUTE = '/v1/prebid_requests'; +export const EVENT_ROUTE = '/v1/events'; +export const REPORTING_ROUTE = ''; + +const PREBID_VERSION = '$prebid.version$'; +const PROVIDER_NAME = 'prebid'; +const PROVIDER_VERSION = '0.0.1'; + +const url = (route) => ENDPOINT_URL + route; + +export function mediaSize(data) { + if (!data || !data.creative_set) return { width: 0, height: 0 }; + const media = data.creative_set.video || data.creative_set.image || {}; + return { width: media.width, height: media.height }; +} + +function isBidRequestValid(bidderRequest) { + return ( + !!bidderRequest.params?.property_slug && + !!bidderRequest.params?.placement_slug + ); +} + +function buildRequests(validBidRequests, bidderRequest) { + const result = validBidRequests.map((request) => { + const uids = (request.userIdAsEids || []).reduce((a, c) => { + const ids = c.uids.map((uid) => uid.id); + return [...a, ...ids]; + }, []); + + const uuid = uids[0] ? uids[0] : generateTemporaryUUID(); + + const payload = { + property_slug: request.params.property_slug, + placement_slug: request.params.placement_slug, + provider_version: PROVIDER_VERSION, + provider_name: PROVIDER_NAME, + referrer: + bidderRequest.refererInfo?.page || typeof window != 'undefined' + ? window.location.href + : '', + sdk_version: PREBID_VERSION, + sizes: request.sizes, + wids: [], + uuid, + bidRequestsCount: request.bidRequestsCount, + bidderRequestsCount: request.bidderRequestsCount, + bidderWinsCount: request.bidderWinsCount, + wp: { + ada: typeof window != 'undefined' && !!window.cardano, + bnb: typeof window != 'undefined' && !!window.BinanceChain, + eth: typeof window != 'undefined' && !!window.ethereum, + sol: typeof window != 'undefined' && !!window.solana, + tron: typeof window != 'undefined' && !!window.tron, + }, + }; + + return { + method: 'POST', + url: url(REQUEST_ROUTE), + options: { contentType: 'application/json', withCredentials: false }, + data: payload, + bidId: request.bidId, + }; + }); + + return result; +} + +function generateTemporaryUUID() { + return 'tmp_' + generateUUID(); +} + +function interpretResponse(serverResponse, bidRequest) { + const { data } = serverResponse.body; + + if (!data.cpm || !data.html) return []; + + const size = mediaSize(data); + + const result = { + requestId: bidRequest.bidId, + cpm: data.cpm, + width: size.width, + height: size.height, + creativeId: data.creative_set_slug, + currency: data.currency, + netRevenue: true, + referrer: bidRequest.data.referrer, + ttl: data.ttl, + ad: data.html, + mediaType: serverResponse.body.data.media_type, + meta: { + advertiserDomains: data.advertiserDomains || [], + }, + }; + + return [result]; +} + +export function report(eventType, data, route = REPORTING_ROUTE) { + if (!route) return; + + const options = { + method: 'POST', + contentType: 'application/json', + withCredentials: true, + }; + + const request = { type: eventType, data }; + ajax(url(route), null, request, options); +} + +function onTimeout(timeoutData) { + this.report('timeout', timeoutData); +} + +function onBidWon(bid) { + this.report('bidWon', bid); +} + +function onSetTargeting(bid) { + this.report('setTargeting', bid); +} + +function onBidderError(errorData) { + this.report('bidderError', errorData); +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + aliases: ['hype'], + isBidRequestValid, + buildRequests, + interpretResponse, + onTimeout, + onBidWon, + onSetTargeting, + onBidderError, + report, + REPORTING_ROUTE: 'a', +}; + +registerBidder(spec); diff --git a/modules/hypelabBidAdapter.md b/modules/hypelabBidAdapter.md new file mode 100644 index 00000000000..f33664da578 --- /dev/null +++ b/modules/hypelabBidAdapter.md @@ -0,0 +1,38 @@ +# Overview + +``` +Module Name: HypeLab Bid Adapter +Module Type: Bidder Adapter +Maintainer: sdk@hypelab.com +``` + +# Description + +Prebid.JS adapter that connects to HypeLab ad network for bids. +*NOTE*: The HypeLab Adapter requires setup and approval before use. Please reach out to `partnerships@hypelab.com` for more details. To get started, replace the `property_slug` with your property_slug and the `placement_slug` with your placement slug to receive bids. The placement slug will depend on the required size and can be set via the HypeLab interface. + +# Test Parameters + +## Sample Banner Ad Unit + +```js +var adUnits = [ + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[728, 90]], + }, + }, + bids: [ + { + bidder: 'hypelab', + params: { + property_slug: 'prebid', + placement_slug: 'test_placement' + } + } + ] + } +] +``` diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index b7ff836a7e6..bb8a21264d0 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -60,11 +60,11 @@ export const id5IdSubmodule = { */ decode(value, config) { let universalUid; - let linkType = 0; + let ext = {}; if (value && typeof value.universal_uid === 'string') { universalUid = value.universal_uid; - linkType = value.link_type || linkType; + ext = value.ext || ext; } else { return undefined; } @@ -72,9 +72,7 @@ export const id5IdSubmodule = { let responseObj = { id5id: { uid: universalUid, - ext: { - linkType: linkType - } + ext: ext } }; diff --git a/modules/synacormediaBidAdapter.js b/modules/imdsBidAdapter.js similarity index 93% rename from modules/synacormediaBidAdapter.js rename to modules/imdsBidAdapter.js index 7165f778e5f..545a0bd1ac3 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/imdsBidAdapter.js @@ -16,7 +16,10 @@ const BLOCKED_AD_SIZES = [ ]; const DEFAULT_MAX_TTL = 420; // 7 minutes export const spec = { - code: 'synacormedia', + code: 'imds', + aliases: [ + { code: 'synacormedia' } + ], supportedMediaTypes: [ BANNER, VIDEO ], sizeMap: {}, @@ -36,7 +39,7 @@ export const spec = { } const refererInfo = bidderRequest.refererInfo; const openRtbBidRequest = { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, site: { // TODO: does the fallback make sense here? domain: refererInfo.domain || location.hostname, @@ -63,7 +66,7 @@ export const spec = { validBidReqs.forEach((bid, i) => { if (seatId && seatId !== bid.params.seatId) { - logWarn(`Synacormedia: there is an inconsistent seatId: ${bid.params.seatId} but only sending bid requests for ${seatId}, you should double check your configuration`); + logWarn(`IMDS: there is an inconsistent seatId: ${bid.params.seatId} but only sending bid requests for ${seatId}, you should double check your configuration`); return; } else { seatId = bid.params.seatId; @@ -71,7 +74,7 @@ export const spec = { const tagIdOrPlacementId = bid.params.tagId || bid.params.placementId; let pos = parseInt(bid.params.pos || deepAccess(bid.mediaTypes, 'video.pos'), 10); if (isNaN(pos)) { - logWarn(`Synacormedia: there is an invalid POS: ${bid.params.pos}`); + logWarn(`IMDS: there is an invalid POS: ${bid.params.pos}`); pos = 0; } const videoOrBannerKey = this.isVideoBid(bid) ? 'video' : 'banner'; @@ -149,7 +152,7 @@ export const spec = { }; const bidFloor = getBidFloor(bid, 'banner', '*'); if (isNaN(bidFloor)) { - logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`); + logWarn(`IMDS: there is an invalid bid floor: ${bid.params.bidfloor}`); } if (bidFloor !== null && !isNaN(bidFloor)) { imp.bidfloor = bidFloor; @@ -173,7 +176,7 @@ export const spec = { }; const bidFloor = getBidFloor(bid, 'video', size); if (isNaN(bidFloor)) { - logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`); + logWarn(`IMDS: there is an invalid bid floor: ${bid.params.bidfloor}`); } if (bidFloor !== null && !isNaN(bidFloor)) { @@ -211,7 +214,7 @@ export const spec = { }; if (!serverResponse.body || typeof serverResponse.body != 'object') { - logWarn('Synacormedia: server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); + logWarn('IMDS: server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); return; } const {id, seatbid: seatbids} = serverResponse.body; @@ -299,7 +302,7 @@ export const spec = { url: `${USER_SYNC_HOST}/html/usersync.html?src=$$REPO_AND_VERSION$$` }); } else { - logWarn('Synacormedia: Please enable iframe based user sync.'); + logWarn('IMDS: Please enable iframe based user sync.'); } return syncs; } diff --git a/modules/synacormediaBidAdapter.md b/modules/imdsBidAdapter.md similarity index 84% rename from modules/synacormediaBidAdapter.md rename to modules/imdsBidAdapter.md index 523c66fd1d9..15fb407e7ef 100644 --- a/modules/synacormediaBidAdapter.md +++ b/modules/imdsBidAdapter.md @@ -1,14 +1,14 @@ # Overview ``` -Module Name: Synacor Media Bidder Adapter +Module Name: iMedia Digital Services Bidder Adapter Module Type: Bidder Adapter -Maintainer: eng-demand@synacor.com +Maintainer: eng-demand@imds.tv ``` # Description -The Synacor Media adapter requires setup and approval from Synacor. +The iMedia Digital Services adapter requires setup and approval from iMedia Digital Services. Please reach out to your account manager for more information. ### DFP Video Creative @@ -30,7 +30,7 @@ https://track.technoratimedia.com/openrtb/tags?ID=%%PATTERN:hb_cache_id_synacorm } }, bids: [{ - bidder: "synacormedia", + bidder: "imds", params: { seatId: "prebid", tagId: "demo1", @@ -49,7 +49,7 @@ https://track.technoratimedia.com/openrtb/tags?ID=%%PATTERN:hb_cache_id_synacorm } }, bids: [{ - bidder: "synacormedia", + bidder: "imds", params: { seatId: "prebid", tagId: "demo1", diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index d35d4498136..f2bf9aaddcb 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -1,7 +1,7 @@ -import { deepAccess, deepSetValue, generateUUID } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import { ajax } from '../src/ajax.js'; +import {deepAccess, deepSetValue, generateUUID} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import {ajax} from '../src/ajax.js'; const BIDDER_CODE = 'impactify'; const BIDDER_ALIAS = ['imp']; @@ -41,11 +41,11 @@ const getFloor = (bid) => { const createOpenRtbRequest = (validBidRequests, bidderRequest) => { // Create request and set imp bids inside let request = { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, validBidRequests, cur: [DEFAULT_CURRENCY], imp: [], - source: {tid: bidderRequest.auctionId} + source: {tid: bidderRequest.ortb2?.source?.tid} }; // Get the url parameters diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 437fcf7d5bb..b56cc56a186 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -150,8 +150,7 @@ export const CONVERTER = ortbConverter({ mergeDeep(request, { id: getUniqueIdentifierStr(), source: { - // TODO: once https://github.com/prebid/Prebid.js/issues/8573 is resolved, this should be handled by the base ortbConverter logic - tid: context.bidRequests[0].transactionId, + }, ext: { improvedigital: { diff --git a/modules/inmarBidAdapter.js b/modules/inmarBidAdapter.js deleted file mode 100755 index 42bd64ee816..00000000000 --- a/modules/inmarBidAdapter.js +++ /dev/null @@ -1,132 +0,0 @@ -import {logError, mergeDeep} from '../src/utils.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'inmar'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['inm'], - supportedMediaTypes: [BANNER, VIDEO], - - /** - * Determines whether or not the given bid request is valid - * - * @param {bidRequest} bid The bid params to validate. - * @returns {boolean} True if this is a valid bid, and false otherwise - */ - isBidRequestValid: function(bid) { - return !!(bid.params && bid.params.partnerId); - }, - - /** - * Build a server request from the list of valid BidRequests - * @param {validBidRequests} is an array of the valid bids - * @param {bidderRequest} bidder request object - * @returns {ServerRequest} Info describing the request to the server - */ - buildRequests: function(validBidRequests, bidderRequest) { - var payload = { - bidderCode: bidderRequest.bidderCode, - auctionId: bidderRequest.auctionId, - bidderRequestId: bidderRequest.bidderRequestId, - bidRequests: validBidRequests, - auctionStart: bidderRequest.auctionStart, - timeout: bidderRequest.timeout, - // TODO: please do not send internal data structures over the network - refererInfo: bidderRequest.refererInfo.legacy, - start: bidderRequest.start, - gdprConsent: bidderRequest.gdprConsent, - uspConsent: bidderRequest.uspConsent, - currencyCode: config.getConfig('currency.adServerCurrency'), - coppa: config.getConfig('coppa'), - firstPartyData: getLegacyFpd(bidderRequest.ortb2), - prebidVersion: '$prebid.version$' - }; - - var payloadString = JSON.stringify(payload); - - return { - method: 'POST', - url: 'https://prebid.owneriq.net:8443/bidder/pb/bid', - data: payloadString, - }; - }, - - /** - * Read the response from the server and build a list of bids - * @param {serverResponse} Response from the server. - * @param {bidRequest} Bid request object - * @returns {bidResponses} Array of bids which were nested inside the server - */ - interpretResponse: function(serverResponse, bidRequest) { - const bidResponses = []; - var response = serverResponse.body; - - try { - if (response) { - var bidResponse = { - requestId: response.requestId, - cpm: response.cpm, - currency: response.currency, - width: response.width, - height: response.height, - ad: response.ad, - ttl: response.ttl, - creativeId: response.creativeId, - netRevenue: response.netRevenue, - vastUrl: response.vastUrl, - dealId: response.dealId, - meta: response.meta - }; - - bidResponses.push(bidResponse); - } - } catch (error) { - logError('Error while parsing inmar response', error); - } - return bidResponses; - }, - - /** - * User Syncs - * - * @param {syncOptions} Publisher prebid configuration - * @param {serverResponses} Response from the server - * @returns {Array} - */ - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = []; - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: 'https://px.owneriq.net/eucm/p/pb' - }); - } - return syncs; - } -}; - -function getLegacyFpd(ortb2) { - if (typeof ortb2 !== 'object') return; - - let duplicate = {}; - - Object.keys(ortb2).forEach((type) => { - let prop = (type === 'site') ? 'context' : type; - duplicate[prop] = (prop === 'context' || prop === 'user') ? Object.keys(ortb2[type]).filter(key => key !== 'data').reduce((result, key) => { - if (key === 'ext') { - mergeDeep(result, ortb2[type][key]); - } else { - mergeDeep(result, {[key]: ortb2[type][key]}); - } - - return result; - }, {}) : ortb2[type]; - }); - - return duplicate; -} - -registerBidder(spec); diff --git a/modules/inmarBidAdapter.md b/modules/inmarBidAdapter.md deleted file mode 100644 index 8ed6b998602..00000000000 --- a/modules/inmarBidAdapter.md +++ /dev/null @@ -1,44 +0,0 @@ -# Overview - -``` -Module Name: Inmar Bidder Adapter -Module Type: Bidder Adapter -Maintainer: oiq_rtb@inmar.com -``` - -# Description - -Connects to Inmar for bids. This adapter supports Display and Video. - -The Inmar adapter requires setup and approval from the Inmar team. -Please reach out to your account manager for more information. - -# Test Parameters - -## Web -``` - var adUnits = [ - { - code: 'test-div1', - sizes: [[300, 250],[300, 600]], - bids: [{ - bidder: 'inmar', - params: { - partnerId: 12345, - position: 1 - } - }] - }, - { - code: 'test-div2', - sizes: [[728, 90],[970, 250]], - bids: [{ - bidder: 'inmar', - params: { - partnerId: 12345, - position: 0 - } - }] - } - ]; -``` diff --git a/modules/innityBidAdapter.js b/modules/innityBidAdapter.js index 71fe588441c..99eec210193 100644 --- a/modules/innityBidAdapter.js +++ b/modules/innityBidAdapter.js @@ -30,6 +30,7 @@ export const spec = { vph: window.screen.height, callback: 'json', callback_uid: bidRequest.bidId, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auction: bidRequest.auctionId, }, }; diff --git a/modules/inskinBidAdapter.js b/modules/inskinBidAdapter.js deleted file mode 100644 index f3464765bde..00000000000 --- a/modules/inskinBidAdapter.js +++ /dev/null @@ -1,391 +0,0 @@ -import { createTrackPixelHtml } from '../src/utils.js'; -import { loadExternalScript } from '../src/adloader.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'inskin'; - -const CONFIG = { - BASE_URI: 'https://mfad.inskinad.com/api/v2' -}; - -export const spec = { - code: BIDDER_CODE, - - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function(bid) { - return !!(bid.params.networkId && bid.params.siteId); - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @param {bidderRequest} - the full bidder request object - * @return ServerRequest Info describing the request to the server. - */ - - buildRequests: function(validBidRequests, bidderRequest) { - // Do we need to group by bidder? i.e. to make multiple requests for - // different endpoints. - - let ret = { - method: 'POST', - url: '', - data: '', - bidRequest: [] - }; - - if (validBidRequests.length < 1) { - return ret; - } - - let ENDPOINT_URL; - - const data = Object.assign({ - placements: [], - time: Date.now(), - user: {}, - url: bidderRequest.refererInfo.page, - enableBotFiltering: true, - includePricingData: true, - parallel: true - }, validBidRequests[0].params); - - if (validBidRequests[0].schain) { - data.rtb = { - schain: validBidRequests[0].schain - }; - } else if (data.publisherId) { - data.rtb = { - schain: { - ext: { - sid: String(data.publisherId) - } - } - }; - } - - delete data.publisherId; - - data.keywords = data.keywords || []; - const restrictions = []; - - if (bidderRequest && bidderRequest.gdprConsent) { - data.consent = { - gdprVendorId: 150, - gdprConsentString: 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 - gdprConsentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - }; - - const purposes = [ - {id: 1, kw: 'nocookies'}, - {id: 2, kw: 'nocontext'}, - {id: 3, kw: 'nodmp'}, - {id: 4, kw: 'nodata'}, - {id: 7, kw: 'noclicks'}, - {id: 9, kw: 'noresearch'} - ]; - - const d = bidderRequest.gdprConsent.vendorData; - - if (d) { - if (d.purposeOneTreatment) { - data.keywords.push('cst-nodisclosure'); - restrictions.push('nodisclosure'); - } - - purposes.map(p => { - if (!checkConsent(p.id, d)) { - data.keywords.push('cst-' + p.kw); - restrictions.push(p.kw); - } - }); - } - } - - validBidRequests.map(bid => { - ENDPOINT_URL = CONFIG.BASE_URI; - - const placement = Object.assign({ - divName: bid.bidId, - adTypes: bid.adTypes || getSize(bid.sizes), - eventIds: [40, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295] - }, bid.params); - - placement.adTypes.push(5, 9, 163, 2163, 3006); - - placement.properties = placement.properties || {}; - - placement.properties.screenWidth = screen.width; - placement.properties.screenHeight = screen.height; - - if (restrictions.length) { - placement.properties.restrictions = restrictions; - } - - if (placement.networkId && placement.siteId) { - data.placements.push(placement); - } - }); - - ret.data = JSON.stringify(data); - ret.bidRequest = validBidRequests; - ret.url = ENDPOINT_URL; - - return ret; - }, - - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse, bidRequest) { - let bid; - let bids; - let bidId; - let bidObj; - let bidResponses = []; - let bidsMap = {}; - - bids = bidRequest.bidRequest; - - serverResponse = (serverResponse || {}).body; - for (let i = 0; i < bids.length; i++) { - bid = {}; - bidObj = bids[i]; - bidId = bidObj.bidId; - - bidsMap[bidId] = bidObj; - - if (serverResponse) { - const decision = serverResponse.decisions && serverResponse.decisions[bidId]; - const data = decision && decision.contents && decision.contents[0] && decision.contents[0].data; - const pubCPM = data && data.customData && data.customData.pubCPM; - const clearPrice = decision && decision.pricing && decision.pricing.clearPrice; - const price = pubCPM || clearPrice; - - if (decision && price) { - decision.impressionUrl += ('&property:pubcpm=' + price); - bidObj.price = price; - - bid.requestId = bidId; - bid.cpm = price; - bid.width = decision.width; - bid.height = decision.height; - bid.ad = retrieveAd(bidId, decision); - bid.currency = 'USD'; - bid.creativeId = decision.adId; - bid.ttl = 360; - bid.meta = { advertiserDomains: decision.adomain ? decision.adomain : [] }; - bid.netRevenue = true; - - bidResponses.push(bid); - } - } - } - - if (bidResponses.length) { - window.addEventListener('message', function(e) { - if (!e.data || e.data.from !== 'ism-bid') { - return; - } - - const decision = serverResponse.decisions && serverResponse.decisions[e.data.bidId]; - if (!decision) { - return; - } - - const id = 'ism_tag_' + Math.floor((Math.random() * 10e16)); - window[id] = { - plr_AdSlot: e.source && e.source.frameElement, - bidId: e.data.bidId, - bidPrice: bidsMap[e.data.bidId].price, - serverResponse - }; - - const url = 'https://cdn.inskinad.com/isfe/publishercode/' + bidsMap[e.data.bidId].params.siteId + '/default.js?autoload&id=' + id; - loadExternalScript(url, BIDDER_CODE); - }); - } - - return bidResponses; - }, - - getUserSyncs: function(syncOptions) { - const userSyncs = []; - - if (syncOptions.pixelEnabled) { - userSyncs.push({ - type: 'image', - url: 'https://e.serverbid.com/udb/9969/match?redir=https%3A%2F%2Fmfad.inskinad.com%2Fudb%2F9874%2Fpool%2Fset%2Fi.gif%3FpoolId%3D9969%26poolKey%3D' - }); - userSyncs.push({ - type: 'image', - url: 'https://ssum.casalemedia.com/usermatchredir?s=185638&cb=https%3A%2F%2Fmfad.inskinad.com%2Fudb%2F9874%2Fsync%2Fi.gif%3FpartnerId%3D1%26userId%3D' - }); - } - - if (syncOptions.iframeEnabled) { - userSyncs.push({ - type: 'iframe', - url: 'https://ssum-sec.casalemedia.com/usermatch?s=184665&cb=https%3A%2F%2Fmfad.inskinad.com%2Fudb%2F9874%2Fsync%2Fi.gif%3FpartnerId%3D1%26userId%3D' - }); - } - - return userSyncs; - } -}; - -const sizeMap = [ - null, - '120x90', - '120x90', - '468x60', - '728x90', - '300x250', - '160x600', - '120x600', - '300x100', - '180x150', - '336x280', - '240x400', - '234x60', - '88x31', - '120x60', - '120x240', - '125x125', - '220x250', - '250x250', - '250x90', - '0x0', - '200x90', - '300x50', - '320x50', - '320x480', - '185x185', - '620x45', - '300x125', - '800x250' -]; - -sizeMap[77] = '970x90'; -sizeMap[123] = '970x250'; -sizeMap[43] = '300x600'; - -function getSize(sizes) { - const result = []; - sizes.forEach(function(size) { - const index = sizeMap.indexOf(size[0] + 'x' + size[1]); - if (index >= 0) { - result.push(index); - } - }); - return result; -} - -function retrieveAd(bidId, decision) { - return "'; - -let getDefaultBidResponse = () => { - return { - id: '245730051428950632', - cur: 'USD', - seatbid: [{ - bid: [{ - id: 1, - impid: '245730051428950632', - price: 0.09, - adm: DEFAULT_AD_CONTENT, - crid: 'creative-id', - h: 90, - w: 728, - dealid: 'deal-id', - ext: {sizeid: 225} - }] - }] - }; -}; - -let getMarketplaceBidParams = () => { - return { - placement: 1234567, - network: '9599.1' - }; -}; - -let getNexageGetBidParams = () => { - return { - dcn: '2c9d2b50015c5ce9db6aeeed8b9500d6', - pos: 'header' - }; -}; - -let getNexagePostBidParams = () => { - return { - id: 'id-1', - imp: [{ - id: 'id-2', - banner: { - w: '100', - h: '100' - }, - tagid: 'header1' - }] - }; -}; - -let getDefaultBidRequest = () => { - return { - bidderCode: 'aol', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - bidderRequestId: '7101db09af0db2', - start: new Date().getTime(), - bids: [{ - bidder: 'aol', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - placementCode: 'foo', - params: getMarketplaceBidParams() - }] - }; -}; - -let getPixels = () => { - return ''; -}; - -describe('AolAdapter', function () { - const MARKETPLACE_URL = 'https://adserver-us.adtech.advertising.com/pubapi/3.0/'; - const NEXAGE_URL = 'https://c2shb.ssp.yahoo.com/bidRequest?'; - const ONE_DISPLAY_TTL = 60; - const ONE_MOBILE_TTL = 3600; - const SUPPORTED_USER_ID_SOURCES = { - 'admixer.net': '100', - 'adserver.org': '200', - 'adtelligent.com': '300', - 'amxdt.net': '500', - 'audigent.com': '600', - 'britepool.com': '700', - 'criteo.com': '800', - 'crwdcntrl.net': '900', - 'deepintent.com': '1000', - 'epsilon.com': '1100', - 'hcn.health': '1200', - 'id5-sync.com': '1300', - 'idx.lat': '1400', - 'intentiq.com': '1500', - 'intimatemerger.com': '1600', - 'liveintent.com': '1700', - 'liveramp.com': '1800', - 'mediawallahscript.com': '1900', - 'netid.de': '2100', - 'neustar.biz': '2200', - 'pubcid.org': '2600', - 'quantcast.com': '2700', - 'tapad.com': '2800', - 'zeotap.com': '3200' - }; - - const USER_ID_DATA = { - admixerId: SUPPORTED_USER_ID_SOURCES['admixer.net'], - adtelligentId: SUPPORTED_USER_ID_SOURCES['adtelligent.com'], - amxId: SUPPORTED_USER_ID_SOURCES['amxdt.net'], - britepoolid: SUPPORTED_USER_ID_SOURCES['britepool.com'], - criteoId: SUPPORTED_USER_ID_SOURCES['criteo.com'], - connectid: SUPPORTED_USER_ID_SOURCES['verizonmedia.com'], - dmdId: SUPPORTED_USER_ID_SOURCES['hcn.health'], - hadronId: SUPPORTED_USER_ID_SOURCES['audigent.com'], - lotamePanoramaId: SUPPORTED_USER_ID_SOURCES['crwdcntrl.net'], - deepintentId: SUPPORTED_USER_ID_SOURCES['deepintent.com'], - fabrickId: SUPPORTED_USER_ID_SOURCES['neustar.biz'], - idl_env: SUPPORTED_USER_ID_SOURCES['liveramp.com'], - IDP: SUPPORTED_USER_ID_SOURCES['zeotap.com'], - lipb: { - lipbid: SUPPORTED_USER_ID_SOURCES['liveintent.com'], - segments: ['100', '200'] - }, - tdid: SUPPORTED_USER_ID_SOURCES['adserver.org'], - id5id: { - uid: SUPPORTED_USER_ID_SOURCES['id5-sync.com'], - ext: {foo: 'bar'} - }, - idx: SUPPORTED_USER_ID_SOURCES['idx.lat'], - imuid: SUPPORTED_USER_ID_SOURCES['intimatemerger.com'], - intentIqId: SUPPORTED_USER_ID_SOURCES['intentiq.com'], - mwOpenLinkId: SUPPORTED_USER_ID_SOURCES['mediawallahscript.com'], - netId: SUPPORTED_USER_ID_SOURCES['netid.de'], - quantcastId: SUPPORTED_USER_ID_SOURCES['quantcast.com'], - publinkId: SUPPORTED_USER_ID_SOURCES['epsilon.com'], - pubcid: SUPPORTED_USER_ID_SOURCES['pubcid.org'], - tapadId: SUPPORTED_USER_ID_SOURCES['tapad.com'] - }; - - function createCustomBidRequest({bids, params} = {}) { - var bidderRequest = getDefaultBidRequest(); - if (bids && Array.isArray(bids)) { - bidderRequest.bids = bids; - } - if (params) { - bidderRequest.bids.forEach(bid => bid.params = params); - } - return bidderRequest; - } - - describe('interpretResponse()', function () { - let bidderSettingsBackup; - let bidResponse; - let bidRequest; - let logWarnSpy; - let isOneMobileBidderStub; - - beforeEach(function () { - bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; - bidRequest = { - bidderCode: 'test-bidder-code', - bidId: 'bid-id', - ttl: 1234 - }; - bidResponse = { - body: getDefaultBidResponse() - }; - logWarnSpy = sinon.spy(utils, 'logWarn'); - isOneMobileBidderStub = sinon.stub(spec, 'isOneMobileBidder'); - }); - - afterEach(function () { - $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; - logWarnSpy.restore(); - isOneMobileBidderStub.restore(); - }); - - it('should return formatted bid response with required properties', function () { - let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); - expect(formattedBidResponse).to.deep.equal({ - bidderCode: bidRequest.bidderCode, - requestId: 'bid-id', - ad: DEFAULT_AD_CONTENT, - cpm: 0.09, - width: 728, - height: 90, - creativeId: 'creative-id', - pubapiId: '245730051428950632', - currency: 'USD', - dealId: 'deal-id', - netRevenue: true, - meta: { - advertiserDomains: [] - }, - ttl: bidRequest.ttl - }); - }); - }); - - describe('buildRequests()', function () { - it('method exists and is a function', function () { - expect(spec.buildRequests).to.exist.and.to.be.a('function'); - }); - - describe('Marketplace', function () { - it('should not return request when no bids are present', function () { - let [request] = spec.buildRequests([]); - expect(request).to.be.undefined; - }); - - it('should return request for Marketplace endpoint', function () { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); - }); - - it('should return request for Marketplace via onedisplay bidder code', function () { - let bidRequest = createCustomBidRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: getMarketplaceBidParams() - }); - - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); - }); - - it('should return Marketplace request via onedisplay bidder code when' + - 'Marketplace and One Mobile GET params are present', () => { - let bidParams = Object.assign(getMarketplaceBidParams(), getNexageGetBidParams()); - let bidRequest = createCustomBidRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: bidParams - }); - - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); - }); - - it('should return Marketplace request via onedisplay bidder code when' + - 'Marketplace and One Mobile GET + POST params are present', () => { - let bidParams = Object.assign(getMarketplaceBidParams(), getNexageGetBidParams(), getNexagePostBidParams()); - let bidRequest = createCustomBidRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: bidParams - }); - - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); - }); - - it('should not resolve endpoint for onedisplay bidder code ' + - 'when only One Mobile params are present', () => { - let bidParams = Object.assign(getNexageGetBidParams(), getNexagePostBidParams()); - let bidRequest = createCustomBidRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: bidParams - }); - - let [request] = spec.buildRequests(bidRequest.bids); - expect(request).to.be.undefined; - }); - - it('should return Marketplace URL for eu region', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - region: 'eu' - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf('https://adserver-eu.adtech.advertising.com/pubapi/3.0/')) - .to.equal(0); - }); - - it('should return insecure MP URL if insecure server option is present', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - server: 'https://adserver-eu.adtech.advertising.com' - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf('https://adserver-eu.adtech.advertising.com/pubapi/3.0/')) - .to.equal(0); - }); - - it('should return a secure MP URL if relative proto server option is present', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - server: 'https://adserver-eu.adtech.advertising.com' - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf('https://adserver-eu.adtech.advertising.com/pubapi/3.0/')) - .to.equal(0); - }); - - it('should return a secure MP URL when server option without protocol is present', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - server: 'adserver-eu.adtech.advertising.com' - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf('https://adserver-eu.adtech.advertising.com/pubapi/3.0/')) - .to.equal(0); - }); - - it('should return default Marketplace URL in case of unknown region config option', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - region: 'an' - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); - }); - - it('should return url with pubapi bid option', function () { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('cmd=bid;'); - }); - - it('should return url with version 2 of pubapi', function () { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('v=2;'); - }); - - it('should return url with cache busting option', function () { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.match(/misc=\d+/); - }); - - it('should return url with default pageId and sizeId', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1' - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('/pubapi/3.0/9599.1/1234567/0/0/ADTECH;'); - }); - - it('should return url with custom pageId and sizeId when options are present', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - pageId: 1111, - sizeId: 2222 - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('/pubapi/3.0/9599.1/1234567/1111/2222/ADTECH;'); - }); - - it('should return url with default alias if alias param is missing', function () { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.match(/alias=\w+?;/); - }); - - it('should return url with custom alias if it is present', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - alias: 'desktop_articlepage_something_box_300_250' - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('alias=desktop_articlepage_something_box_300_250'); - }); - - it('should return url without bidfloor option if is is missing', function () { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).not.to.contain('bidfloor='); - }); - - it('should return url with key values if keyValues param is present', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - keyValues: { - age: 25, - height: 3.42, - test: 'key' - } - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('kvage=25;kvheight=3.42;kvtest=key'); - }); - - it('should return request object for One Display when configuration is present', function () { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.method).to.equal('GET'); - expect(request.ttl).to.equal(ONE_DISPLAY_TTL); - }); - }); - - describe('One Mobile', function () { - it('should return One Mobile url when One Mobile get params are present', function () { - let bidRequest = createCustomBidRequest({ - params: getNexageGetBidParams() - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(NEXAGE_URL); - }); - - it('should return One Mobile url with different host when host option is present', function () { - let bidParams = Object.assign({ - host: 'https://qa-hb.nexage.com' - }, getNexageGetBidParams()); - let bidRequest = createCustomBidRequest({ - params: bidParams - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('https://qa-hb.nexage.com/bidRequest?'); - }); - - it('should return One Mobile url when One Mobile and Marketplace params are present', function () { - let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); - let bidRequest = createCustomBidRequest({ - params: bidParams - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(NEXAGE_URL); - }); - - it('should return One Mobile url for onemobile bidder code ' + - 'when One Mobile GET and Marketplace params are present', () => { - let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); - let bidRequest = createCustomBidRequest({ - bids: [{ - bidder: 'onemobile' - }], - params: bidParams - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(NEXAGE_URL); - }); - - it('should not return any url for onemobile bidder code' + - 'when only Marketplace params are present', () => { - let bidRequest = createCustomBidRequest({ - bids: [{ - bidder: 'onemobile' - }], - params: getMarketplaceBidParams() - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request).to.be.undefined; - }); - - it('should return One Mobile url with required params - dcn & pos', function () { - let bidRequest = createCustomBidRequest({ - params: getNexageGetBidParams() - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(NEXAGE_URL + 'dcn=2c9d2b50015c5ce9db6aeeed8b9500d6&pos=header'); - }); - - it('should return One Mobile url with configured GPP data', function () { - let bidRequest = createCustomBidRequest({ - params: getNexageGetBidParams() - }); - bidRequest.ortb2 = { - regs: { - gpp: 'testgpp', - gpp_sid: [8] - } - } - let [request] = spec.buildRequests(bidRequest.bids, bidRequest); - expect(request.url).to.contain('gpp=testgpp&gpp_sid=8'); - }); - - it('should return One Mobile url with cmd=bid option', function () { - let bidRequest = createCustomBidRequest({ - params: getNexageGetBidParams() - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('cmd=bid'); - }); - - it('should return One Mobile url with generic params if ext option is present', function () { - let bidRequest = createCustomBidRequest({ - params: { - dcn: '54321123', - pos: 'footer-2324', - ext: { - param1: 'val1', - param2: 'val2', - param3: 'val3', - param4: 'val4' - } - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.equal('https://c2shb.ssp.yahoo.com/bidRequest?dcn=54321123&pos=footer-2324&cmd=bid' + - '¶m1=val1¶m2=val2¶m3=val3¶m4=val4'); - }); - - Object.keys(SUPPORTED_USER_ID_SOURCES).forEach(source => { - it(`should set the user ID query param for ${source}`, function () { - let bidRequest = createCustomBidRequest({ - params: getNexageGetBidParams() - }); - bidRequest.bids[0].userId = {}; - bidRequest.bids[0].userIdAsEids = createEidsArray(USER_ID_DATA); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(`&eid${source}=${encodeURIComponent(SUPPORTED_USER_ID_SOURCES[source])}`); - }); - }); - - it('should return request object for One Mobile POST endpoint when POST configuration is present', function () { - let bidConfig = getNexagePostBidParams(); - let bidRequest = createCustomBidRequest({ - params: bidConfig - }); - - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(NEXAGE_URL); - expect(request.method).to.equal('POST'); - expect(request.ttl).to.equal(ONE_MOBILE_TTL); - expect(request.data).to.deep.equal(bidConfig); - expect(request.options).to.deep.equal({ - contentType: 'application/json', - customHeaders: { - 'x-openrtb-version': '2.2' - } - }); - }); - - it('should not return request object for One Mobile POST endpoint' + - 'if required parameters are missed', () => { - let bidRequest = createCustomBidRequest({ - params: { - imp: [] - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request).to.be.undefined; - }); - }); - }); - - describe('buildOpenRtbRequestData', () => { - const bid = { - params: { - id: 'bid-id', - imp: [] - } - }; - let euConsentRequiredStub; - - beforeEach(function () { - euConsentRequiredStub = sinon.stub(spec, 'isEUConsentRequired'); - }); - - afterEach(function () { - euConsentRequiredStub.restore(); - }); - - it('returns the basic bid info when regulation data is omitted', () => { - expect(spec.buildOpenRtbRequestData(bid)).to.deep.equal({ - id: 'bid-id', - imp: [] - }); - }); - - it('returns the basic bid info with gdpr data when gdpr consent data is included', () => { - let consentData = { - gdpr: { - consentString: 'someEUConsent' - } - }; - euConsentRequiredStub.returns(true); - expect(spec.buildOpenRtbRequestData(bid, consentData)).to.deep.equal({ - id: 'bid-id', - imp: [], - regs: { - ext: { - gdpr: 1 - } - }, - user: { - ext: { - consent: 'someEUConsent' - } - } - }); - }); - - it('returns the basic bid info with CCPA data when CCPA consent data is included', () => { - let consentData = { - uspConsent: 'someUSPConsent' - }; - expect(spec.buildOpenRtbRequestData(bid, consentData)).to.deep.equal({ - id: 'bid-id', - imp: [], - regs: { - ext: { - us_privacy: 'someUSPConsent' - } - } - }); - }); - - it('returns the basic bid info with GDPR and CCPA data when GDPR and CCPA consent data is included', () => { - let consentData = { - gdpr: { - consentString: 'someEUConsent' - }, - uspConsent: 'someUSPConsent' - }; - euConsentRequiredStub.returns(true); - expect(spec.buildOpenRtbRequestData(bid, consentData)).to.deep.equal({ - id: 'bid-id', - imp: [], - regs: { - ext: { - gdpr: 1, - us_privacy: 'someUSPConsent' - } - }, - user: { - ext: { - consent: 'someEUConsent' - } - } - }); - }); - - it('returns the bid object with eid array populated with PB set eids', () => { - let userIdBid = Object.assign({ - userId: {} - }, bid); - userIdBid.userIdAsEids = createEidsArray(USER_ID_DATA); - expect(spec.buildOpenRtbRequestData(userIdBid)).to.deep.equal({ - id: 'bid-id', - imp: [], - user: { - ext: { - eids: userIdBid.userIdAsEids - } - } - }); - }); - }); - - describe('getUserSyncs()', function () { - let serverResponses; - let bidResponse; - - beforeEach(function () { - bidResponse = getDefaultBidResponse(); - bidResponse.ext = { - pixels: getPixels() - }; - - serverResponses = [ - {body: bidResponse} - ]; - }); - - it('should return user syncs if pixels are present in the response', function () { - let userSyncs = spec.getUserSyncs({}, serverResponses); - - expect(userSyncs).to.deep.equal([ - {type: 'image', url: 'img.org'}, - {type: 'iframe', url: 'pixels1.org'} - ]); - }); - - it('should not return user syncs if pixels are not present', function () { - bidResponse.ext.pixels = null; - let userSyncs = spec.getUserSyncs({}, serverResponses); - - expect(userSyncs).to.deep.equal([]); - }); - }); - - describe('isOneMobileBidder()', function () { - it('should return false when when bidderCode is not present', () => { - expect(spec.isOneMobileBidder(null)).to.be.false; - }); - - it('should return false for unknown bidder code', function () { - expect(spec.isOneMobileBidder('unknownBidder')).to.be.false; - }); - - it('should return true for aol bidder code', function () { - expect(spec.isOneMobileBidder('aol')).to.be.true; - }); - - it('should return true for one mobile bidder code', function () { - expect(spec.isOneMobileBidder('onemobile')).to.be.true; - }); - }); - - describe('isEUConsentRequired()', function () { - it('should return false when consentData object is not present', function () { - expect(spec.isEUConsentRequired(null)).to.be.false; - }); - - it('should return true when gdprApplies equals true and consentString is not present', function () { - let consentData = { - gdpr: { - consentString: null, - gdprApplies: true - } - }; - - expect(spec.isEUConsentRequired(consentData)).to.be.true; - }); - - it('should return false when consentString is present and gdprApplies equals false', function () { - let consentData = { - gdpr: { - consentString: 'consent-string', - gdprApplies: false - } - }; - - expect(spec.isEUConsentRequired(consentData)).to.be.false; - }); - - it('should return true when consentString is present and gdprApplies equals true', function () { - let consentData = { - gdpr: { - consentString: 'consent-string', - gdprApplies: true - } - }; - - expect(spec.isEUConsentRequired(consentData)).to.be.true; - }); - }); - - describe('formatMarketplaceDynamicParams()', function () { - let formatConsentDataStub; - let formatKeyValuesStub; - - beforeEach(function () { - formatConsentDataStub = sinon.stub(spec, 'formatConsentData'); - formatKeyValuesStub = sinon.stub(spec, 'formatKeyValues'); - }); - - afterEach(function () { - formatConsentDataStub.restore(); - formatKeyValuesStub.restore(); - }); - - it('should return empty string when params are not present', function () { - expect(spec.formatMarketplaceDynamicParams()).to.be.equal(''); - }); - - it('should return formatted EU consent params when formatConsentData returns GDPR data', function () { - formatConsentDataStub.returns({ - euconsent: 'test-consent', - gdpr: 1 - }); - expect(spec.formatMarketplaceDynamicParams()).to.be.equal('euconsent=test-consent;gdpr=1;'); - }); - - it('should return formatted US privacy params when formatConsentData returns USP data', function () { - formatConsentDataStub.returns({ - us_privacy: 'test-usp-consent' - }); - expect(spec.formatMarketplaceDynamicParams()).to.be.equal('us_privacy=test-usp-consent;'); - }); - - it('should return formatted EU and USP consent params when formatConsentData returns all data', function () { - formatConsentDataStub.returns({ - euconsent: 'test-consent', - gdpr: 1, - us_privacy: 'test-usp-consent' - }); - expect(spec.formatMarketplaceDynamicParams()).to.be.equal( - 'euconsent=test-consent;gdpr=1;us_privacy=test-usp-consent;'); - }); - - it('should return formatted gpp privacy params when formatConsentData returns GPP data', function () { - formatConsentDataStub.returns({ - gpp: 'gppstring', - gpp_sid: [6, 7] - }); - expect(spec.formatMarketplaceDynamicParams()).to.be.equal('gpp=gppstring;gpp_sid=6%2C7;'); - }); - - it('should return formatted params when formatKeyValues returns data', function () { - formatKeyValuesStub.returns({ - param1: 'val1', - param2: 'val2', - param3: 'val3' - }); - expect(spec.formatMarketplaceDynamicParams()).to.be.equal('param1=val1;param2=val2;param3=val3;'); - }); - }); - - describe('formatOneMobileDynamicParams()', function () { - let euConsentRequiredStub; - let secureProtocolStub; - - beforeEach(function () { - euConsentRequiredStub = sinon.stub(spec, 'isEUConsentRequired'); - secureProtocolStub = sinon.stub(spec, 'isSecureProtocol'); - }); - - afterEach(function () { - euConsentRequiredStub.restore(); - secureProtocolStub.restore(); - }); - - it('should return empty string when params are not present', function () { - expect(spec.formatOneMobileDynamicParams()).to.be.equal(''); - }); - - it('should return formatted params when params are present', function () { - let params = { - param1: 'val1', - param2: 'val2', - param3: 'val3' - }; - expect(spec.formatOneMobileDynamicParams(params)).to.contain('¶m1=val1¶m2=val2¶m3=val3'); - }); - - it('should return formatted gdpr params when isEUConsentRequired returns true', function () { - let consentData = { - gdpr: { - consentString: 'test-consent' - } - }; - euConsentRequiredStub.returns(true); - expect(spec.formatOneMobileDynamicParams({}, consentData)).to.be.equal('&gdpr=1&euconsent=test-consent'); - }); - - it('should return formatted US privacy params when consentData contains USP data', function () { - let consentData = { - uspConsent: 'test-usp-consent' - }; - expect(spec.formatMarketplaceDynamicParams({}, consentData)).to.be.equal('us_privacy=test-usp-consent;'); - }); - - it('should return formatted EU and USP consent params when consentData contains gdpr and usp values', function () { - euConsentRequiredStub.returns(true); - let consentData = { - gdpr: { - consentString: 'test-consent' - }, - uspConsent: 'test-usp-consent' - }; - expect(spec.formatMarketplaceDynamicParams({}, consentData)).to.be.equal( - 'gdpr=1;euconsent=test-consent;us_privacy=test-usp-consent;'); - }); - - it('should return formatted secure param when isSecureProtocol returns true', function () { - secureProtocolStub.returns(true); - expect(spec.formatOneMobileDynamicParams()).to.be.equal('&secure=1'); - }); - }); -}); diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index 773c9925d58..98d07575ee7 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -321,9 +321,9 @@ describe('ApacdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.eids).to.deep.equal(bidRequest[0].userIdAsEids) }); - it('should return a properly formatted request with geo defined', function () { + it('should fail to return a properly formatted request with geo defined', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); - expect(bidRequests.data.geo).to.deep.equal(bidRequest[0].params.geo) + expect(bidRequests.data.geo).to.not.deep.equal(bidRequest[0].params.geo) }); it('should return a properly formatted request with us_privacy included', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 1603c6e9397..13ef31a68d4 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -18,6 +18,18 @@ describe('AppNexusAdapter', function () { }); }); + function expectKeywords(actual, expected) { + expect(actual.length).to.equal(expected.length); + actual.forEach(el => { + const match = expected.find(ob => ob.key === el.key); + if (el.value) { + expect(el.value).to.have.members(match.value); + } else { + expect(match.value).to.not.exist; + } + }) + } + describe('isBidRequestValid', function () { let bid = { 'bidder': 'appnexus', @@ -737,7 +749,7 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest], bidderRequest); const payload = JSON.parse(request.data); - expect(payload.keywords).to.deep.equal([{ + expectKeywords(payload.keywords, [{ 'key': 'gender', 'value': ['m'] }, { @@ -772,6 +784,69 @@ describe('AppNexusAdapter', function () { config.getConfig.restore(); }); + it('adds ortb2 segments to auction request as keywords', function() { + let bidRequest = Object.assign({}, bidRequests[0]); + const bidderRequest = { + ortb2: { + site: { + keywords: 'drill', + content: { + data: [{ + name: 'siteseg1', + ext: { + segtax: 540 + }, + segment: [{ + id: 's123', + }, { + id: 's234' + }] + }, { + name: 'sitseg2', + ext: { + segtax: 1 + }, + segment: [{ + id: 'unknown' + }] + }, { + name: 'siteseg3', + ext: { + segtax: 526 + }, + segment: [{ + id: 'dog' + }] + }] + } + }, + user: { + data: [{ + name: 'userseg1', + ext: { + segtax: 526 + }, + segment: [{ + id: 'cat' + }] + }] + } + } + }; + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expectKeywords(payload.keywords, [{ + 'key': 'drill' + }, { + 'key': '1plusX', + 'value': ['cat', 'dog'] + }, { + 'key': 'perid', + 'value': ['s123', 's234'] + }]); + }); + if (FEATURES.NATIVE) { it('should attach native params to the request', function () { let bidRequest = Object.assign({}, @@ -872,26 +947,28 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); - expect(payload.tags[0].keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'singleArrNum', - 'value': ['5'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'singleValNum', - 'value': ['123'] - }, { - 'key': 'emptyStr' - }, { - 'key': 'emptyArr' - }]); + expectKeywords(payload.tags[0].keywords, [ + { + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' + } + ]) }); it('should convert adUnit ortb2 keywords (when there are no bid param keywords) to proper form and attaches to request', function () { @@ -911,7 +988,7 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); - expect(payload.tags[0].keywords).to.deep.equal([{ + expectKeywords(payload.tags[0].keywords, [{ 'key': 'ortb2', 'value': ['yes'] }, { @@ -955,7 +1032,7 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); - expect(payload.tags[0].keywords).to.deep.equal([{ + expectKeywords(payload.tags[0].keywords, [{ 'key': 'single', 'value': ['val'] }, { @@ -1451,16 +1528,13 @@ describe('AppNexusAdapter', function () { }) describe('interpretResponse', function () { - let bfStub; let bidderSettingsStorage; before(function () { - bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); bidderSettingsStorage = $$PREBID_GLOBAL$$.bidderSettings; }); after(function () { - bfStub.restore(); $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsStorage; }); @@ -1707,7 +1781,6 @@ describe('AppNexusAdapter', function () { } }] }; - bfStub.returns('1'); let result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(result[0]).to.have.property('vastUrl'); diff --git a/test/spec/modules/astraoneBidAdapter_spec.js b/test/spec/modules/astraoneBidAdapter_spec.js index 0e545081869..80d6dcdf627 100644 --- a/test/spec/modules/astraoneBidAdapter_spec.js +++ b/test/spec/modules/astraoneBidAdapter_spec.js @@ -8,7 +8,11 @@ function getSlotConfigs(mediaTypes, params) { bidId: '2df8c0733f284e', bidder: 'astraone', mediaTypes: mediaTypes, - transactionId: '31a58515-3634-4e90-9c96-f86196db1459' + ortb2Imp: { + ext: { + tid: '31a58515-3634-4e90-9c96-f86196db1459' + } + } } } diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index addd4304c7d..4e30b822a61 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -695,7 +695,6 @@ describe('BeachfrontAdapter', function () { const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); expect(bidResponse).to.deep.equal({ requestId: bidRequest.bidId, - bidderCode: spec.code, cpm: serverResponse.bidPrice, creativeId: serverResponse.crid, vastUrl: serverResponse.url, diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js index 9d3c00b368b..22c48c2d182 100644 --- a/test/spec/modules/beopBidAdapter_spec.js +++ b/test/spec/modules/beopBidAdapter_spec.js @@ -130,6 +130,30 @@ describe('BeOp Bid Adapter tests', () => { expect(payload.url).to.exist; // check that the protocol is added correctly expect(payload.url).to.equal('http://test.te'); + expect(payload.psegs).to.not.exist; + }); + + it('should call the endpoint with psegs data if any', function () { + let bidderRequest = + { + 'ortb2': { + 'user': { + 'ext': { + 'data': { + 'permutive': [1234, 5678, 910] + } + } + } + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.psegs).to.exist; + expect(payload.psegs).to.include(1234); + expect(payload.psegs).to.include(5678); + expect(payload.psegs).to.include(910); + expect(payload.psegs).to.not.include(1); }); it('should not prepend the protocol in page url if already present', function () { diff --git a/test/spec/modules/big-richmediaBidAdapter_spec.js b/test/spec/modules/big-richmediaBidAdapter_spec.js index f01e261ef9f..c3a9a8ef6c1 100644 --- a/test/spec/modules/big-richmediaBidAdapter_spec.js +++ b/test/spec/modules/big-richmediaBidAdapter_spec.js @@ -131,16 +131,6 @@ describe('bigRichMediaAdapterTests', function () { }); describe('interpretResponse', function () { - let bfStub; - - before(function() { - bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); - }); - - after(function() { - bfStub.restore(); - }); - const response = { 'version': '3.0.0', 'tags': [ diff --git a/test/spec/modules/bluebillywigBidAdapter_spec.js b/test/spec/modules/bluebillywigBidAdapter_spec.js index 0826acc7f29..4b58e3507db 100644 --- a/test/spec/modules/bluebillywigBidAdapter_spec.js +++ b/test/spec/modules/bluebillywigBidAdapter_spec.js @@ -1,10 +1,8 @@ -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'; +import {expect} from 'chai'; +import {spec} from 'modules/bluebillywigBidAdapter.js'; +import {deepAccess, deepClone} from 'src/utils.js'; +import {config} from 'src/config.js'; +import {VIDEO} from 'src/mediaTypes.js'; const BB_CONSTANTS = { BIDDER_CODE: 'bluebillywig', @@ -254,7 +252,11 @@ describe('BlueBillywigAdapter', () => { const baseValidBidRequests = [baseValidBid]; const validBidderRequest = { - auctionId: '12abc345-67d8-9012-e345-6f78901a2b34', + ortb2: { + source: { + tid: '12abc345-67d8-9012-e345-6f78901a2b34', + } + }, auctionStart: 1585918458868, bidderCode: BB_CONSTANTS.BIDDER_CODE, bidderRequestId: '1a2345b67c8d9e0', @@ -293,9 +295,9 @@ describe('BlueBillywigAdapter', () => { const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); const payload = JSON.parse(request.data); - expect(payload.id).to.equal(validBidderRequest.auctionId); + expect(payload.id).to.exist; expect(payload.source).to.be.an('object'); - expect(payload.source.tid).to.equal(validBidderRequest.auctionId); + expect(payload.source.tid).to.equal(validBidderRequest.ortb2.source.tid); expect(payload.tmax).to.equal(BB_CONSTANTS.DEFAULT_TIMEOUT); expect(payload.imp).to.be.an('array'); expect(payload.test).to.be.a('number'); diff --git a/test/spec/modules/browsiBidAdapter_spec.js b/test/spec/modules/browsiBidAdapter_spec.js index 8892396adac..9693972fd7f 100644 --- a/test/spec/modules/browsiBidAdapter_spec.js +++ b/test/spec/modules/browsiBidAdapter_spec.js @@ -64,7 +64,11 @@ describe('browsi Bid Adapter Test', function () { 'sizes': [640, 480], 'bidId': '12345678', 'requestId': '1234567-3456-4562-7689-98765434A', - 'transactionId': '1234567-3456-4562-7689-98765434B', + ortb2Imp: { + ext: { + tid: '1234567-3456-4562-7689-98765434B', + } + }, 'schain': {}, 'mediaTypes': {video: {playerSize: [640, 480]}} } @@ -112,7 +116,7 @@ describe('browsi Bid Adapter Test', function () { video: {playerSize: [640, 480]}, aUCode: inputRequest.adUnitCode, aID: inputRequest.auctionId, - tID: inputRequest.transactionId, + tID: inputRequest.ortb2Imp.ext.tid, schain: inputRequest.schain, params: inputRequest.params } @@ -157,7 +161,6 @@ describe('browsi Bid Adapter Test', function () { const actualBidResponse = bidResponses[0]; const expectedBidResponse = { requestId: bidRequest.data.bidId, - bidderCode: 'browsi', bidId: 'bidId1', width: 300, height: 250, diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/cadentApertureMXBidAdapter_spec.js similarity index 97% rename from test/spec/modules/emx_digitalBidAdapter_spec.js rename to test/spec/modules/cadentApertureMXBidAdapter_spec.js index d80d0f3e875..091a8105354 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/cadentApertureMXBidAdapter_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; -import { spec } from 'modules/emx_digitalBidAdapter.js'; +import { spec } from 'modules/cadentApertureMXBidAdapter.js'; import * as utils from 'src/utils.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -describe('emx_digital Adapter', function () { +describe('cadent_aperture_mx Adapter', function () { describe('callBids', function () { const adapter = newBidder(spec); it('exists and is a function', function () { @@ -14,7 +14,7 @@ describe('emx_digital Adapter', function () { describe('isBidRequestValid', function () { describe('banner request validity', function () { let bid = { - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251' }, @@ -33,7 +33,7 @@ describe('emx_digital Adapter', function () { 'auctionId': '1d1a01234a475' }; let badBid = { - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251' }, @@ -95,7 +95,7 @@ describe('emx_digital Adapter', function () { describe('video request validity', function () { let bid = { - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251', 'video': {} @@ -116,7 +116,7 @@ describe('emx_digital Adapter', function () { 'auctionId': '1d1a01234a475' }; let noInstreamBid = { - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251', 'video': { @@ -139,7 +139,7 @@ describe('emx_digital Adapter', function () { }; let outstreamBid = { - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251', 'video': {} @@ -168,7 +168,7 @@ describe('emx_digital Adapter', function () { it('should contain tagid param', function () { expect(spec.isBidRequestValid({ - bidder: 'emx_digital', + bidder: 'cadent_aperture_mx', params: {}, mediaTypes: { banner: { @@ -177,7 +177,7 @@ describe('emx_digital Adapter', function () { } })).to.equal(false); expect(spec.isBidRequestValid({ - bidder: 'emx_digital', + bidder: 'cadent_aperture_mx', params: { tagid: '' }, @@ -188,7 +188,7 @@ describe('emx_digital Adapter', function () { } })).to.equal(false); expect(spec.isBidRequestValid({ - bidder: 'emx_digital', + bidder: 'cadent_aperture_mx', params: { tagid: '123' }, @@ -204,7 +204,7 @@ describe('emx_digital Adapter', function () { describe('buildRequests', function () { let bidderRequest = { - 'bidderCode': 'emx_digital', + 'bidderCode': 'cadent_aperture_mx', 'auctionId': 'e19f1eff-8b27-42a6-888d-9674e5a6130c', 'bidderRequestId': '22edbae3120bf6', 'timeout': 1500, @@ -216,7 +216,7 @@ describe('emx_digital Adapter', function () { 'ref': 'https://referrer.com' }, 'bids': [{ - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251' }, @@ -464,7 +464,7 @@ describe('emx_digital Adapter', function () { describe('interpretResponse', function () { let bid = { - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251', 'video': {} @@ -488,7 +488,7 @@ describe('emx_digital Adapter', function () { const bid_outstream = { 'bidderRequest': { 'bids': [{ - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25251', 'video': {} @@ -508,7 +508,7 @@ describe('emx_digital Adapter', function () { 'bidderRequestId': '22edbae3120bf6', 'auctionId': '1d1a01234a475' }, { - 'bidder': 'emx_digital', + 'bidder': 'cadent_aperture_mx', 'params': { 'tagid': '25252', 'video': {} @@ -700,7 +700,7 @@ describe('emx_digital Adapter', function () { it('should not throw an error when decoding an improperly encoded adm', function () { const badAdmServerResponse = utils.deepClone(serverResponse); badAdmServerResponse.seatbid[0].bid[0].adm = '\\<\\/script\\>'; - badAdmServerResponse.seatbid[1].bid[0].adm = '%3F%%3Demx%3C3prebid'; + badAdmServerResponse.seatbid[1].bid[0].adm = '%3F%%3Dcadent%3C3prebid'; assert.doesNotThrow(() => spec.interpretResponse({ body: badAdmServerResponse diff --git a/test/spec/modules/captifyRtdProvider_spec.js b/test/spec/modules/captifyRtdProvider_spec.js deleted file mode 100644 index 2e1052e000f..00000000000 --- a/test/spec/modules/captifyRtdProvider_spec.js +++ /dev/null @@ -1,253 +0,0 @@ -import {addSegmentData, captifySubmodule, getMatchingBidders, setCaptifyTargeting} from 'modules/captifyRtdProvider.js'; -import {server} from 'test/mocks/xhr.js'; -import {config} from 'src/config.js'; -import {deepAccess} from '../../../src/utils'; - -const responseHeader = {'Content-Type': 'application/json'}; -const defaultRequestUrl = 'https://live-classification.cpx.to/prebid-segments'; - -describe('captifyRtdProvider', function () { - describe('init function', function () { - it('successfully instantiates, when configured properly', function () { - const config = { - params: { - pubId: 123456, - bidders: ['appnexus'], - } - }; - expect(captifySubmodule.init(config, null)).to.equal(true); - }); - - it('return false on init, when config is invalid', function () { - const config = { - params: {} - }; - expect(captifySubmodule.init(config, null)).to.equal(false); - expect(captifySubmodule.init(null, null)).to.equal(false); - }); - - it('return false on init, when pubId is absent', function () { - const config = { - params: { - bidders: ['appnexus'], - } - }; - expect(captifySubmodule.init(config, null)).to.equal(false); - expect(captifySubmodule.init(null, null)).to.equal(false); - }); - - it('return false on init, when bidders is empty array', function () { - const config = { - params: { - bidders: [], - pubId: 123, - } - }; - expect(captifySubmodule.init(config, null)).to.equal(false); - expect(captifySubmodule.init(null, null)).to.equal(false); - }); - }); - - describe('addSegmentData function', function () { - it('adds segment data', function () { - config.resetConfig(); - - let data = { - xandr: [111111, 222222], - }; - - addSegmentData(['appnexus'], data); - expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.eql(data['xandr']); - }); - }); - - describe('getMatchingBidders function', function () { - it('returns only bidders that used within adUnits', function () { - const moduleConfig = { - params: { - pubId: 123, - bidders: ['appnexus', 'pubmatic'], - } - }; - let reqBidsConfigObj = { - adUnits: [{ - bids: [{ - bidder: 'appnexus', - params: { - placementId: 13144370 - } - }] - }] - }; - - let matchedBidders = getMatchingBidders(moduleConfig, reqBidsConfigObj); - expect(matchedBidders).to.eql(['appnexus']); - }); - - it('return empty result, when there are no bidders configured for adUnits', function () { - const moduleConfig = { - params: { - pubId: 123, - bidders: ['pubmatic'], - } - }; - let reqBidsConfigObj = { - adUnits: [{ - bids: [] - }] - }; - expect(getMatchingBidders(moduleConfig, reqBidsConfigObj)).to.be.empty; - }); - - it('return empty result, when there are no bidders matched', function () { - const moduleConfig = { - params: { - pubId: 123, - bidders: ['pubmatic'], - } - }; - let reqBidsConfigObj = { - adUnits: [{ - bids: [{ - params: { - bidder: 'appnexus', - placementId: 13144370, - } - }] - }] - }; - expect(getMatchingBidders(moduleConfig, reqBidsConfigObj)).to.be.empty; - }); - - it('return empty result, when there are no adUnits with bidders', function () { - const moduleConfig = { - params: { - pubId: 123, - bidders: ['pubmatic'], - } - }; - let reqBidsConfigObj = { - adUnits: [{ - bids: [{ - params: { - placementId: 13144370, - } - }] - }] - }; - expect(getMatchingBidders(moduleConfig, reqBidsConfigObj)).to.be.empty; - }); - }); - - describe('integration test with mock live-classification response', function () { - const moduleConfig = { - params: { - pubId: 123456, - bidders: ['appnexus'], - } - }; - - const reqBidsConfigObj = { - adUnits: [{ - bids: [{ - bidder: 'appnexus', - params: { - placementId: 13144370 - } - }, { - bidder: 'other' - }] - }] - }; - - const expectedUrlParam = 'http://localhost:9876/context.html'; - - it('gets data from async request and adds segment data', function () { - config.resetConfig(); - let data = {xandr: [111111, 222222]}; - const callbackSpy = sinon.spy(); - setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); - let request = server.requests[0]; - let requestBody = JSON.parse(server.requests[0].requestBody); - expect(request.url).to.be.eq(defaultRequestUrl); - expect(requestBody['pubId']).to.eq(moduleConfig.params.pubId); - expect(requestBody['url']).to.eq(expectedUrlParam); - request.respond(200, responseHeader, JSON.stringify(data)); - expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.eql(data['xandr']); - expect(callbackSpy.calledOnce).to.be.true; - }); - - it('do not send classification request, if no matching adUnits on page', function () { - config.resetConfig(); - let reqBidsConfigObj = { - adUnits: [{ - bids: [ - {bidder: 'pubmatic'} - ] - }] - }; - const callbackSpy = sinon.spy(); - setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); - expect(server.requests).to.be.empty; - }); - - it('gets data from async request and adds segment data, using URL from config', function () { - config.resetConfig(); - let data = {xandr: [111111, 222222]}; - const callbackSpy = sinon.spy(); - const testUrl = 'http://my-test-server.com/path'; - const conf = { - params: { - url: testUrl, - pubId: 123456, - bidders: ['appnexus'], - } - }; - setCaptifyTargeting(reqBidsConfigObj, callbackSpy, conf, {}); - let request = server.requests[0]; - let requestBody = JSON.parse(server.requests[0].requestBody); - expect(request.url).to.be.eq(testUrl); - expect(requestBody['pubId']).to.eq(conf.params.pubId); - expect(requestBody['url']).to.eq(expectedUrlParam); - request.respond(200, responseHeader, JSON.stringify(data)); - expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.eql(data['xandr']); - expect(callbackSpy.calledOnce).to.be.true; - }); - - it('do not set anything, in case server responded with 202', function () { - config.resetConfig(); - const callbackSpy = sinon.spy(); - setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); - let request = server.requests[0]; - let requestBody = JSON.parse(server.requests[0].requestBody); - expect(request.url).to.be.eq(defaultRequestUrl); - expect(requestBody['pubId']).to.eq(moduleConfig.params.pubId); - expect(requestBody['url']).to.eq(expectedUrlParam); - request.respond(202, responseHeader, ''); - expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.be.undefined; - expect(callbackSpy.calledOnce).to.be.true; - }); - - it('do not set anything, in case server responded with error', function () { - config.resetConfig(); - const callbackSpy = sinon.spy(); - setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); - let request = server.requests[0]; - expect(request.url).to.be.eq(defaultRequestUrl); - request.respond(500, null, ''); - expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.be.undefined; - expect(callbackSpy.calledOnce).to.be.true; - }); - - it('do not set anything, in case request error', function () { - config.resetConfig(); - const callbackSpy = sinon.spy(); - setCaptifyTargeting(reqBidsConfigObj, callbackSpy, moduleConfig, {}); - let request = server.requests[0]; - expect(request.url).to.be.eq(defaultRequestUrl); - request.abort('test error'); - expect(deepAccess(config.getConfig(), 'appnexusAuctionKeywords.captify_segments')).to.be.undefined; - expect(callbackSpy.calledOnce).to.be.true; - }); - }); -}); diff --git a/test/spec/modules/ccxBidAdapter_spec.js b/test/spec/modules/ccxBidAdapter_spec.js index d346a14d38a..cbae441e7e7 100644 --- a/test/spec/modules/ccxBidAdapter_spec.js +++ b/test/spec/modules/ccxBidAdapter_spec.js @@ -81,7 +81,7 @@ describe('ccxAdapter', function () { }); it('Valid bid request - default', function () { - let response = spec.buildRequests(bids, {bids}); + let response = spec.buildRequests(bids, {bids, bidderRequestId: 'id'}); expect(response).to.be.not.empty; expect(response.data).to.be.not.empty; diff --git a/test/spec/modules/cleanmedianetBidAdapter_spec.js b/test/spec/modules/cleanmedianetBidAdapter_spec.js index a19cdf7fd35..3c73dac07de 100644 --- a/test/spec/modules/cleanmedianetBidAdapter_spec.js +++ b/test/spec/modules/cleanmedianetBidAdapter_spec.js @@ -310,7 +310,7 @@ describe('CleanmedianetAdapter', () => { response = spec.buildRequests([bidRequest], bidRequest)[0]; expect(response.method).to.equal('POST'); expect(response.url).to.match(new RegExp(`^https://bidder\\.cleanmediaads\\.com/r/${supplyPartnerId}/bidr\\?rformat=open_rtb&reqformat=rtb_json&bidder=prebid$`, 'g')); - expect(response.data.id).to.equal(bidRequest.auctionId); + expect(response.data.id).to.equal(bidRequest.bidId); const bidRequestWithEndpoint = utils.deepClone(bidRequest); bidRequestWithEndpoint.params.rtbEndpoint = 'https://bidder.cleanmediaads.com/a12'; response = spec.buildRequests([bidRequestWithEndpoint], bidRequest)[0]; @@ -330,7 +330,7 @@ describe('CleanmedianetAdapter', () => { expect(response.data.site.page).to.equal('http://www.test.com/page.html'); expect(response.data.site.ref).to.equal('http://referrer.com'); expect(response.data.imp.length).to.equal(1); - expect(response.data.imp[0].id).to.equal(bidRequest.transactionId); + expect(response.data.imp[0].id).to.equal(bidRequest.bidId); expect(response.data.imp[0].instl).to.equal(0); expect(response.data.imp[0].tagid).to.equal(bidRequest.adUnitCode); expect(response.data.imp[0].bidfloor).to.equal(0); diff --git a/test/spec/modules/codefuelBidAdapter_spec.js b/test/spec/modules/codefuelBidAdapter_spec.js index 354cbe63ffa..6123f768d88 100644 --- a/test/spec/modules/codefuelBidAdapter_spec.js +++ b/test/spec/modules/codefuelBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec} from 'modules/codefuelBidAdapter.js'; import {config} from 'src/config.js'; +import * as utils from 'src/utils.js'; import {server} from 'test/mocks/xhr'; const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/92.0.4515.159 Safari/537.36'; @@ -9,6 +10,13 @@ const setUADefault = () => { window.navigator.__defineGetter__('userAgent', func const setUAMock = () => { window.navigator.__defineGetter__('userAgent', function () { return USER_AGENT }) }; describe('Codefuel Adapter', function () { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + afterEach(() => { + sandbox.restore(); + }) describe('Bid request and response', function () { const commonBidRequest = { bidder: 'codefuel', @@ -137,6 +145,7 @@ describe('Codefuel Adapter', function () { const commonBidderRequest = { timeout: 500, + bidderRequestId: 'mock-uuid', auctionId: '12043683-3254-4f74-8934-f941b085579e', refererInfo: { page: 'https://example.com/', @@ -157,7 +166,7 @@ describe('Codefuel Adapter', function () { devicetype: 2, ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/92.0.4515.159 Safari/537.36' }, - id: '12043683-3254-4f74-8934-f941b085579e', + id: 'mock-uuid', imp: [ { banner: { diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index adb9137d892..b8c872d879d 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -19,12 +19,12 @@ describe('ColossussspAdapter', function () { }, ortb2Imp: { ext: { + tid: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', data: { pbadslot: '/19968336/prebid_cache_video_adunit' } } }, - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', schain: { ver: '1.0', complete: 1, @@ -283,6 +283,35 @@ describe('ColossussspAdapter', function () { }); }); + describe('gpp consent', function () { + it('bidderRequest.gppConsent', () => { + bidderRequest.gppConsent = { + gppString: 'abc123', + applicableSections: [8] + }; + + let serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + + delete bidderRequest.gppConsent; + }) + + it('bidderRequest.ortb2.regs.gpp', () => { + bidderRequest.ortb2.regs = bidderRequest.ortb2.regs || {}; + bidderRequest.ortb2.regs.gpp = 'abc123'; + bidderRequest.ortb2.regs.gpp_sid = [8]; + + let serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + }) + }); + describe('interpretResponse', function () { let resObject = { body: [{ diff --git a/test/spec/modules/conceptxBidAdapter_spec.js b/test/spec/modules/conceptxBidAdapter_spec.js new file mode 100644 index 00000000000..349ee765b71 --- /dev/null +++ b/test/spec/modules/conceptxBidAdapter_spec.js @@ -0,0 +1,136 @@ +// import or require modules necessary for the test, e.g.: +import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' +import { spec } from 'modules/conceptxBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +// import { config } from 'src/config.js'; + +describe('conceptxBidAdapter', function () { + const URL = 'https://conceptx.cncpt-central.com/openrtb'; + + // before(() => { + + // }); + + // after(() => { + // // $$PREBID_GLOBAL$$.bidderSettings = {}; + // }); + + // afterEach(function () { + // config.resetConfig(); + // }); + + const ENDPOINT_URL = `${URL}`; + const ENDPOINT_URL_CONSENT = `${URL}?gdpr_applies=true&consentString=ihaveconsented`; + const adapter = newBidder(spec); + + const bidderRequests = [ + { + bidId: '123', + bidder: 'conceptx', + params: { + site: 'example', + adunit: 'some-id-3' + }, + mediaTypes: { + banner: { + sizes: [[930, 180]], + } + }, + } + ] + + const singleBidRequest = { + bid: [ + { + bidId: '123', + } + ] + } + + const serverResponse = { + body: { + 'bidResponses': [ + { + 'ads': [ + { + 'referrer': 'http://localhost/prebidpage_concept_bidder.html', + 'ttl': 360, + 'html': '

DUMMY

', + 'requestId': '214dfadd1f8826', + 'cpm': 46, + 'currency': 'DKK', + 'width': 930, + 'height': 180, + 'creativeId': 'FAKE-ID', + 'meta': { + 'mediaType': 'banner' + }, + 'netRevenue': true, + 'destinationUrls': { + 'destination': 'https://concept.dk' + } + } + ], + 'matchedAdCount': 1, + 'targetId': '214dfadd1f8826' + } + ] + } + } + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bidderRequests[0])).to.equal(true); + }); + }); + + describe('buildRequests', function () { + it('Test requests', function () { + const request = spec.buildRequests(bidderRequests, {}); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('data'); + const bid = JSON.parse(request[0].data).adUnits[0] + expect(bid.site).to.equal('example'); + expect(bid.adunit).to.equal('some-id-3'); + expect(JSON.stringify(bid.dimensions)).to.equal(JSON.stringify([ + [930, 180]])); + }); + }); + + describe('user privacy', function () { + it('should NOT send GDPR Consent data if gdprApplies equals undefined', function () { + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'iDoNotConsent' } }); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + }); + it('should send GDPR Consent data if gdprApplies', function () { + let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'ihaveconsented' } }); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); + }); + }); + + describe('interpretResponse', function () { + it('should return valid response when passed valid server response', function () { + const interpretedResponse = spec.interpretResponse(serverResponse, singleBidRequest); + const ad = serverResponse.body.bidResponses[0].ads[0] + expect(interpretedResponse).to.have.lengthOf(1); + expect(interpretedResponse[0].cpm).to.equal(ad.cpm); + expect(interpretedResponse[0].width).to.equal(Number(ad.width)); + expect(interpretedResponse[0].height).to.equal(Number(ad.height)); + expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); + expect(interpretedResponse[0].currency).to.equal(ad.currency); + expect(interpretedResponse[0].netRevenue).to.equal(true); + expect(interpretedResponse[0].ad).to.equal(ad.html); + expect(interpretedResponse[0].ttl).to.equal(360); + }); + }); +}); diff --git a/test/spec/modules/concertBidAdapter_spec.js b/test/spec/modules/concertBidAdapter_spec.js index f5c807b4703..96c98e5e5a2 100644 --- a/test/spec/modules/concertBidAdapter_spec.js +++ b/test/spec/modules/concertBidAdapter_spec.js @@ -48,7 +48,11 @@ describe('ConcertAdapter', function () { }, adUnitCode: 'desktop_leaderboard_variable', bidId: 'foo', - transactionId: '', + ortb2Imp: { + ext: { + tid: '' + } + }, sizes: [[1030, 590]] } ]; diff --git a/test/spec/modules/connectIdSystem_spec.js b/test/spec/modules/connectIdSystem_spec.js index 405e7c5e8b9..5376ba60886 100644 --- a/test/spec/modules/connectIdSystem_spec.js +++ b/test/spec/modules/connectIdSystem_spec.js @@ -2,7 +2,7 @@ import {expect} from 'chai'; import {connectIdSubmodule, storage} from 'modules/connectIdSystem.js'; import {server} from '../../mocks/xhr'; import {parseQS, parseUrl} from 'src/utils.js'; -import {uspDataHandler} from 'src/adapterManager.js'; +import {uspDataHandler, gppDataHandler} from 'src/adapterManager.js'; const TEST_SERVER_URL = 'http://localhost:9876/'; @@ -14,6 +14,10 @@ describe('Yahoo ConnectID Submodule', () => { const OVERRIDE_ENDPOINT = 'https://foo/bar'; const STORAGE_KEY = 'connectId'; const USP_DATA = '1YYY'; + const GPP_DATA = { + gppString: 'gppconsent', + applicableSections: [6, 7] + }; it('should have the correct module name declared', () => { expect(connectIdSubmodule.name).to.equal('connectId'); @@ -34,6 +38,7 @@ describe('Yahoo ConnectID Submodule', () => { let localStorageEnabledStub; let removeLocalStorageDataStub; let uspConsentDataStub; + let gppConsentDataStub; let consentData; beforeEach(() => { @@ -48,10 +53,12 @@ describe('Yahoo ConnectID Submodule', () => { localStorageEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); removeLocalStorageDataStub = sinon.stub(storage, 'removeDataFromLocalStorage'); uspConsentDataStub = sinon.stub(uspDataHandler, 'getConsentData'); + gppConsentDataStub = sinon.stub(gppDataHandler, 'getConsentData'); cookiesEnabledStub.returns(true); localStorageEnabledStub.returns(true); uspConsentDataStub.returns(USP_DATA); + gppConsentDataStub.returns(GPP_DATA); consentData = { gdprApplies: 1, @@ -69,6 +76,7 @@ describe('Yahoo ConnectID Submodule', () => { localStorageEnabledStub.restore(); removeLocalStorageDataStub.restore(); uspConsentDataStub.restore(); + gppConsentDataStub.restore(); }); function invokeGetIdAPI(configParams, consentData) { @@ -135,37 +143,254 @@ describe('Yahoo ConnectID Submodule', () => { }, consentData)).to.be.undefined; expect(ajaxStub.callCount).to.equal(0); }); - - it('returns undefined if the pixelId param is passed, but the he and puid param are not passed', () => { - expect(invokeGetIdAPI({ - pixelId: PIXEL_ID - }, consentData)).to.be.undefined; - expect(ajaxStub.callCount).to.equal(0); - }); }); describe('with valid module configuration', () => { describe('when data is in client storage', () => { - it('returns an object with the stored ID from cookies for valid module configuration', () => { + it('returns an object with the stored ID from cookies for valid module configuration and sync is done', () => { const cookieData = {connectId: 'foobar'}; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); let result = invokeGetIdAPI({ he: HASHED_EMAIL, pixelId: PIXEL_ID }, consentData); + + expect(result).to.be.an('object').that.has.all.keys('id', 'callback'); + expect(result.id).to.deep.equal(cookieData); + expect(typeof result.callback).to.equal('function'); + }); + + it('returns an object with the stored ID from cookies for valid module configuration with no user sync', () => { + const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); + const cookieData = {connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days}; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + let result = invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + + expect(result).to.be.an('object').that.has.all.keys('id'); + expect(result.id.lastUsed).to.be.a('number'); + cookieData.lastUsed = result.id.lastUsed; + expect(result.id).to.deep.equal(cookieData); + }); + + it('returns an object with the stored ID and refreshes the storages with the new lastUsed', () => { + const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); + const cookieData = {connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days, lastUsed: 1}; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + const dateNowStub = sinon.stub(Date, 'now'); + dateNowStub.returns(20); + const newCookieData = Object.assign({}, cookieData, {lastUsed: 20}) + let result = invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + dateNowStub.restore(); + + const expiryDelta = new Date(60 * 60 * 24 * 365 * 1000); + expect(result).to.be.an('object').that.has.all.keys('id'); + expect(setCookieStub.calledOnce).to.be.true; + expect(setCookieStub.firstCall.args[0]).to.equal(STORAGE_KEY); + expect(setCookieStub.firstCall.args[1]).to.equal(JSON.stringify(newCookieData)); + expect(setCookieStub.firstCall.args[2]).to.equal(expiryDelta.toUTCString()); + expect(setLocalStorageStub.calledOnce).to.be.true; + expect(setLocalStorageStub.firstCall.args[0]).to.equal(STORAGE_KEY); + expect(setLocalStorageStub.firstCall.args[1]).to.equal(JSON.stringify(newCookieData)); + }); + + it('returns an object with the stored ID from cookies and no sync when puid stays the same', () => { + const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); + const cookieData = {connectId: 'foobar', puid: '123', lastSynced: last13Days}; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + const dateNowStub = sinon.stub(Date, 'now'); + dateNowStub.returns(20); + let result = invokeGetIdAPI({ + puid: '123', + pixelId: PIXEL_ID + }, consentData); + dateNowStub.restore(); + expect(result).to.be.an('object').that.has.all.keys('id'); + cookieData.lastUsed = 20; expect(result.id).to.deep.equal(cookieData); }); - it('returns an object with the stored ID from localStorage for valid module configuration', () => { + it('returns an object with the stored ID from cookies and syncs because of expired auto generated puid', () => { + const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); + const last31Days = Date.now() - (60 * 60 * 24 * 1000 * 31); + const cookieData = {connectId: 'foo', he: 'email', lastSynced: last13Days, puid: '9', lastUsed: last31Days}; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + let result = invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + + delete cookieData.puid; + expect(result).to.be.an('object').that.has.all.keys('id', 'callback'); + expect(result.id).to.deep.equal(cookieData); + expect(typeof result.callback).to.equal('function'); + }); + + it('returns an object with the stored ID from cookies and does not sync for valid auto generated puid', () => { + const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); + const last29Days = Date.now() - (60 * 60 * 24 * 1000 * 29); + const cookieData = { + connectId: 'foo', + he: HASHED_EMAIL, + lastSynced: last13Days, + puid: '9', + lastUsed: last29Days + }; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + const dateNowStub = sinon.stub(Date, 'now'); + dateNowStub.returns(20); + let result = invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + dateNowStub.restore(); + + expect(result).to.be.an('object').that.has.all.keys('id'); + cookieData.lastUsed = 20; + expect(result.id).to.deep.equal(cookieData); + }); + + it('returns an object with the stored ID from localStorage for valid module configuration and sync is done', () => { const localStorageData = {connectId: 'foobarbaz'}; getLocalStorageStub.withArgs(STORAGE_KEY).returns(localStorageData); let result = invokeGetIdAPI({ he: HASHED_EMAIL, pixelId: PIXEL_ID }, consentData); - expect(result).to.be.an('object').that.has.all.keys('id'); + + expect(result).to.be.an('object').that.has.all.keys('id', 'callback'); expect(result.id).to.deep.equal(localStorageData); + expect(typeof result.callback).to.equal('function'); + }); + + it('returns an object with the stored ID from localStorage and refreshes the cookie storage', () => { + const localStorageData = {connectId: 'foobarbaz'}; + getLocalStorageStub.withArgs(STORAGE_KEY).returns(localStorageData); + const dateNowStub = sinon.stub(Date, 'now'); + dateNowStub.returns(1); + let result = invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + dateNowStub.restore(); + + const expiryDelta = new Date(1 + 60 * 60 * 24 * 365 * 1000); + expect(result).to.be.an('object').that.has.all.keys('id', 'callback'); + expect(setCookieStub.calledOnce).to.be.true; + expect(setCookieStub.firstCall.args[0]).to.equal(STORAGE_KEY); + expect(setCookieStub.firstCall.args[1]).to.equal(JSON.stringify(localStorageData)); + expect(setCookieStub.firstCall.args[2]).to.equal(expiryDelta.toUTCString()); + }); + + it('Makes an ajax GET request to the production API endpoint with stored puid when id is stale', () => { + const last15Days = Date.now() - (60 * 60 * 24 * 1000 * 15); + const last29Days = Date.now() - (60 * 60 * 24 * 1000 * 29); + const cookieData = { + connectId: 'foobar', + he: HASHED_EMAIL, + lastSynced: last15Days, + puid: '981', + lastUsed: last29Days + }; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + + const expectedParams = { + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + '1p': '0', + gdpr: '1', + gdpr_consent: consentData.consentString, + v: '1', + url: TEST_SERVER_URL, + us_privacy: USP_DATA, + puid: '981', + gpp: GPP_DATA.gppString, + gpp_sid: GPP_DATA.applicableSections.join(',') + }; + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + }); + + it('Makes an ajax GET request to the production API endpoint without the stored puid after 30 days', () => { + const last31Days = Date.now() - (60 * 60 * 24 * 1000 * 31); + const cookieData = { + connectId: 'foobar', + he: HASHED_EMAIL, + lastSynced: last31Days, + puid: '981', + lastUsed: last31Days + }; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + + const expectedParams = { + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + '1p': '0', + gdpr: '1', + gdpr_consent: consentData.consentString, + v: '1', + url: TEST_SERVER_URL, + us_privacy: USP_DATA, + gpp: GPP_DATA.gppString, + gpp_sid: GPP_DATA.applicableSections.join(',') + }; + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + }); + + it('Makes an ajax GET request to the production API endpoint with provided puid', () => { + const last3Days = Date.now() - (60 * 60 * 24 * 1000 * 3); + const cookieData = { + connectId: 'foobar', + he: HASHED_EMAIL, + lastSynced: last3Days, + puid: '981', + lastUsed: last3Days + }; + getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); + invokeGetIdAPI({ + pixelId: PIXEL_ID, + puid: PUBLISHER_USER_ID + }, consentData); + + const expectedParams = { + pixelId: PIXEL_ID, + puid: PUBLISHER_USER_ID, + he: HASHED_EMAIL, + '1p': '0', + gdpr: '1', + gdpr_consent: consentData.consentString, + v: '1', + url: TEST_SERVER_URL, + us_privacy: USP_DATA, + gpp: GPP_DATA.gppString, + gpp_sid: GPP_DATA.applicableSections.join(',') + }; + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); }); it('deletes local storage data when expiry has passed', () => { @@ -284,7 +509,9 @@ describe('Yahoo ConnectID Submodule', () => { gdpr_consent: consentData.consentString, v: '1', url: TEST_SERVER_URL, - us_privacy: USP_DATA + us_privacy: USP_DATA, + gpp: GPP_DATA.gppString, + gpp_sid: GPP_DATA.applicableSections.join(',') }; const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); @@ -307,7 +534,9 @@ describe('Yahoo ConnectID Submodule', () => { pixelId: PIXEL_ID, gdpr_consent: consentData.consentString, url: TEST_SERVER_URL, - us_privacy: USP_DATA + us_privacy: USP_DATA, + gpp: GPP_DATA.gppString, + gpp_sid: GPP_DATA.applicableSections.join(',') }; const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); @@ -332,7 +561,9 @@ describe('Yahoo ConnectID Submodule', () => { gdpr_consent: consentData.consentString, v: '1', url: TEST_SERVER_URL, - us_privacy: USP_DATA + us_privacy: USP_DATA, + gpp: GPP_DATA.gppString, + gpp_sid: GPP_DATA.applicableSections.join(',') }; const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); @@ -347,6 +578,31 @@ describe('Yahoo ConnectID Submodule', () => { endpoint: OVERRIDE_ENDPOINT }, consentData); + const expectedParams = { + he: HASHED_EMAIL, + '1p': '0', + gdpr: '1', + gdpr_consent: consentData.consentString, + v: '1', + url: TEST_SERVER_URL, + us_privacy: USP_DATA, + gpp: GPP_DATA.gppString, + gpp_sid: GPP_DATA.applicableSections.join(',') + }; + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${OVERRIDE_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + }); + + it('Makes an ajax GET request to the specified override API endpoint without GPP', () => { + gppConsentDataStub.returns(undefined); + invokeGetIdAPI({ + he: HASHED_EMAIL, + endpoint: OVERRIDE_ENDPOINT + }, consentData); + const expectedParams = { he: HASHED_EMAIL, '1p': '0', @@ -409,12 +665,12 @@ describe('Yahoo ConnectID Submodule', () => { }); }); - it('stores the result in client cookie storage', () => { + it('stores the result in client both storages', () => { const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(0); getAjaxFnStub.restore(); const upsResponse = {connectid: 'foobarbaz'}; - const expiryDelta = new Date(60 * 60 * 24 * 14 * 1000); + const expiryDelta = new Date(60 * 60 * 24 * 365 * 1000); invokeGetIdAPI({ puid: PUBLISHER_USER_ID, pixelId: PIXEL_ID @@ -425,14 +681,23 @@ describe('Yahoo ConnectID Submodule', () => { {'Content-Type': 'application/json'}, JSON.stringify(upsResponse) ); + const storage = Object.assign({}, upsResponse, { + puid: PUBLISHER_USER_ID, + lastSynced: 0, + lastUsed: 0 + }); + dateNowStub.restore(); + expect(setCookieStub.calledOnce).to.be.true; expect(setCookieStub.firstCall.args[0]).to.equal(STORAGE_KEY); - expect(setCookieStub.firstCall.args[1]).to.equal(JSON.stringify(upsResponse)); + expect(setCookieStub.firstCall.args[1]).to.equal(JSON.stringify(storage)); expect(setCookieStub.firstCall.args[2]).to.equal(expiryDelta.toUTCString()); expect(setCookieStub.firstCall.args[3]).to.equal(null); const cookieDomain = parseUrl(TEST_SERVER_URL).hostname; expect(setCookieStub.firstCall.args[4]).to.equal(`.${cookieDomain}`); - dateNowStub.restore(); + expect(setLocalStorageStub.calledOnce).to.be.true; + expect(setLocalStorageStub.firstCall.args[0]).to.equal(STORAGE_KEY); + expect(setLocalStorageStub.firstCall.args[1]).to.deep.equal(JSON.stringify(storage)); }); it('stores the result in localStorage if cookies are not permitted', () => { @@ -441,10 +706,12 @@ describe('Yahoo ConnectID Submodule', () => { const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(0); const upsResponse = {connectid: 'barfoo'}; - const expectedStoredData = JSON.stringify({ + const expectedStoredData = { connectid: 'barfoo', - __expires: 60 * 60 * 24 * 14 * 1000 - }); + puid: PUBLISHER_USER_ID, + lastSynced: 0, + lastUsed: 0 + }; invokeGetIdAPI({ puid: PUBLISHER_USER_ID, pixelId: PIXEL_ID @@ -455,10 +722,11 @@ describe('Yahoo ConnectID Submodule', () => { {'Content-Type': 'application/json'}, JSON.stringify(upsResponse) ); + dateNowStub.restore(); + expect(setLocalStorageStub.calledOnce).to.be.true; expect(setLocalStorageStub.firstCall.args[0]).to.equal(STORAGE_KEY); - expect(setLocalStorageStub.firstCall.args[1]).to.deep.equal(expectedStoredData); - dateNowStub.restore(); + expect(setLocalStorageStub.firstCall.args[1]).to.deep.equal(JSON.stringify(expectedStoredData)); }); }); }); diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index bf02a2893f0..59ebefa2d60 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -256,6 +256,11 @@ describe('Conversant adapter tests', function() { const bidderRequest = { refererInfo: { page: page + }, + ortb2: { + source: { + tid: 'tid000' + } } }; const request = spec.buildRequests(bidRequests, bidderRequest); @@ -263,8 +268,8 @@ describe('Conversant adapter tests', function() { expect(request.url).to.equal('https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'); const payload = request.data; - expect(payload).to.have.property('id', 'req000'); - expect(payload.source).to.have.property('tid', 'req000'); + expect(payload).to.have.property('id'); + expect(payload.source).to.have.property('tid', 'tid000'); expect(payload).to.have.property('at', 1); expect(payload).to.have.property('imp'); expect(payload.imp).to.be.an('array').with.lengthOf(8); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index a13e81b6d88..b2f3d64a156 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -604,13 +604,67 @@ describe('The Criteo bidding adapter', function () { config.resetConfig(); }); + it('should properly build a request using random uuid as auction id', function () { + const generateUUIDStub = sinon.stub(utils, 'generateUUID'); + generateUUIDStub.returns('def'); + const bidderRequest = { + }; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: {} + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest.id).to.equal('def'); + generateUUIDStub.restore(); + }); + + it('should properly transmit source.tid if available', function () { + const bidderRequest = { + ortb2: { + source: { + tid: 'abc' + } + } + }; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: {} + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest.source.tid).to.equal('abc'); + }); + it('should properly build a request if refererInfo is not provided', function () { const bidderRequest = {}; const bidRequests = [ { bidder: 'criteo', adUnitCode: 'bid-123', - transactionId: 'transaction-123', + ortb2Imp: { + ext: { + tid: 'transaction-123', + }, + }, mediaTypes: { banner: { sizes: [[728, 90]] @@ -629,7 +683,11 @@ describe('The Criteo bidding adapter', function () { { bidder: 'criteo', adUnitCode: 'bid-123', - transactionId: 'transaction-123', + ortb2Imp: { + ext: { + tid: 'transaction-123', + }, + }, mediaTypes: { banner: { sizes: [[728, 90]] @@ -842,7 +900,11 @@ describe('The Criteo bidding adapter', function () { { bidder: 'criteo', adUnitCode: 'bid-123', - transactionId: 'transaction-123', + ortb2Imp: { + ext: { + tid: 'transaction-123', + }, + }, mediaTypes: { banner: { sizes: [[300, 250], [728, 90]] @@ -881,7 +943,11 @@ describe('The Criteo bidding adapter', function () { { bidder: 'criteo', adUnitCode: 'bid-123', - transactionId: 'transaction-123', + ortb2Imp: { + ext: { + tid: 'transaction-123', + }, + }, mediaTypes: { banner: { sizes: [[728, 90]] @@ -894,7 +960,11 @@ describe('The Criteo bidding adapter', function () { { bidder: 'criteo', adUnitCode: 'bid-234', - transactionId: 'transaction-234', + ortb2Imp: { + ext: { + tid: 'transaction-234', + }, + }, mediaTypes: { banner: { sizes: [[300, 250], [728, 90]] @@ -1062,6 +1132,90 @@ describe('The Criteo bidding adapter', function () { expect(request.data.source.ext.schain).to.equal(expectedSchain); }); + it('should properly build a request with bcat field', function () { + const bcat = ['IAB1', 'IAB2']; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + zoneId: 123, + }, + }, + ]; + const bidderRequest = { + ortb2: { + bcat + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bcat).to.not.be.null; + expect(request.data.bcat).to.equal(bcat); + }); + + it('should properly build a request with badv field', function () { + const badv = ['ford.com']; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + zoneId: 123, + }, + }, + ]; + const bidderRequest = { + ortb2: { + badv + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.badv).to.not.be.null; + expect(request.data.badv).to.equal(badv); + }); + + it('should properly build a request with bapp field', function () { + const bapp = ['com.foo.mygame']; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + zoneId: 123, + }, + }, + ]; + const bidderRequest = { + ortb2: { + bapp + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bapp).to.not.be.null; + expect(request.data.bapp).to.equal(bapp); + }); + it('should properly build a request with if ccpa consent field is not provided', function () { const bidRequests = [ { @@ -2373,7 +2527,7 @@ describe('The Criteo bidding adapter', function () { const adapters = { Prebid: function () { } }; const adaptersMock = sinon.mock(adapters); - adaptersMock.expects('Prebid').withExactArgs(PROFILE_ID_PUBLISHERTAG, ADAPTER_VERSION, bidRequests, bidderRequest, '$prebid.version$').once().returns(prebidAdapter); + adaptersMock.expects('Prebid').withExactArgs(PROFILE_ID_PUBLISHERTAG, ADAPTER_VERSION, bidRequests, bidderRequest, '$prebid.version$', sinon.match.any).once().returns(prebidAdapter); global.Criteo = { PubTag: { diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index b674cb5976d..88c640e38cc 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -366,15 +366,15 @@ describe('currency', function () { expect(innerBid.cpm).to.equal(1); }); - it('should result in NO_BID when currency support is not enabled and fromCurrency is not USD', function () { + it('should reject bid when currency support is not enabled and fromCurrency is not USD', function () { setConfig({}); var bid = makeBid({ 'cpm': 1, 'currency': 'GBP' }); - var innerBid; + let bidAdded = false; addBidResponseHook(function(adCodeId, bid) { - innerBid = bid; + bidAdded = true; }, 'elementId', bid, reject); - expect(innerBid.status).to.equal('rejected'); + expect(bidAdded).to.be.false; expect(reject.calledOnce).to.be.true; }); @@ -390,7 +390,7 @@ describe('currency', function () { expect(bid).to.equal(innerBid); }); - it('should result in NO_BID when fromCurrency is not supported in file', function () { + it('should reject bid when fromCurrency is not supported in file', function () { // RESET to request currency file setConfig({ 'adServerCurrency': undefined }); @@ -398,24 +398,24 @@ describe('currency', function () { setConfig({ 'adServerCurrency': 'JPY' }); fakeCurrencyFileServer.respond(); var bid = makeBid({ 'cpm': 1, 'currency': 'ABC' }); - var innerBid; + let bidAdded = false; addBidResponseHook(function(adCodeId, bid) { - innerBid = bid; + bidAdded = true; }, 'elementId', bid, reject); - expect(innerBid.status).to.equal('rejected'); + expect(bidAdded).to.be.false; expect(reject.calledOnce).to.be.true; }); - it('should result in NO_BID when adServerCurrency is not supported in file', function () { + it('should reject bid when adServerCurrency is not supported in file', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'ABC' }); fakeCurrencyFileServer.respond(); var bid = makeBid({ 'cpm': 1, 'currency': 'GBP' }); - var innerBid; + let bidAdded = false; addBidResponseHook(function(adCodeId, bid) { - innerBid = bid; + bidAdded = true; }, 'elementId', bid, reject); - expect(innerBid.status).to.equal('rejected'); + expect(bidAdded).to.be.false; expect(reject.calledOnce).to.be.true; }); diff --git a/test/spec/modules/datawrkzBidAdapter_spec.js b/test/spec/modules/datawrkzBidAdapter_spec.js index 3ddc2bad1cf..5524e318600 100644 --- a/test/spec/modules/datawrkzBidAdapter_spec.js +++ b/test/spec/modules/datawrkzBidAdapter_spec.js @@ -462,7 +462,6 @@ describe('datawrkzAdapterTests', function () { expect(result[0].ad).to.equal(decodeURIComponent(serverResponse.body.seatbid[0].bid[0].adm + '')); expect(result[0].creativeId).to.equal('1'); expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); - expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); expect(result[0].mediaType).to.equal('banner'); }); @@ -540,7 +539,6 @@ describe('datawrkzAdapterTests', function () { expect(result[0].native.desc).to.equal('Test body'); expect(result[0].creativeId).to.equal('1'); expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); - expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); expect(result[0].mediaType).to.equal('native'); }); @@ -610,7 +608,6 @@ describe('datawrkzAdapterTests', function () { expect(result[0].adserverTargeting.hb_protocol).to.equal('3'); expect(result[0].creativeId).to.equal('1'); expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); - expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); expect(result[0].mediaType).to.equal('video'); }); @@ -649,7 +646,6 @@ describe('datawrkzAdapterTests', function () { expect(result[0].renderer).to.exist; expect(result[0].creativeId).to.equal('1'); expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); - expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); expect(result[0].mediaType).to.equal('video'); }); }); diff --git a/test/spec/modules/dianomiBidAdapter_spec.js b/test/spec/modules/dianomiBidAdapter_spec.js index 61509913c41..0838762d750 100644 --- a/test/spec/modules/dianomiBidAdapter_spec.js +++ b/test/spec/modules/dianomiBidAdapter_spec.js @@ -162,7 +162,7 @@ describe('Dianomi adapter', () => { }, ]; let request = JSON.parse( - spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, auctionId: 'tid' }).data + spec.buildRequests(validBidRequests, {refererInfo: {page: 'page'}, ortb2: {source: {tid: 'tid'}}}).data ); assert.equal(request.source.tid, 'tid'); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index e5e779479cc..725d631cd99 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -348,6 +348,21 @@ describe('eids array generation for known sub-modules', function() { }); }); + it('euid', function() { + const userId = { + euid: {'id': 'Sample_AD_Token'} + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'euid.eu', + uids: [{ + id: 'Sample_AD_Token', + atype: 3 + }] + }); + }); + it('kpuid', function() { const userId = { kpuid: 'Sample_Token' diff --git a/test/spec/modules/eskimiBidAdapter_spec.js b/test/spec/modules/eskimiBidAdapter_spec.js index 4622b374de5..d01240c86ab 100644 --- a/test/spec/modules/eskimiBidAdapter_spec.js +++ b/test/spec/modules/eskimiBidAdapter_spec.js @@ -1,155 +1,307 @@ -import {expect} from 'chai'; -import {spec} from 'modules/eskimiBidAdapter.js'; - -const REQUEST = { - 'bidderCode': 'eskimi', - 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708', - 'bidderRequestId': 'requestId', - 'bidRequest': [{ - 'bidder': 'eskimi', - 'params': { - 'placementId': 1003000, - 'accId': 123 +import { expect } from 'chai'; +import { spec } from 'modules/eskimiBidAdapter.js'; +import * as utils from 'src/utils'; + +const BANNER_BID = { + bidder: 'eskimi', + params: { + placementId: 1003000 + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ], }, - 'sizes': [ - [300, 250] - ], - 'bidId': 'bidId1', - 'adUnitCode': 'adUnitCode1', - 'bidderRequestId': 'bidderRequestId', - 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' }, - { - 'bidder': 'eskimi', - 'params': { - 'placementId': 1003001, - 'accId': 123 + adUnitCode: 'adUnitCode1', + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', +}; + +const VIDEO_BID = { + bidder: 'eskimi', + params: { + placementId: 1003000 + }, + mediaTypes: { + video: { + context: 'outstream', + api: [1, 2, 4, 6], + mimes: ['video/mp4'], + playbackmethod: [2, 4, 6], + playerSize: [[1024, 768]], + protocols: [3, 4, 7, 8, 10], + placement: 1, + minduration: 0, + maxduration: 60, + startdelay: 0 }, - 'sizes': [ - [300, 250] - ], - 'bidId': 'bidId2', - 'adUnitCode': 'adUnitCode2', - 'bidderRequestId': 'bidderRequestId', - 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' - }], - 'start': 1487883186070, - 'auctionStart': 1487883186069, - 'timeout': 3000 + }, + adUnitCode: 'adUnitCode1', + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', }; -const RESPONSE = { - 'headers': null, - 'body': { - 'id': 'requestId', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'bidId1', - 'impid': 'bidId1', - 'price': 0.18, - 'adm': '', - 'adid': '144762342', - 'adomain': [ - 'https://dummydomain.com' - ], - 'iurl': 'iurl', - 'cid': '109', - 'crid': 'creativeId', - 'cat': [], - 'w': 300, - 'h': 250, - 'ext': { - 'prebid': { - 'type': 'banner' - }, - 'bidder': { - 'eskimi': { - 'brand_id': 334553, - 'auction_id': 514667951122925701, - 'bidder_id': 2, - 'bid_ad_type': 0 - } - } - } - } - ], - 'seat': 'seat' - } - ] +const BIDDER_REQUEST = { + auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', + bidderRequestId: 'bidderRequestId', + timeout: 3000, + refererInfo: { + page: 'https://hello-world-page.com/', + domain: 'hello-world-page.com', + ref: 'http://example-domain.com/foo', } }; +const BANNER_BID_RESPONSE = { + 'id': 'bidderRequestId', + 'bidId': 'bidid', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'id', + 'impid': 'bidId', + 'price': 0.18, + 'adm': '', + 'adid': '144762342', + 'burl': 'http://0.0.0.0:8181/burl', + 'adomain': [ + 'https://dummydomain.com' + ], + 'cid': 'cid', + 'crid': 'crid', + 'iurl': 'iurl', + 'cat': [], + 'w': 300, + 'h': 250 + } + ] + } + ], + 'cur': 'USD' +}; + +const VIDEO_BID_RESPONSE = { + 'id': 'bidderRequestId', + 'bidid': 'bidid', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'id', + 'impid': 'bidId', + 'price': 1.09, + 'adid': '144762342', + 'burl': 'http://0.0.0.0:8181/burl', + 'adm': '', + 'adomain': [ + 'https://dummydomain.com' + ], + 'cid': 'cid', + 'crid': 'crid', + 'iurl': 'iurl', + 'cat': [], + 'h': 768, + 'w': 1024 + } + ] + } + ], + 'cur': 'USD' +}; + describe('Eskimi bid adapter', function () { - describe('isBidRequestValid', function () { + describe('isBidRequestValid()', function () { it('should accept request if placementId is passed', function () { let bid = { bidder: 'eskimi', params: { placementId: 123 + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } } }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('reject requests without params', function () { + + it('should reject requests without params', function () { let bid = { bidder: 'eskimi', params: {} }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - }); - describe('buildRequests', function () { - it('creates request data', function () { - let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0]; - expect(request).to.exist.and.to.be.a('object'); - const payload = request.data; - expect(payload.imp[0]).to.have.property('id', REQUEST.bidRequest[0].bidId); - expect(payload.imp[1]).to.have.property('id', REQUEST.bidRequest[1].bidId); + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(BANNER_BID)).to.equal(true); + expect(spec.isBidRequestValid(VIDEO_BID)).to.equal(true); }); + }); - it('has gdpr data if applicable', function () { - const req = Object.assign({}, REQUEST, { + describe('buildRequests()', function () { + it('should have gdpr data if applicable', function () { + const bid = utils.deepClone(BANNER_BID); + + const req = Object.assign({}, BIDDER_REQUEST, { gdprConsent: { consentString: 'consentString', gdprApplies: true, } }); - let request = spec.buildRequests(REQUEST.bidRequest, req)[0]; + let request = spec.buildRequests([bid], req)[0]; const payload = request.data; expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); expect(payload.regs.ext).to.have.property('gdpr', 1); }); - }); - describe('interpretResponse', function () { - it('has bids', function () { - let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0]; - let bids = spec.interpretResponse(RESPONSE, request); - expect(bids).to.be.an('array').that.is.not.empty; - validateBidOnIndex(0); - - function validateBidOnIndex(index) { - expect(bids[index]).to.have.property('currency', 'USD'); - expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].id); - expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price); - expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w); - expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h); - expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); - expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); - expect(bids[index]).to.have.property('ttl', 30); - expect(bids[index]).to.have.property('netRevenue', true); - } + it('should properly forward ORTB blocking params', function () { + let bid = utils.deepClone(BANNER_BID); + bid = utils.mergeDeep(bid, { + params: { bcat: ['IAB1-1'], badv: ['example.com'], bapp: ['com.example'] }, + mediaTypes: { banner: { battr: [1] } } + }); + + let [request] = spec.buildRequests([bid], BIDDER_REQUEST); + + expect(request).to.exist.and.to.be.an('object'); + const payload = request.data; + expect(payload).to.have.deep.property('bcat', ['IAB1-1']); + expect(payload).to.have.deep.property('badv', ['example.com']); + expect(payload).to.have.deep.property('bapp', ['com.example']); + expect(payload.imp[0].banner).to.have.deep.property('battr', [1]); + }); + + context('when mediaType is banner', function () { + it('should build correct request for banner bid with both w, h', () => { + const bid = utils.deepClone(BANNER_BID); + + const [request] = spec.buildRequests([bid], BIDDER_REQUEST); + const requestData = request.data; + + expect(requestData.imp[0].banner.w).to.equal(300); + expect(requestData.imp[0].banner.h).to.equal(250); + }); + + it('should create request data', function () { + const bid = utils.deepClone(BANNER_BID); + + let [request] = spec.buildRequests([bid], BIDDER_REQUEST); + expect(request).to.exist.and.to.be.a('object'); + const payload = request.data; + expect(payload.imp[0]).to.have.property('id', bid.bidId); + }); }); - it('handles empty response', function () { - let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0]; - const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}}); - const bids = spec.interpretResponse(EMPTY_RESP, request); - expect(bids).to.be.empty; + context('when mediaType is video', function () { + it('should return false when there is no video in mediaTypes', () => { + const bid = utils.deepClone(VIDEO_BID); + delete bid.mediaTypes.video; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should reutrn false if player size is not set', () => { + const bid = utils.deepClone(VIDEO_BID); + delete bid.mediaTypes.video.playerSize; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should use bidder video params if they are set', () => { + const videoBidWithParams = utils.deepClone(VIDEO_BID); + const bidderVideoParams = { + api: [1, 2], + mimes: ['video/mp4', 'video/x-flv'], + playbackmethod: [3, 4], + protocols: [5, 6], + placement: 1, + minduration: 0, + maxduration: 60, + w: 1024, + h: 768, + startdelay: 0 + }; + + videoBidWithParams.params.video = bidderVideoParams; + + const requests = spec.buildRequests([videoBidWithParams], BIDDER_REQUEST); + const request = requests[0].data; + + expect(request.imp[0]).to.deep.include({ + video: { + ...bidderVideoParams, + w: videoBidWithParams.mediaTypes.video.playerSize[0][0], + h: videoBidWithParams.mediaTypes.video.playerSize[0][1], + }, + }); + }); }); }); + + describe('interpretResponse()', function () { + context('when mediaType is banner', function () { + it('should correctly interpret valid banner response', function () { + const bid = utils.deepClone(BANNER_BID); + const [request] = spec.buildRequests([bid], BIDDER_REQUEST); + const response = utils.deepClone(BANNER_BID_RESPONSE); + + const bids = spec.interpretResponse({ body: response }, request); + expect(bids).to.be.an('array').that.is.not.empty; + + expect(bids[0].mediaType).to.equal('banner'); + expect(bids[0].burl).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].burl); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].requestId).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].impid); + expect(bids[0].cpm).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].price); + expect(bids[0].width).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].w); + expect(bids[0].height).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].h); + expect(bids[0].ad).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].adm); + expect(bids[0].creativeId).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].crid); + expect(bids[0].meta.advertiserDomains[0]).to.equal('https://dummydomain.com'); + expect(bids[0].ttl).to.equal(30); + expect(bids[0].netRevenue).to.equal(true); + }); + + it('should handle empty bid response', function () { + const bid = utils.deepClone(BANNER_BID); + + let request = spec.buildRequests([bid], BIDDER_REQUEST)[0]; + const EMPTY_RESP = Object.assign({}, BANNER_BID_RESPONSE, { 'body': {} }); + const bids = spec.interpretResponse(EMPTY_RESP, request); + expect(bids).to.be.empty; + }); + }); + if (FEATURES.VIDEO) { + context('when mediaType is video', function () { + it('should correctly interpret valid instream video response', () => { + const bid = utils.deepClone(VIDEO_BID); + + const [request] = spec.buildRequests([bid], BIDDER_REQUEST); + const bids = spec.interpretResponse({ body: VIDEO_BID_RESPONSE }, request); + expect(bids).to.be.an('array').that.is.not.empty; + + expect(bids[0].mediaType).to.equal('video'); + expect(bids[0].burl).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].burl); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].requestId).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].impid); + expect(bids[0].cpm).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].price); + expect(bids[0].width).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].w); + expect(bids[0].height).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].h); + expect(bids[0].vastXml).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].adm); + expect(bids[0].creativeId).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].crid); + expect(bids[0].meta.advertiserDomains[0]).to.equal('https://dummydomain.com'); + expect(bids[0].ttl).to.equal(30); + expect(bids[0].netRevenue).to.equal(true); + }); + }); + } + }); }); diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js index 55394dcdeea..a950100d612 100644 --- a/test/spec/modules/etargetBidAdapter_spec.js +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -173,7 +173,6 @@ describe('etarget adapter', function () { assert.isNotNull(result.reason); assert.equal(result.ttl, 360); assert.equal(result.ad, ''); - assert.equal(result.transactionId, '5f33781f-9552-4ca1'); }); it('should set correct netRevenue', function () { @@ -296,7 +295,11 @@ describe('etarget adapter', function () { tid: 45, placementCode: placementCode[0], sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], - transactionId: '5f33781f-9552-4ca1' + ortb2Imp: { + ext: { + tid: '5f33781f-9552-4ca1' + } + } }, { adUnitCode: placementCode[1], @@ -307,7 +310,11 @@ describe('etarget adapter', function () { params: params[1], placementCode: placementCode[1], sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], - transactionId: '5f33781f-9552-4iuy' + ortb2Imp: { + ext: { + tid: '5f33781f-9552-4iuy' + } + } }, { adUnitCode: placementCode[2], @@ -318,7 +325,11 @@ describe('etarget adapter', function () { params: params[2], placementCode: placementCode[2], sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], - transactionId: '5f33781f-9552-7ev3' + ortb2Imp: { + ext: { + tid: '5f33781f-9552-7ev3' + } + } }, { adUnitCode: placementCode[3], @@ -329,7 +340,11 @@ describe('etarget adapter', function () { params: params[2], placementCode: placementCode[2], sizes: [], - transactionId: '5f33781f-9552-7ev3' + ortb2Imp: { + ext: { + tid: '5f33781f-9552-7ev3' + } + } }, { adUnitCode: placementCode[4], @@ -340,7 +355,11 @@ describe('etarget adapter', function () { params: params[2], placementCode: placementCode[2], sizes: [], - transactionId: '5f33781f-9552-7ev3' + ortb2Imp: { + ext: { + tid: '5f33781f-9552-7ev3' + } + } }, { adUnitCode: placementCode[4], @@ -351,7 +370,11 @@ describe('etarget adapter', function () { params: params[3], placementCode: placementCode[2], sizes: [], - transactionId: '5f33781f-9552-7ev3' + ortb2Imp: { + ext: { + tid: '5f33781f-9552-7ev3' + } + } }, { adUnitCode: placementCode[4], @@ -362,7 +385,11 @@ describe('etarget adapter', function () { params: params[4], placementCode: placementCode[2], sizes: [], - transactionId: '5f33781f-9552-7ev3' + ortb2Imp: { + ext: { + tid: '5f33781f-9552-7ev3' + } + } } ]; serverResponse = { diff --git a/test/spec/modules/euidIdSystem_spec.js b/test/spec/modules/euidIdSystem_spec.js new file mode 100644 index 00000000000..9a016b0facd --- /dev/null +++ b/test/spec/modules/euidIdSystem_spec.js @@ -0,0 +1,123 @@ +import {coreStorage, init, setSubmoduleRegistry, requestBidsHook} from 'modules/userId/index.js'; +import {config} from 'src/config.js'; +import * as utils from 'src/utils.js'; +import { euidIdSubmodule } from 'modules/euidIdSystem.js'; +import 'modules/consentManagement.js'; +import 'src/prebid.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { server } from 'test/mocks/xhr.js'; +import { configureTimerInterceptors } from 'test/mocks/timers.js'; +import { cookieHelpers, runAuction, apiHelpers, setGdprApplies } from './uid2IdSystem_helpers.js'; +import {hook} from 'src/hook.js'; +import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; + +let expect = require('chai').expect; + +// N.B. Most of the EUID code is shared with UID2 - the tests here only cover the happy path. +// Most of the functionality is covered by the UID2 tests. + +const moduleCookieName = '__euid_advertising_token'; +const publisherCookieName = '__EUID_SERVER_COOKIE'; +const initialToken = `initial-advertising-token`; +const legacyToken = 'legacy-advertising-token'; +const refreshedToken = 'refreshed-advertising-token'; +const auctionDelayMs = 10; + +const makeEuidIdentityContainer = (token) => ({euid: {id: token}}); +const useLocalStorage = true; +const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ + userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'euid', params: {storage: useLocalStorage ? 'localStorage' : 'cookie', ...params}, ...extraSettings}] }, debug +}); + +const apiUrl = 'https://prod.euid.eu/v2/token/refresh'; +const headers = { 'Content-Type': 'application/json' }; +const makeSuccessResponseBody = () => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: refreshedToken } })); +const configureEuidResponse = (httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); +const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeEuidIdentityContainer(token)); +const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId'); + +describe('EUID module', function() { + let suiteSandbox, testSandbox, timerSpy, fullTestTitle, restoreSubtleToUndefined = false; + before(function() { + uninstallGdprEnforcement(); + hook.ready(); + suiteSandbox = sinon.sandbox.create(); + if (typeof window.crypto.subtle === 'undefined') { + restoreSubtleToUndefined = true; + window.crypto.subtle = { importKey: () => {}, decrypt: () => {} }; + } + suiteSandbox.stub(window.crypto.subtle, 'importKey').callsFake(() => Promise.resolve()); + suiteSandbox.stub(window.crypto.subtle, 'decrypt').callsFake((settings, key, data) => Promise.resolve(new Uint8Array([...settings.iv, ...data]))); + }); + after(function() { + suiteSandbox.restore(); + if (restoreSubtleToUndefined) window.crypto.subtle = undefined; + }); + beforeEach(function() { + init(config); + setSubmoduleRegistry([euidIdSubmodule]); + }); + afterEach(function() { + $$PREBID_GLOBAL$$.requestBids.removeAll(); + config.resetConfig(); + cookieHelpers.clearCookies(moduleCookieName, publisherCookieName); + coreStorage.removeDataFromLocalStorage(moduleCookieName); + }); + + it('When a server-only token value is provided in config, it is available to the auction.', async function() { + setGdprApplies(true); + config.setConfig(makePrebidConfig(null, {value: makeEuidIdentityContainer(initialToken)})); + const bid = await runAuction(); + expectToken(bid, initialToken); + }); + + it('When a server-only token is provided in the module storage cookie but consent is not available, it is not available to the auction.', async function() { + setGdprApplies(); + coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); + config.setConfig({userSync: {auctionDelay: auctionDelayMs, userIds: [{name: 'euid'}]}}); + const bid = await runAuction(); + expectNoIdentity(bid); + }); + + it('When a server-only token is provided in the module storage cookie, it is available to the auction.', async function() { + setGdprApplies(true); + coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); + config.setConfig({userSync: {auctionDelay: auctionDelayMs, userIds: [{name: 'euid'}]}}); + const bid = await runAuction(); + expectToken(bid, legacyToken); + }) + + it('When a valid response body is provided in config, it is available to the auction', async function() { + setGdprApplies(true); + const euidToken = apiHelpers.makeTokenResponse(initialToken, false, false); + config.setConfig(makePrebidConfig({euidToken})); + const bid = await runAuction(); + expectToken(bid, initialToken); + }) + + it('When a valid response body is provided via cookie, it is available to the auction', async function() { + setGdprApplies(true); + const euidToken = apiHelpers.makeTokenResponse(initialToken, false, false); + cookieHelpers.setPublisherCookie(publisherCookieName, euidToken); + config.setConfig(makePrebidConfig({euidCookie: publisherCookieName})); + const bid = await runAuction(); + expectToken(bid, initialToken); + }) + + it('When an expired token is provided in config, it calls the API.', function() { + setGdprApplies(true); + const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); + config.setConfig(makePrebidConfig({euidToken})); + expect(server.requests[0]?.url).to.have.string('https://prod.euid.eu/'); + }); + + it('When an expired token is provided and the API responds in time, the refreshed token is provided to the auction.', async function() { + setGdprApplies(true); + const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); + configureEuidResponse(200, makeSuccessResponseBody()); + config.setConfig(makePrebidConfig({euidToken})); + apiHelpers.respondAfterDelay(1); + const bid = await runAuction(); + expectToken(bid, refreshedToken); + }); +}); diff --git a/test/spec/modules/feedadBidAdapter_spec.js b/test/spec/modules/feedadBidAdapter_spec.js index 5789361d2a1..152adba9d00 100644 --- a/test/spec/modules/feedadBidAdapter_spec.js +++ b/test/spec/modules/feedadBidAdapter_spec.js @@ -508,7 +508,11 @@ describe('FeedAdAdapter', function () { } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': transactionId, + ortb2Imp: { + ext: { + tid: transactionId + } + }, 'sizes': [ [ 300, diff --git a/test/spec/modules/finativeBidAdapter_spec.js b/test/spec/modules/finativeBidAdapter_spec.js index 7b3f23d8b9e..d5c56aca65d 100644 --- a/test/spec/modules/finativeBidAdapter_spec.js +++ b/test/spec/modules/finativeBidAdapter_spec.js @@ -49,17 +49,6 @@ describe('Finative adapter', function () { assert.deepEqual(keys, data); }); - it('Verify the auction ID', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {}, - auctionId: 'auctionId' - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' }, auctionId: validBidRequests[0].auctionId }).data); - - assert.equal(request.id, validBidRequests[0].auctionId); - }); - it('Verify the device', function () { let validBidRequests = [{ bidId: 'bidId', @@ -166,7 +155,6 @@ describe('Finative adapter', function () { assert.deepEqual(result[0].cpm, bid.price); assert.deepEqual(result[0].creativeId, bid.crid); assert.deepEqual(result[0].mediaType, 'native'); - assert.deepEqual(result[0].bidderCode, 'finative'); }); it('should return the correct tracking links', function () { diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index e9681c05314..d970f70ad85 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -319,7 +319,6 @@ describe('fluctAdapter', function () { const expectedResponse = [ { - bidderCode: 'fluct', requestId: '237f4d1a293f99', currency: 'JPY', cpm: 100, @@ -376,7 +375,6 @@ describe('fluctAdapter', function () { const expectedResponse = [ { - bidderCode: 'fluct', requestId: '237f4d1a293f99', currency: 'JPY', cpm: 100, diff --git a/test/spec/modules/freepassIdSystem_spec.js b/test/spec/modules/freepassIdSystem_spec.js new file mode 100644 index 00000000000..f7407f5eb94 --- /dev/null +++ b/test/spec/modules/freepassIdSystem_spec.js @@ -0,0 +1,186 @@ +import {freepassIdSubmodule} from 'modules/freepassIdSystem'; +import sinon from 'sinon'; +import * as utils from '../../../src/utils'; + +let expect = require('chai').expect; + +describe('FreePass ID System', function () { + const UUID = '15fde1dc-1861-4894-afdf-b757272f3568'; + + before(function () { + sinon.stub(utils, 'generateUUID').returns(UUID); + sinon.stub(utils, 'logMessage'); + }); + + after(function () { + utils.generateUUID.restore(); + utils.logMessage.restore(); + }); + + describe('freepassIdSubmodule', function () { + it('should expose submodule name', function () { + expect(freepassIdSubmodule.name).to.equal('freepassId'); + }); + }); + + describe('getId', function () { + const config = { + storage: { + name: '_freepassId', + type: 'cookie', + expires: 30 + }, + params: { + freepassData: { + commonId: 'commonId', + userIp: '127.0.0.1' + } + } + }; + + it('should return an IdObject with a UUID', function () { + const objectId = freepassIdSubmodule.getId(config, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.userId).to.equal(UUID); + }); + + it('should include userIp in IdObject', function () { + const objectId = freepassIdSubmodule.getId(config, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.userIp).to.equal('127.0.0.1'); + }); + it('should skip userIp in IdObject if not available', function () { + const localConfig = Object.assign({}, config); + delete localConfig.params.freepassData.userIp; + const objectId = freepassIdSubmodule.getId(localConfig, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.userIp).to.be.undefined; + }); + it('should skip userIp in IdObject if freepassData is not available', function () { + const localConfig = JSON.parse(JSON.stringify(config)); + delete localConfig.params.freepassData; + const objectId = freepassIdSubmodule.getId(localConfig, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.userIp).to.be.undefined; + }); + it('should skip userIp in IdObject if params is not available', function () { + const localConfig = JSON.parse(JSON.stringify(config)); + delete localConfig.params; + const objectId = freepassIdSubmodule.getId(localConfig, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.userIp).to.be.undefined; + }); + it('should include commonId in IdObject', function () { + const objectId = freepassIdSubmodule.getId(config, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.commonId).to.equal('commonId'); + }); + it('should skip commonId in IdObject if not available', function () { + const localConfig = Object.assign({}, config); + delete localConfig.params.freepassData.commonId; + const objectId = freepassIdSubmodule.getId(localConfig, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.commonId).to.be.undefined; + }); + it('should skip commonId in IdObject if freepassData is not available', function () { + const localConfig = JSON.parse(JSON.stringify(config)); + delete localConfig.params.freepassData; + const objectId = freepassIdSubmodule.getId(localConfig, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.commonId).to.be.undefined; + }); + it('should skip commonId in IdObject if params is not available', function () { + const localConfig = JSON.parse(JSON.stringify(config)); + delete localConfig.params; + const objectId = freepassIdSubmodule.getId(localConfig, undefined); + expect(objectId).to.be.an('object'); + expect(objectId.id).to.be.an('object'); + expect(objectId.id.commonId).to.be.undefined; + }); + }); + + describe('decode', function () { + it('should have module name as property', function () { + const decodedId = freepassIdSubmodule.decode({}, {}); + expect(decodedId).to.be.an('object'); + expect(decodedId).to.have.property('freepassId'); + }); + it('should have IObject as property value', function () { + const idObject = { + commonId: 'commonId', + userIp: '127.0.0.1', + userId: UUID + }; + const decodedId = freepassIdSubmodule.decode(idObject, {}); + expect(decodedId).to.be.an('object'); + expect(decodedId.freepassId).to.be.an('object'); + expect(decodedId.freepassId).to.equal(idObject); + }); + }); + + describe('extendId', function () { + const config = { + storage: { + name: '_freepassId', + type: 'cookie', + expires: 30 + }, + params: { + freepassData: { + commonId: 'commonId', + userIp: '127.0.0.1' + } + } + }; + + it('should return cachedIdObject if there are no changes', function () { + const idObject = freepassIdSubmodule.getId(config, undefined); + const cachedIdObject = Object.assign({}, idObject.id); + const extendedIdObject = freepassIdSubmodule.extendId(config, undefined, cachedIdObject); + expect(extendedIdObject).to.be.an('object'); + expect(extendedIdObject.id).to.be.an('object'); + expect(extendedIdObject.id).to.equal(cachedIdObject); + }); + + it('should return cachedIdObject if there are no new data', function () { + const idObject = freepassIdSubmodule.getId(config, undefined); + const cachedIdObject = Object.assign({}, idObject.id); + const localConfig = JSON.parse(JSON.stringify(config)); + delete localConfig.params.freepassData; + const extendedIdObject = freepassIdSubmodule.extendId(localConfig, undefined, cachedIdObject); + expect(extendedIdObject).to.be.an('object'); + expect(extendedIdObject.id).to.be.an('object'); + expect(extendedIdObject.id).to.equal(cachedIdObject); + }); + + it('should return new commonId if there are changes', function () { + const idObject = freepassIdSubmodule.getId(config, undefined); + const cachedIdObject = Object.assign({}, idObject.id); + const localConfig = JSON.parse(JSON.stringify(config)); + localConfig.params.freepassData.commonId = 'newCommonId'; + const extendedIdObject = freepassIdSubmodule.extendId(localConfig, undefined, cachedIdObject); + expect(extendedIdObject).to.be.an('object'); + expect(extendedIdObject.id).to.be.an('object'); + expect(extendedIdObject.id.commonId).to.equal('newCommonId'); + }); + + it('should return new userIp if there are changes', function () { + const idObject = freepassIdSubmodule.getId(config, undefined); + const cachedIdObject = Object.assign({}, idObject.id); + const localConfig = JSON.parse(JSON.stringify(config)); + localConfig.params.freepassData.userIp = '192.168.1.1'; + const extendedIdObject = freepassIdSubmodule.extendId(localConfig, undefined, cachedIdObject); + expect(extendedIdObject).to.be.an('object'); + expect(extendedIdObject.id).to.be.an('object'); + expect(extendedIdObject.id.userIp).to.equal('192.168.1.1'); + }); + }); +}); diff --git a/test/spec/modules/gamoshiBidAdapter_spec.js b/test/spec/modules/gamoshiBidAdapter_spec.js index 8f8e3f03736..984830f67d4 100644 --- a/test/spec/modules/gamoshiBidAdapter_spec.js +++ b/test/spec/modules/gamoshiBidAdapter_spec.js @@ -46,7 +46,11 @@ describe('GamoshiAdapter', () => { 'supplyPartnerId': supplyPartnerId }, 'sizes': [[300, 250], [300, 600]], - 'transactionId': 'a123456789', + ortb2Imp: { + ext: { + tid: 'a123456789', + } + }, refererInfo: {referer: 'http://examplereferer.com'}, gdprConsent: { consentString: 'some string', @@ -310,7 +314,6 @@ describe('GamoshiAdapter', () => { response = spec.buildRequests([bidRequest], bidRequest)[0]; expect(response.method).to.equal('POST'); expect(response.url).to.match(new RegExp(`^https://rtb\\.gamoshi\\.io/r/${supplyPartnerId}/bidr\\?rformat=open_rtb&reqformat=rtb_json&bidder=prebid$`, 'g')); - expect(response.data.id).to.equal(bidRequest.auctionId); const bidRequestWithEndpoint = utils.deepClone(bidRequest); bidRequestWithEndpoint.params.rtbEndpoint = 'https://rtb2.gamoshi.io/a12'; response = spec.buildRequests([bidRequestWithEndpoint], bidRequest)[0]; diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index 941f2b3c8df..1585b8346ba 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -1,16 +1,17 @@ import { + accessDeviceRule, deviceAccessHook, - enableAnalyticsHook, enforcementRules, + enrichEidsRule, + fetchBidsRule, getGvlid, getGvlidFromAnalyticsAdapter, - makeBidRequestsHook, purpose1Rule, purpose2Rule, + reportAnalyticsRule, setEnforcementConfig, STRICT_STORAGE_ENFORCEMENT, - userIdHook, - userSyncHook, + syncUserRule, validateRules } from 'modules/gdprEnforcement.js'; import {config} from 'src/config.js'; @@ -19,7 +20,7 @@ import * as utils from 'src/utils.js'; import { MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, - MODULE_TYPE_CORE, + MODULE_TYPE_PREBID, MODULE_TYPE_UID } from '../../../src/activities/modules.js'; import * as events from 'src/events.js'; @@ -28,6 +29,7 @@ import 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../../../src/consentHandler.js'; import {validateStorageEnforcement} from '../../../src/storageManager.js'; +import {activityParams} from '../../../src/activities/activityParams.js'; describe('gdpr enforcement', function () { let nextFnSpy; @@ -107,65 +109,47 @@ describe('gdpr enforcement', function () { } } }; - let gvlids; + let gvlids, sandbox; + + function setupConsentData({gdprApplies = true, apiVersion = 2} = {}) { + const cd = utils.deepClone(staticConfig); + const consent = { + vendorData: cd.consentData.getTCData, + gdprApplies, + apiVersion + }; + sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => consent) + return consent; + } before(() => { hook.ready(); }); after(function () { - validateStorageEnforcement.getHooks({ hook: deviceAccessHook }).remove(); $$PREBID_GLOBAL$$.requestBids.getHooks().remove(); - adapterManager.makeBidRequests.getHooks({ hook: makeBidRequestsHook }).remove(); }) + function expectAllow(allow, ruleResult) { + allow ? expect(ruleResult).to.not.exist : sinon.assert.match(ruleResult, {allow: false}); + } + beforeEach(() => { + sandbox = sinon.sandbox.create(); gvlids = {}; - sinon.stub(GDPR_GVLIDS, 'get').callsFake((name) => ({gvlid: gvlids[name], modules: {}})); + sandbox.stub(GDPR_GVLIDS, 'get').callsFake((name) => ({gvlid: gvlids[name], modules: {}})); }); afterEach(() => { - GDPR_GVLIDS.get.restore(); - }); - - describe('deviceAccessHook', function () { - beforeEach(function () { - nextFnSpy = sinon.spy(); - gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); - logWarnSpy = sinon.spy(utils, 'logWarn'); - }); + sandbox.restore(); + }) - afterEach(function () { + describe('deviceAccessRule', () => { + afterEach(() => { config.resetConfig(); - gdprDataHandler.getConsentData.restore(); - logWarnSpy.restore(); - }); - - it('should not allow device access when device access flag is set to false', function () { - config.setConfig({ - deviceAccess: false, - consentManagement: { - gdpr: { - rules: [{ - purpose: 'storage', - enforcePurpose: false, - enforceVendor: false, - vendorExceptions: ['appnexus', 'rubicon'] - }] - } - } - }); - - deviceAccessHook(nextFnSpy); - expect(nextFnSpy.calledOnce).to.equal(true); - let result = { - hasEnforcementHook: true, - valid: false - } - sinon.assert.calledWith(nextFnSpy, undefined, undefined, result); }); - it('should only check for consent for vendor exceptions when enforcePurpose and enforceVendor are false', function () { + it('should not check for consent when enforcePurpose and enforceVendor are false', function () { Object.assign(gvlids, { appnexus: 1, rubicon: 5 @@ -180,15 +164,8 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.gdprApplies = true; - consentData.apiVersion = 2; - gdprDataHandlerStub.returns(consentData); - - deviceAccessHook(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus'); - deviceAccessHook(nextFnSpy, MODULE_TYPE_BIDDER, 'rubicon'); - expect(logWarnSpy.callCount).to.equal(0); + setupConsentData(); + ['appnexus', 'rubicon'].forEach(bidder => expectAllow(true, accessDeviceRule(activityParams(MODULE_TYPE_BIDDER, bidder)))); }); it('should check consent for all vendors when enforcePurpose and enforceVendor are true', function () { @@ -205,15 +182,13 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.gdprApplies = true; - consentData.apiVersion = 2; - gdprDataHandlerStub.returns(consentData); - - deviceAccessHook(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus'); - deviceAccessHook(nextFnSpy, MODULE_TYPE_BIDDER, 'rubicon'); - expect(logWarnSpy.callCount).to.equal(1); + setupConsentData(); + Object.entries({ + appnexus: true, + rubicon: false + }).forEach(([bidder, isAllowed]) => { + expectAllow(isAllowed, accessDeviceRule(activityParams(MODULE_TYPE_BIDDER, bidder))); + }) }); it('should allow device access when gdprApplies is false and hasDeviceAccess flag is true', function () { @@ -228,19 +203,8 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.gdprApplies = false; - consentData.apiVersion = 2; - gdprDataHandlerStub.returns(consentData); - - deviceAccessHook(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus'); - expect(nextFnSpy.calledOnce).to.equal(true); - let result = { - hasEnforcementHook: true, - valid: true - } - sinon.assert.calledWith(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus', result); + setupConsentData(); + expectAllow(true, accessDeviceRule(activityParams(MODULE_TYPE_BIDDER, 'appnexus'))); }); it('should use gvlMapping set by publisher', function() { @@ -259,92 +223,18 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.gdprApplies = true; - consentData.apiVersion = 2; - gdprDataHandlerStub.returns(consentData); - - deviceAccessHook(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus'); - expect(nextFnSpy.calledOnce).to.equal(true); - let result = { - hasEnforcementHook: true, - valid: true - } - sinon.assert.calledWith(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus', result); - config.resetConfig(); - }); - - it('should use gvl id of alias and not of parent', function() { - let curBidderStub = sinon.stub(config, 'getCurrentBidder'); - curBidderStub.returns('appnexus-alias'); - adapterManager.aliasBidAdapter('appnexus', 'appnexus-alias'); - config.setConfig({ - 'gvlMapping': { - 'appnexus-alias': 4 - } - }); - setEnforcementConfig({ - gdpr: { - rules: [{ - purpose: 'storage', - enforcePurpose: true, - enforceVendor: true, - vendorExceptions: [] - }] - } - }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.gdprApplies = true; - consentData.apiVersion = 2; - gdprDataHandlerStub.returns(consentData); - - deviceAccessHook(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus'); - expect(nextFnSpy.calledOnce).to.equal(true); - let result = { - hasEnforcementHook: true, - valid: true - } - sinon.assert.calledWith(nextFnSpy, MODULE_TYPE_BIDDER, 'appnexus', result); - config.resetConfig(); - curBidderStub.restore(); + setupConsentData(); + expectAllow(true, accessDeviceRule(activityParams(MODULE_TYPE_BIDDER, 'appnexus'))); }); it(`should not enforce consent for vendorless modules if ${STRICT_STORAGE_ENFORCEMENT} is not set`, () => { setEnforcementConfig({}); - let consentData = { - vendorData: staticConfig.consentData.getTCData, - gdprApplies: true - } - gdprDataHandlerStub.returns(consentData); - const validate = sinon.stub().callsFake(() => false); - deviceAccessHook(nextFnSpy, MODULE_TYPE_CORE, 'mockModule', undefined, {validate}); - sinon.assert.callCount(validate, 0); - sinon.assert.calledWith(nextFnSpy, MODULE_TYPE_CORE, 'mockModule', {hasEnforcementHook: true, valid: true}); + setupConsentData(); + expectAllow(true, accessDeviceRule(activityParams(MODULE_TYPE_PREBID, 'mockCoreModule'))); }) }); - describe('userSyncHook', function () { - let curBidderStub; - let adapterManagerStub; - - beforeEach(function () { - gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); - logWarnSpy = sinon.spy(utils, 'logWarn'); - curBidderStub = sinon.stub(config, 'getCurrentBidder'); - adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); - nextFnSpy = sinon.spy(); - }); - - afterEach(function () { - config.getCurrentBidder.restore(); - config.resetConfig(); - gdprDataHandler.getConsentData.restore(); - adapterManager.getBidAdapter.restore(); - logWarnSpy.restore(); - }); - + describe('syncUserRule', () => { it('should allow bidder to do user sync if consent is true', function () { setEnforcementConfig({ gdpr: { @@ -356,20 +246,12 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.gdprApplies = true; - consentData.apiVersion = 2; - gdprDataHandlerStub.returns(consentData); - - curBidderStub.returns('sampleBidder1'); - gvlids.sampleBidder1 = 1; - userSyncHook(nextFnSpy); - - curBidderStub.returns('sampleBidder2'); - gvlids.sampleBidder2 = 3; - userSyncHook(nextFnSpy); - expect(nextFnSpy.calledTwice).to.equal(true); + setupConsentData(); + Object.assign(gvlids, { + sampleBidder1: 1, + sampleBidder2: 2 + }) + Object.keys(gvlids).forEach(bidder => expect(syncUserRule(activityParams(MODULE_TYPE_BIDDER, bidder))).to.not.exist); }); it('should not allow bidder to do user sync if user has denied consent', function () { @@ -383,21 +265,18 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 2; - consentData.gdprApplies = true; - gdprDataHandlerStub.returns(consentData); - - curBidderStub.returns('sampleBidder1'); - gvlids.sampleBidder1 = 1; - userSyncHook(nextFnSpy); - - curBidderStub.returns('sampleBidder2'); - gvlids.sampleBidder2 = 3; - userSyncHook(nextFnSpy); - expect(nextFnSpy.calledOnce).to.equal(true); - expect(logWarnSpy.callCount).to.equal(1); + setupConsentData(); + Object.assign(gvlids, { + sampleBidder1: 1, + sampleBidder2: 3 + }) + + Object.entries({ + sampleBidder1: true, + sampleBidder2: false + }).forEach(([bidder, isAllowed]) => { + expectAllow(isAllowed, syncUserRule(activityParams(MODULE_TYPE_BIDDER, bidder))); + }) }); it('should not check vendor consent when enforceVendor is false', function () { @@ -411,33 +290,15 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 2; - consentData.gdprApplies = true; - gdprDataHandlerStub.returns(consentData); - - curBidderStub.returns('sampleBidder1'); - gvlids.sampleBidder1 = 1; - userSyncHook(nextFnSpy); - - curBidderStub.returns('sampleBidder2'); - gvlids.sampleBidder2 = 3; - userSyncHook(nextFnSpy); - expect(nextFnSpy.calledTwice).to.equal(true); - expect(logWarnSpy.callCount).to.equal(0); + setupConsentData(); + Object.assign(gvlids, { + sampleBidder1: 1, + sampleBidder2: 3 + }) + Object.keys(gvlids).forEach(bidder => expect(syncUserRule(activityParams(MODULE_TYPE_BIDDER, bidder))).to.not.exist); }); }); - - describe('userIdHook', function () { - beforeEach(function () { - logWarnSpy = sinon.spy(utils, 'logWarn'); - nextFnSpy = sinon.spy(); - }); - afterEach(function () { - config.resetConfig(); - logWarnSpy.restore(); - }); + describe('enrichEidsRule', () => { it('should allow user id module if consent is given', function () { setEnforcementConfig({ gdpr: { @@ -449,40 +310,16 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 2; - consentData.gdprApplies = true; - let submodules = [{ - submodule: { - gvlid: 1, - name: 'sampleUserId' - } - }] + setupConsentData(); gvlids.sampleUserId = 1; - userIdHook(nextFnSpy, submodules, consentData); - // Should pass back hasValidated flag since version 2 - const args = nextFnSpy.getCalls()[0].args; - expect(args[1].hasValidated).to.be.true; - expect(nextFnSpy.calledOnce).to.equal(true); - sinon.assert.calledWith(nextFnSpy, submodules, { ...consentData, hasValidated: true }); + expect(enrichEidsRule(activityParams(MODULE_TYPE_UID, 'sampleUserId'))).to.not.exist; }); it('should allow userId module if gdpr not in scope', function () { - let submodules = [{ - submodule: { - gvlid: 1, - name: 'sampleUserId' - } - }]; gvlids.sampleUserId = 1; - let consentData = null; - userIdHook(nextFnSpy, submodules, consentData); - // Should not pass back hasValidated flag since version 2 - const args = nextFnSpy.getCalls()[0].args; - expect(args[1]).to.be.null; - expect(nextFnSpy.calledOnce).to.equal(true); - sinon.assert.calledWith(nextFnSpy, submodules, consentData); + const consent = setupConsentData({gdprApplies: false}); + consent.vendorData.purpose.consents['1'] = false; + expect(enrichEidsRule(activityParams(MODULE_TYPE_UID, 'sampleUserId'))).to.not.exist; }); it('should not allow user id module if user denied consent', function () { @@ -496,72 +333,23 @@ describe('gdpr enforcement', function () { }] } }); - let consentData = {} - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 2; - consentData.gdprApplies = true; - - let submodules = [{ - submodule: { - gvlid: 1, - name: 'sampleUserId' - } - }, { - submodule: { - gvlid: 3, - name: 'sampleUserId1' - } - }] + setupConsentData(); Object.assign(gvlids, { sampleUserId: 1, sampleUserId1: 3 }); - userIdHook(nextFnSpy, submodules, consentData); - expect(logWarnSpy.callCount).to.equal(1); - let expectedSubmodules = [{ - submodule: { - gvlid: 1, - name: 'sampleUserId' - } - }] - sinon.assert.calledWith(nextFnSpy, expectedSubmodules, { ...consentData, hasValidated: true }); + Object.entries({ + sampleUserId: true, + sampleUserId1: false + }).forEach(([name, allow]) => { + expectAllow(allow, enrichEidsRule(activityParams(MODULE_TYPE_UID, name))) + }); }); }); - describe('makeBidRequestsHook', function () { - let sandbox; - let adapterManagerStub; - let emitEventSpy; - - const MOCK_AD_UNITS = [{ - code: 'ad-unit-1', - mediaTypes: {}, - bids: [{ - bidder: 'bidder_1' // has consent - }, { - bidder: 'bidder_2' // doesn't have consent, but liTransparency is true. Bidder remains active. - }] - }, { - code: 'ad-unit-2', - mediaTypes: {}, - bids: [{ - bidder: 'bidder_2' - }, { - bidder: 'bidder_3' - }] - }]; - - beforeEach(function () { - sandbox = sinon.createSandbox(); - gdprDataHandlerStub = sandbox.stub(gdprDataHandler, 'getConsentData'); - adapterManagerStub = sandbox.stub(adapterManager, 'getBidAdapter'); - logWarnSpy = sandbox.spy(utils, 'logWarn'); - nextFnSpy = sandbox.spy(); - emitEventSpy = sandbox.spy(events, 'emit'); - }); + describe('fetchBidsRule', () => { afterEach(function () { config.resetConfig(); - sandbox.restore(); }); it('should block bidder which does not have consent and allow bidder which has consent (liTransparency is established)', function () { @@ -575,35 +363,12 @@ describe('gdpr enforcement', function () { }] } }); - const consentData = {}; - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 2; - consentData.gdprApplies = true; - - gdprDataHandlerStub.returns(consentData); + setupConsentData() Object.assign(gvlids, { bidder_1: 4, bidder_2: 5, }); - makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); - - // Assertions - expect(nextFnSpy.calledOnce).to.equal(true); - sinon.assert.calledWith(nextFnSpy, [{ - code: 'ad-unit-1', - mediaTypes: {}, - bids: [ - sinon.match({ bidder: 'bidder_1' }), - sinon.match({ bidder: 'bidder_2' }) - ] - }, { - code: 'ad-unit-2', - mediaTypes: {}, - bids: [ - sinon.match({ bidder: 'bidder_2' }), - sinon.match({ bidder: 'bidder_3' }) // should be allowed even though it's doesn't have a gvlId because liTransparency is established. - ] - }], []); + ['bidder_1', 'bidder_2', 'bidder_3'].forEach(bidder => expect(fetchBidsRule(activityParams(MODULE_TYPE_BIDDER, bidder))).to.not.exist); }); it('should block bidder which does not have consent and allow bidder which has consent (liTransparency is NOT established)', function() { @@ -617,41 +382,19 @@ describe('gdpr enforcement', function () { }] } }); - const consentData = {}; - - // set li for purpose 2 to false - const newConsentData = utils.deepClone(staticConfig); - newConsentData.consentData.getTCData.purpose.legitimateInterests['2'] = false; - - consentData.vendorData = newConsentData.consentData.getTCData; - consentData.apiVersion = 2; - consentData.gdprApplies = true; - - gdprDataHandlerStub.returns(consentData); + const consent = setupConsentData(); + consent.vendorData.purpose.legitimateInterests['2'] = false; Object.assign(gvlids, { bidder_1: 4, bidder_2: 5, }) - - makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); - - // Assertions - expect(nextFnSpy.calledOnce).to.equal(true); - sinon.assert.calledWith(nextFnSpy, [{ - code: 'ad-unit-1', - mediaTypes: {}, - bids: [ - sinon.match({ bidder: 'bidder_1' }), // 'bidder_2' is not present because it doesn't have vendorConsent - ] - }, { - code: 'ad-unit-2', - mediaTypes: {}, - bids: [ - sinon.match({ bidder: 'bidder_3' }), // 'bidder_3' is allowed despite gvlId being undefined because it's part of vendorExceptions - ] - }], []); - - expect(logWarnSpy.calledOnce).to.equal(true); + Object.entries({ + bidder_1: true, + bidder_2: false, + bidder_3: true + }).forEach(([bidder, allowed]) => { + expectAllow(allowed, fetchBidsRule(activityParams(MODULE_TYPE_BIDDER, bidder))); + }) }); it('should skip validation checks if GDPR version is not equal to "2"', function () { @@ -659,57 +402,36 @@ describe('gdpr enforcement', function () { gdpr: { rules: [{ purpose: 'storage', - enforePurpose: false, - enforceVendor: false, + enforcePurpose: true, + enforceVendor: true, vendorExceptions: [] }] } }); - - const consentData = {}; - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 1; - consentData.gdprApplies = true; - gdprDataHandlerStub.returns(consentData); - - makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); - - // Assertions - expect(nextFnSpy.calledOnce).to.equal(true); - sinon.assert.calledWith(nextFnSpy, sinon.match.array.deepEquals(MOCK_AD_UNITS), []); - expect(emitEventSpy.notCalled).to.equal(true); - expect(logWarnSpy.notCalled).to.equal(true); - }); - }); - - describe('enableAnalyticsHook', function () { - let sandbox; - let adapterManagerStub; - - const MOCK_ANALYTICS_ADAPTER_CONFIG = [{ - provider: 'analyticsAdapter_A', - options: {} - }, { - provider: 'analyticsAdapter_B', - options: {} - }, { - provider: 'analyticsAdapter_C', - options: {} - }]; - - beforeEach(function () { - sandbox = sinon.createSandbox(); - gdprDataHandlerStub = sandbox.stub(gdprDataHandler, 'getConsentData'); - adapterManagerStub = sandbox.stub(adapterManager, 'getAnalyticsAdapter'); - logWarnSpy = sandbox.spy(utils, 'logWarn'); - nextFnSpy = sandbox.spy(); + const consent = setupConsentData(); + consent.vendorData.purpose.consents['2'] = false; + consent.apiVersion = 1; + ['bidder_1', 'bidder_2', 'bidder_3'].forEach(bidder => expect(fetchBidsRule(activityParams(MODULE_TYPE_BIDDER, bidder))).to.not.exist); }); - afterEach(function() { - config.resetConfig(); - sandbox.restore(); - }); + it('should skip validation if enforcePurpose is false', () => { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: true, + vendorExceptions: [] + }] + } + }); + const consent = setupConsentData(); + consent.vendorData.purpose.consents['2'] = false; + ['bidder_1', 'bidder_2', 'bidder_3'].forEach(bidder => expect(fetchBidsRule(activityParams(MODULE_TYPE_BIDDER, bidder))).to.not.exist); + }) + }); + describe('reportAnalyticsRule', () => { it('should block analytics adapter which does not have consent and allow the one(s) which have consent', function() { setEnforcementConfig({ gdpr: { @@ -722,30 +444,21 @@ describe('gdpr enforcement', function () { } }); - const consentData = {}; - consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 2; - consentData.gdprApplies = true; - - gdprDataHandlerStub.returns(consentData); Object.assign(gvlids, { analyticsAdapter_A: 3, analyticsAdapter_B: 5, analyticsAdapter_C: 1 }); - enableAnalyticsHook(nextFnSpy, MOCK_ANALYTICS_ADAPTER_CONFIG); + setupConsentData() - // Assertions - expect(nextFnSpy.calledOnce).to.equal(true); - sinon.assert.calledWith(nextFnSpy, [{ - provider: 'analyticsAdapter_B', - options: {} - }, { - provider: 'analyticsAdapter_C', - options: {} - }]); - expect(logWarnSpy.calledOnce).to.equal(true); + Object.entries({ + analyticsAdapter_A: false, + analyticsAdapter_B: true, + analyticsAdapter_C: true + }).forEach(([adapter, allow]) => { + expectAllow(allow, reportAnalyticsRule(activityParams(MODULE_TYPE_ANALYTICS, adapter))) + }) }); }); @@ -1150,7 +863,7 @@ describe('gdpr enforcement', function () { it('should return VENDORLESS_GVLID for core modules', () => { entry = {gvlid: 123}; - expect(getGvlid(MODULE_TYPE_CORE, MOCK_MODULE, fallbackFn)).to.equal(VENDORLESS_GVLID); + expect(getGvlid(MODULE_TYPE_PREBID, MOCK_MODULE, fallbackFn)).to.equal(VENDORLESS_GVLID); }); describe('multiple GVL IDs are found', () => { diff --git a/test/spec/modules/glimpseBidAdapter_spec.js b/test/spec/modules/glimpseBidAdapter_spec.js deleted file mode 100644 index 353e61c7859..00000000000 --- a/test/spec/modules/glimpseBidAdapter_spec.js +++ /dev/null @@ -1,422 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/glimpseBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from 'src/config'; -import { BANNER } from '../../../src/mediaTypes'; - -const ENDPOINT = 'https://market.glimpsevault.io/public/v1/prebid'; - -const nonStringValues = [null, undefined, 123, true, {}, [], () => {}]; -const nonArrayValues = [null, undefined, 123, true, {}, 'str', () => {}]; - -const mock = { - bidRequest: { - bidder: 'glimpse', - bidId: '26a80b71cfd671', - bidderRequestId: '133baeded6ac94', - auctionId: '96692a73-307b-44b8-8e4f-ddfb40341570', - adUnitCode: 'banner-div-a', - sizes: [[300, 250]], - params: { - pid: 'glimpse-demo-300x250', - }, - }, - bidderRequest: { - bidderCode: 'glimpse', - bidderRequestId: '133baeded6ac94', - auctionId: '96692a73-307b-44b8-8e4f-ddfb40341570', - timeout: 3000, - gdprConsent: { - consentString: - 'COzP517OzP517AcABBENAlCsAP_AAAAAAAwIF8NX-T5eL2vju2Zdt7JEaYwfZxyigOgThgQIsW8NwIeFbBoGP2EgHBG4JCQAGBAkkgCBAQMsHGBcCQAAgIgRiRKMYE2MjzNKBJJAigkbc0FACDVunsHS2ZCY70-8O__bPAviADAvUC-AAAAA.YAAAAAAAAAAA', - vendorData: {}, - gdprApplies: true, - }, - uspConsent: '1YYY', - refererInfo: { - numIframes: 0, - reachedTop: true, - page: 'https://demo.glimpseprotocol.io/prebid/desktop', - stack: ['https://demo.glimpseprotocol.io/prebid/desktop'], - }, - }, - bidResponse: { - auth: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6IndvcmxkISJ9.1p6T0ORhJ6riLprhXBGdzRhG3Q1egM27uFhPGNapPxs', - data: { - bids: [ - { - requestId: '133baeded6ac94', - creativeId: 'glimpse-demo-300x250', - adUnitCode: 'banner-div-a', - currency: 'GBP', - ad: '
Hello, World!
', - width: 300, - height: 250, - cpm: 1.04, - netRevenue: true, - mediaType: 'banner', - ttl: 300, - }, - ], - }, - }, -}; - -const getBidRequest = () => getDeepCopy(mock.bidRequest); -const getBidderRequest = () => ({ - bids: [getBidRequest()], - ...getDeepCopy(mock.bidderRequest), -}); - -const getBidResponse = () => ({ - body: getDeepCopy(mock.bidResponse), -}); - -function getDeepCopy(object) { - return JSON.parse(JSON.stringify(object)); -} - -describe('GlimpseProtocolAdapter', () => { - const glimpseAdapter = newBidder(spec); - - describe('spec', () => { - it('Has defined the glimpse gvlid', () => { - expect(spec.gvlid).to.equal(1012); - }); - - it('Has defined glimpse as the bidder', () => { - expect(spec.code).to.equal('glimpse'); - }); - - it('Has defined valid mediaTypes', () => { - expect(spec.supportedMediaTypes).to.deep.equal([BANNER]); - }); - }); - - describe('Inherited functions', () => { - it('Functions exist and are valid types', () => { - expect(glimpseAdapter.callBids).to.exist.and.to.be.a('function'); - expect(glimpseAdapter.getSpec).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', () => { - it('Returns true if placement id is non-empty string', () => { - const bidRequest = getBidRequest(); - - const isValidBidRequest = spec.isBidRequestValid(bidRequest); - expect(isValidBidRequest).to.be.true; - }); - - it('Returns false if no pid is provided', () => { - const bidRequest = getBidRequest(); - delete bidRequest.params.pid; - - const isValidBidRequest = spec.isBidRequestValid(bidRequest); - expect(isValidBidRequest).to.be.false; - }); - - it('Returns false if pid is empty string', () => { - const bidRequest = getBidRequest(); - bidRequest.params.pid = ''; - - const isValidBidRequest = spec.isBidRequestValid(bidRequest); - expect(isValidBidRequest).to.be.false; - }); - - it('Returns false if pid is not string', () => { - const bidRequest = getBidRequest(); - const invalidPids = nonStringValues; - - invalidPids.forEach((invalidPid) => { - bidRequest.params.pid = invalidPid; - const isValidBidRequest = spec.isBidRequestValid(bidRequest); - expect(isValidBidRequest).to.be.false; - }); - }); - }); - - describe('buildRequests', () => { - const bidRequests = [getBidRequest()]; - const bidderRequest = getBidderRequest(); - - it('Adds additional info to api request query', () => { - const request = spec.buildRequests(bidRequests, bidderRequest); - const url = new URL(request.url); - const queries = new URLSearchParams(url.search); - - expect(queries.get('ver')).to.exist; - expect(queries.get('tmax')).to.exist; - expect(queries.get('gdpr')).to.equal( - bidderRequest.gdprConsent.consentString - ); - expect(queries.get('ccpa')).to.equal(bidderRequest.uspConsent); - }); - - it('Has correct payload shape', () => { - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.auth).to.be.a('string'); - expect(payload.data).to.be.an('object'); - expect(payload.data.referer).to.be.a('string'); - expect(payload.data.imp).to.be.an('array'); - expect(payload.data.fpd).to.be.an('object'); - }); - - it('Has referer information', () => { - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - const expected = mock.bidderRequest.refererInfo.page; - - expect(payload.data.referer).to.equal(expected); - }); - - it('Has correct bids (imp) shape', () => { - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - const imp = payload.data.imp; - - imp.forEach((i) => { - expect(i.bid).to.be.a('string'); - expect(i.pid).to.be.a('string'); - expect(i.sizes).to.be.an('array').that.deep.include([300, 250]); - }); - }); - }); - - describe('interpretResponse', () => { - it('Returns valid bids', () => { - const bidResponse = getBidResponse(); - const bids = spec.interpretResponse(bidResponse); - - expect(bids).to.have.lengthOf(1); - expect(bids[0].adUnitCode).to.equal(mock.bidRequest.adUnitCode); - }); - - it('Returns no bids if auth is not string', () => { - const bidResponse = getBidResponse(); - const invalidAuths = nonStringValues; - - invalidAuths.forEach((invalidAuth) => { - bidResponse.body.auth = invalidAuth; - - const bids = spec.interpretResponse(bidResponse); - expect(bids).to.have.lengthOf(0); - }); - }); - - it('Returns no bids if bids is empty', () => { - const bidResponse = getBidResponse(); - bidResponse.body.data.bids = []; - - const bids = spec.interpretResponse(bidResponse); - expect(bids).to.have.lengthOf(0); - }); - - it('Returns no bids if bids is not array', () => { - const bidResponse = getBidResponse(); - const invalidBids = nonArrayValues; - - invalidBids.forEach((invalidBid) => { - bidResponse.body.data.bids = invalidBid; - - const bids = spec.interpretResponse(bidResponse); - expect(bids).to.have.lengthOf(0); - }); - }); - - it('Contains advertiserDomains', () => { - const bidResponse = getBidResponse(); - - const bids = spec.interpretResponse(bidResponse); - bids.forEach((bid) => { - expect(bid.meta.advertiserDomains).to.be.an('array'); - }); - }); - }); - - describe('optimize request fpd data', () => { - const bidRequests = [getBidRequest()]; - const bidderRequest = getBidderRequest(); - - const fpdMockBase = { - site: { - keywords: 'site,keywords', - ext: { - data: { - fpdProvider: { - dataArray: ['data1', 'data2'], - dataObject: { - data1: 'data1', - data2: 'data2', - }, - dataString: 'data1,data2', - }, - }, - }, - }, - user: { - keywords: 'user,keywords', - ext: { - data: { - fpdProvider: { - dataArray: ['data1', 'data2'], - dataObject: { - data1: 'data1', - data2: 'data2', - }, - dataString: 'data1,data2', - }, - }, - }, - }, - }; - - it('should keep all non-empty fields', () => { - const fpdMock = fpdMockBase; - const expected = fpdMockBase; - - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: fpdMock}); - const payload = JSON.parse(request.data); - const fpd = payload.data.fpd; - - expect(fpd).to.deep.equal(expected); - }); - - it('should remove all empty objects', () => { - const fpdMock = getDeepCopy(fpdMockBase); - fpdMock.site.ext.data.fpdProvider.dataObject = {}; - fpdMock.user.ext.data.fpdProvider = {}; - - const expected = { - site: { - keywords: 'site,keywords', - ext: { - data: { - fpdProvider: { - dataArray: ['data1', 'data2'], - dataString: 'data1,data2', - }, - }, - }, - }, - user: { - keywords: 'user,keywords', - }, - }; - - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: fpdMock}); - const payload = JSON.parse(request.data); - const fpd = payload.data.fpd; - - expect(fpd).to.deep.equal(expected); - }); - - it('should remove all empty arrays', () => { - const fpdMock = getDeepCopy(fpdMockBase); - fpdMock.site.ext.data.fpdProvider.dataArray = []; - fpdMock.user.ext.data.fpdProvider.dataArray = []; - - const expected = { - site: { - keywords: 'site,keywords', - ext: { - data: { - fpdProvider: { - dataObject: { - data1: 'data1', - data2: 'data2', - }, - dataString: 'data1,data2', - }, - }, - }, - }, - user: { - keywords: 'user,keywords', - ext: { - data: { - fpdProvider: { - dataObject: { - data1: 'data1', - data2: 'data2', - }, - dataString: 'data1,data2', - }, - }, - }, - }, - }; - - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: fpdMock}); - const payload = JSON.parse(request.data); - const fpd = payload.data.fpd; - - expect(fpd).to.deep.equal(expected); - }); - - it('should remove all empty strings', () => { - const fpdMock = getDeepCopy(fpdMockBase); - fpdMock.site.keywords = ''; - fpdMock.site.ext.data.fpdProvider.dataString = ''; - fpdMock.user.keywords = ''; - fpdMock.user.ext.data.fpdProvider.dataString = ''; - - const expected = { - site: { - ext: { - data: { - fpdProvider: { - dataArray: ['data1', 'data2'], - dataObject: { - data1: 'data1', - data2: 'data2', - }, - }, - }, - }, - }, - user: { - ext: { - data: { - fpdProvider: { - dataArray: ['data1', 'data2'], - dataObject: { - data1: 'data1', - data2: 'data2', - }, - }, - }, - }, - }, - }; - - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: fpdMock}); - const payload = JSON.parse(request.data); - const fpd = payload.data.fpd; - - expect(fpd).to.deep.equal(expected); - }); - - it('should remove all empty fields', () => { - const fpdMock = getDeepCopy(fpdMockBase); - fpdMock.site.keywords = ''; - fpdMock.site.ext.data.fpdProvider.dataArray = []; - fpdMock.site.ext.data.fpdProvider.dataObject = {}; - fpdMock.site.ext.data.fpdProvider.dataString = ''; - fpdMock.user.keywords = ''; - fpdMock.user.ext.data.fpdProvider.dataArray = []; - fpdMock.user.ext.data.fpdProvider.dataObject = {}; - fpdMock.user.ext.data.fpdProvider.dataString = ''; - - const expected = {}; - - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: fpdMock}); - const payload = JSON.parse(request.data); - const fpd = payload.data.fpd; - - expect(fpd).to.deep.equal(expected); - }); - }); -}); diff --git a/test/spec/modules/gmosspBidAdapter_spec.js b/test/spec/modules/gmosspBidAdapter_spec.js index 6d290504194..8c3aa6c94cb 100644 --- a/test/spec/modules/gmosspBidAdapter_spec.js +++ b/test/spec/modules/gmosspBidAdapter_spec.js @@ -50,7 +50,11 @@ describe('GmosspAdapter', function () { bidId: '2b84475b5b636e', bidderRequestId: '1f4001782ac16c', auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', - transactionId: '791e9d84-af92-4903-94da-24c7426d9d0c', + ortb2Imp: { + ext: { + tid: '791e9d84-af92-4903-94da-24c7426d9d0c', + } + }, userId: { imuid: 'h.0a4749e7ffe09fa6', pubcid: '1111', diff --git a/test/spec/modules/gnetBidAdapter_spec.js b/test/spec/modules/gnetBidAdapter_spec.js index 21526aba201..f1af3b71103 100644 --- a/test/spec/modules/gnetBidAdapter_spec.js +++ b/test/spec/modules/gnetBidAdapter_spec.js @@ -63,7 +63,11 @@ describe('gnetAdapter', function () { bidId: '2a19afd5173318', bidderRequestId: '1f4001782ac16c', auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', - transactionId: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5', + ortb2Imp: { + ext: { + tid: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5', + } + }, gftuid: null }]; diff --git a/test/spec/modules/goldbachBidAdapter_spec.js b/test/spec/modules/goldbachBidAdapter_spec.js index fc2c1beb900..93956d2caf9 100644 --- a/test/spec/modules/goldbachBidAdapter_spec.js +++ b/test/spec/modules/goldbachBidAdapter_spec.js @@ -1001,15 +1001,6 @@ describe('GoldbachXandrAdapter', function () { }) describe('interpretResponse', function () { - let bfStub; - before(function() { - bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); - }); - - after(function() { - bfStub.restore(); - }); - let response = { 'version': '3.0.0', 'tags': [ @@ -1206,7 +1197,6 @@ describe('GoldbachXandrAdapter', function () { } }] }; - bfStub.returns('1'); let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(result[0]).to.have.property('vastUrl'); diff --git a/test/spec/modules/googleAnalyticsAdapter_spec.js b/test/spec/modules/googleAnalyticsAdapter_spec.js deleted file mode 100644 index b801b5fe696..00000000000 --- a/test/spec/modules/googleAnalyticsAdapter_spec.js +++ /dev/null @@ -1,27 +0,0 @@ -import ga from 'modules/googleAnalyticsAdapter.js'; - -var assert = require('assert'); - -describe('Ga', function () { - describe('enableAnalytics', function () { - var cpmDistribution = function(cpm) { - return cpm <= 1 ? '<= 1$' : '> 1$'; - } - var config = { options: { trackerName: 'foo', enableDistribution: true, cpmDistribution: cpmDistribution } }; - - // enableAnalytics can only be called once - ga.enableAnalytics(config); - - it('should accept a tracker name option and output prefixed send string', function () { - var output = ga.getTrackerSend(); - assert.equal(output, 'foo.send'); - }); - - it('should use the custom cpm distribution', function() { - assert.equal(ga.getCpmDistribution(0.5), '<= 1$'); - assert.equal(ga.getCpmDistribution(1), '<= 1$'); - assert.equal(ga.getCpmDistribution(2), '> 1$'); - assert.equal(ga.getCpmDistribution(5.23), '> 1$'); - }); - }); -}); diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 775e1a98a9c..4a1148bf377 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -22,7 +22,6 @@ describe('TheMediaGrid Adapter', function () { 'sizes': [[300, 250], [300, 600]], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', }; it('should return true when required params found', function () { @@ -47,8 +46,13 @@ describe('TheMediaGrid Adapter', function () { refererInfo: { page: 'https://example.com' }, bidderRequestId: '22edbae2733bf6', transactionId: '1239bd74-4511-4335-af21-e828852e25d7', + timeout: 3000, auctionId: '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', - timeout: 3000 + ortb2: { + source: { + tid: '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + } + } }; const referrer = encodeURIComponent(bidderRequest.refererInfo.page); let bidRequests = [ @@ -69,6 +73,11 @@ describe('TheMediaGrid Adapter', function () { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', transactionId: '1239bd74-4511-4335-af21-e828852e25d7', + ortb2Imp: { + ext: { + tid: '1239bd74-4511-4335-af21-e828852e25d7', + } + } }, { 'bidder': 'grid', @@ -81,6 +90,11 @@ describe('TheMediaGrid Adapter', function () { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', transactionId: '1239bd74-4511-4335-af21-e828852e25d7', + ortb2Imp: { + ext: { + tid: '1239bd74-4511-4335-af21-e828852e25d7', + } + } }, { 'bidder': 'grid', @@ -99,6 +113,11 @@ describe('TheMediaGrid Adapter', function () { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', transactionId: '1239bd74-4511-4335-af21-e828852e25d7', + ortb2Imp: { + ext: { + tid: '1239bd74-4511-4335-af21-e828852e25d7', + } + } }, { 'bidder': 'grid', @@ -120,6 +139,11 @@ describe('TheMediaGrid Adapter', function () { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', transactionId: '1239bd74-4511-4335-af21-e828852e25d7', + ortb2Imp: { + ext: { + tid: '1239bd74-4511-4335-af21-e828852e25d7', + } + } } ]; @@ -152,7 +176,7 @@ describe('TheMediaGrid Adapter', function () { }, 'tmax': bidderRequest.timeout, 'source': { - 'tid': bidderRequest.auctionId, + 'tid': bidderRequest.ortb2.source.tid, 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, 'user': { @@ -400,169 +424,6 @@ describe('TheMediaGrid Adapter', function () { getDataFromLocalStorageStub.restore(); }); - it('should send additional request with adUnits with withCriteo parameter', function () { - const fpdUserIdVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; - const getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake( - arg => arg === 'tmguid' ? fpdUserIdVal : null); - - const bidRequestsWithCriteo = bidRequests.map((bid, i) => - i % 2 ? bid : { - ...bid, - params: { - ...bid.params, - withCriteo: true, - networkId: 123 + i - }, - mediaTypes: { - ...bid.mediaTypes, - ...(bid.mediaTypes.video && { video: { - ...bid.mediaTypes.video, - context: 'instream', - protocols: [1, 2, 3], - maxduration: 30, - api: [1, 2], - skip: 1, - placement: 1, - plcmt: 2, - minduration: 0, - playbackmethod: 1, - startdelay: 0 - } - }) - } - }); - - const [request, criteoRequest] = spec.buildRequests(bidRequestsWithCriteo, bidderRequest); - expect(request.data).to.be.an('string'); - expect(criteoRequest.data).to.be.an('object'); - const payload = parseRequest(request.data); - const criteoPayload = criteoRequest.data; - expect(request.url).to.equal('https://grid.bidswitch.net/hbjson'); - expect(criteoRequest.url.replace(/\?.*$/, '')) - .to.equal('https://bidder.criteo.com/cdb'); - expect(payload).to.deep.equal({ - 'id': bidderRequest.bidderRequestId, - 'site': { - 'page': referrer - }, - 'tmax': bidderRequest.timeout, - 'source': { - 'tid': bidderRequest.auctionId, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} - }, - 'user': { - 'id': fpdUserIdVal - }, - 'imp': [{ - 'id': bidRequests[0].bidId, - 'tagid': bidRequests[0].params.uid, - 'ext': {'divid': bidRequests[0].adUnitCode}, - 'bidfloor': bidRequests[0].params.bidFloor, - 'banner': { - 'w': 300, - 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] - } - }, { - 'id': bidRequests[1].bidId, - 'tagid': bidRequests[1].params.uid, - 'ext': {'divid': bidRequests[1].adUnitCode}, - 'banner': { - 'w': 300, - 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] - } - }, { - 'id': bidRequests[2].bidId, - 'tagid': bidRequests[2].params.uid, - 'ext': {'divid': bidRequests[2].adUnitCode}, - 'video': { - 'w': 400, - 'h': 600, - 'protocols': [1, 2, 3], - 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'], - 'context': 'instream', - 'maxduration': 30, - 'minduration': 0, - 'api': [1, 2], - 'skip': 1, - 'placement': 1, - 'plcmt': 2, - 'playbackmethod': 1, - 'startdelay': 0 - } - }, { - 'id': bidRequests[3].bidId, - 'tagid': bidRequests[3].params.uid, - 'ext': {'divid': bidRequests[3].adUnitCode}, - 'banner': { - 'w': 728, - 'h': 90, - 'format': [{'w': 728, 'h': 90}] - }, - 'video': { - 'w': 400, - 'h': 600, - 'protocols': [1, 2, 3] - } - }] - }); - - expect(criteoPayload).to.deep.equal({ - 'publisher': { - 'ext': undefined, - 'url': bidderRequest.refererInfo.page, - 'networkid': 125 - }, - 'regs': { - 'coppa': undefined - }, - 'slots': [{ - 'impid': bidRequests[0].adUnitCode, - 'transactionid': bidderRequest.transactionId, - 'auctionId': bidderRequest.auctionId, - 'sizes': ['300x250', '300x600'] - }, { - 'impid': bidRequests[2].adUnitCode, - 'transactionid': bidderRequest.transactionId, - 'auctionId': bidderRequest.auctionId, - 'sizes': [], - 'video': { - 'api': [ - 1, - 2 - ], - 'maxduration': 30, - 'mimes': [ - 'video/mp4', - 'video/webm', - 'application/javascript', - 'video/ogg', - ], - 'minduration': 0, - 'placement': 1, - 'plcmt': 2, - 'playbackmethod': 1, - 'playersizes': [ - '400x600' - ], - 'protocols': [ - 1, - 2, - 3 - ], - 'skip': 1, - 'startdelay': 0 - } - }], - 'user': { - 'ext': undefined - } - }); - - getDataFromLocalStorageStub.restore(); - }); - it('if gdprConsent is present payload must have gdpr params', function () { const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); const [request] = spec.buildRequests(bidRequests, gdprBidderRequest); @@ -600,7 +461,13 @@ describe('TheMediaGrid Adapter', function () { it('should add gpp information to the request via bidderRequest.ortb2.regs.gpp', function () { let consentString = 'abc1234'; - const gppBidderRequest = Object.assign({ortb2: {regs: {gpp: consentString, gpp_sid: [8]}}}, bidderRequest); + const gppBidderRequest = { + ...bidderRequest, + ortb2: { + regs: {gpp: consentString, gpp_sid: [8]}, + ...bidderRequest.ortb2 + } + }; const [request] = spec.buildRequests(bidRequests, gppBidderRequest); const payload = JSON.parse(request.data); @@ -897,7 +764,7 @@ describe('TheMediaGrid Adapter', function () { } }]; const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { - return Object.assign(ortb2Imp[ind] ? { ortb2Imp: ortb2Imp[ind] } : {}, bid); + return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: {...bid.ortb2Imp, ...ortb2Imp[ind]} } : {}); }); const [request] = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); expect(request.data).to.be.an('string'); @@ -934,7 +801,7 @@ describe('TheMediaGrid Adapter', function () { } }]; const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { - return Object.assign(ortb2Imp[ind] ? { ortb2Imp: ortb2Imp[ind] } : {}, bid); + return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: ortb2Imp[ind] } : {}); }); const [request] = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); expect(request.data).to.be.an('string'); @@ -966,8 +833,12 @@ describe('TheMediaGrid Adapter', function () { const bidderRequestWithNumId = { refererInfo: {page: 'https://example.com'}, bidderRequestId: 345345345, - auctionId: 654645, - timeout: 3000 + timeout: 3000, + ortb2: { + source: { + tid: 654645 + } + } }; const parsedReferrer = encodeURIComponent(bidderRequestWithNumId.refererInfo.page); const [request] = spec.buildRequests([bidRequestWithNumId], bidderRequestWithNumId); @@ -1529,137 +1400,6 @@ describe('TheMediaGrid Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('should add response with biggest price', function () { - const mainResponse = [ - {'bid': [{'impid': '2164be6358b9', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, - {'bid': [{'impid': '4e111f1b66e4', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, - ]; - const criteoResponse = [ - { - impid: 'adunit-code-1', - cpm: 0.15, - creative: '
test content 3
', - creativecode: 1, - bidId: '2164be6358b9', - currency: 'USD', - width: 300, - height: 250, - dealCode: 11, - zoneid: 123, - ttl: 360, - adomain: ['criteo.com'], - }, - { - impid: 'adunit-code-1', - cpm: 1, - creative: '
test content 4
', - creativecode: 2, - bidId: '4e111f1b66e4', - currency: 'USD', - width: 300, - height: 600, - dealCode: 12, - zoneid: 456, - ttl: 360, - adomain: ['criteo.com'], - } - ]; - const bidRequests = [ - { - 'bidder': 'grid', - 'params': { - 'uid': '1', - 'zoneId': 123, - 'withCriteo': true - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '2164be6358b9', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - 'transactionId': '43534f55b213ac', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '2', - 'zoneId': 456, - 'withCriteo': true - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '4e111f1b66e4', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - 'transactionId': '43534f55b213ac', - } - ]; - const bidderRequest = { - refererInfo: { page: 'https://example.com' }, - bidderRequestId: '106efe3247', - transactionId: '43534f55b213ac', - auctionId: '32a1f276cb87cb8', - timeout: 3000 - }; - const [mainRequest, criteoRequest] = spec.buildRequests(bidRequests, bidderRequest); - const expectedMainResponse = [ - { - 'requestId': '2164be6358b9', - 'cpm': 1.15, - 'creativeId': 1, - 'dealId': 11, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'currency': 'USD', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - 'meta': { - advertiserDomains: [] - }, - }, - { - 'requestId': '4e111f1b66e4', - 'cpm': 0.5, - 'creativeId': 2, - 'dealId': 12, - 'width': 300, - 'height': 600, - 'ad': '
test content 2
', - 'currency': 'USD', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - 'meta': { - advertiserDomains: [] - }, - } - ]; - const expectedCriteoResponse = [ - { - 'requestId': '4e111f1b66e4', - 'cpm': 1, - 'creativeId': 2, - 'dealId': 12, - 'width': 300, - 'height': 600, - 'ad': '
test content 4
', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 360, - 'meta': { - advertiserDomains: ['criteo.com'] - }, - } - ]; - - const mainResult = spec.interpretResponse({'body': {'seatbid': mainResponse}}, mainRequest); - const criteoResult = spec.interpretResponse({'body': {'slots': criteoResponse}}, criteoRequest); - expect(mainResult).to.deep.equal(expectedMainResponse); - expect(criteoResult).to.deep.equal(expectedCriteoResponse); - }); - it('response with ext.bidder.grid.demandSource', function () { const bidRequests = [ { diff --git a/test/spec/modules/growthCodeRtdProvider_spec.js b/test/spec/modules/growthCodeRtdProvider_spec.js new file mode 100644 index 00000000000..31e1efc5487 --- /dev/null +++ b/test/spec/modules/growthCodeRtdProvider_spec.js @@ -0,0 +1,127 @@ +import {config} from 'src/config.js'; +import {growthCodeRtdProvider} from '../../../modules/growthCodeRtdProvider'; +import sinon from 'sinon'; +import * as ajaxLib from 'src/ajax.js'; + +const sampleConfig = { + name: 'growthCodeRtd', + waitForIt: true, + params: { + pid: 'TEST01', + } +} + +describe('growthCodeRtdProvider', function() { + beforeEach(function() { + config.resetConfig(); + }); + + afterEach(function () { + }); + + describe('growthCodeRtdSubmodule', function() { + it('test bad config instantiates', function () { + const ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return (url, cbObj) => { + cbObj.success('{"status":"ok","version":"1.0.0","results":1,"items":[{"bidder":"client_a","attachment_point":"data","parameters":"{\\"client_a\\":{\\"user\\":{\\"ext\\":{\\"data\\":{\\"eids\\":[{\\"source\\":\\"\\",\\"uids\\":[{\\"id\\":\\"4254074976bb6a6d970f5f693bd8a75c\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemmd5\\"}},{\\"id\\":\\"d0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemsha256\\"}}]}]}}}}}"}],"expires_at":1685029931}') + } + }); + expect(growthCodeRtdProvider.init(null, null)).to.equal(false); + ajaxStub.restore() + }); + it('successfully instantiates', function () { + const ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return (url, cbObj) => { + cbObj.success('{"status":"ok","version":"1.0.0","results":1,"items":[{"bidder":"client_a","attachment_point":"data","parameters":"{\\"client_a\\":{\\"user\\":{\\"ext\\":{\\"data\\":{\\"eids\\":[{\\"source\\":\\"\\",\\"uids\\":[{\\"id\\":\\"4254074976bb6a6d970f5f693bd8a75c\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemmd5\\"}},{\\"id\\":\\"d0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemsha256\\"}}]}]}}}}}"}],"expires_at":1685029931}') + } + }); + expect(growthCodeRtdProvider.init(sampleConfig, null)).to.equal(true); + ajaxStub.restore() + }); + it('successfully instantiates (cached)', function () { + const ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return (url, cbObj) => { + cbObj.success('{"status":"ok","version":"1.0.0","results":1,"items":[{"bidder":"client_a","attachment_point":"data","parameters":"{\\"client_a\\":{\\"user\\":{\\"ext\\":{\\"data\\":{\\"eids\\":[{\\"source\\":\\"\\",\\"uids\\":[{\\"id\\":\\"4254074976bb6a6d970f5f693bd8a75c\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemmd5\\"}},{\\"id\\":\\"d0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemsha256\\"}}]}]}}}}}"}],"expires_at":1685029931}') + } + }); + const localStoreItem = '[{"bidder":"client_a","attachment_point":"data","parameters":"{\\"client_a\\":{\\"user\\":{\\"ext\\":{\\"data\\":{\\"eids\\":[{\\"source\\":\\"\\",\\"uids\\":[{\\"id\\":\\"4254074976bb6a6d970f5f693bd8a75c\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemmd5\\"}},{\\"id\\":\\"d0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemsha256\\"}}]}]}}}}}"}]' + expect(growthCodeRtdProvider.callServer(sampleConfig, localStoreItem, '1965949885', null)).to.equal(true); + ajaxStub.restore() + }); + it('successfully instantiates (cached,expire)', function () { + const ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return (url, cbObj) => { + cbObj.success('{"status":"ok","version":"1.0.0","results":1,"items":[{"bidder":"client_a","attachment_point":"data","parameters":"{\\"client_a\\":{\\"user\\":{\\"ext\\":{\\"data\\":{\\"eids\\":[{\\"source\\":\\"\\",\\"uids\\":[{\\"id\\":\\"4254074976bb6a6d970f5f693bd8a75c\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemmd5\\"}},{\\"id\\":\\"d0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemsha256\\"}}]}]}}}}}"}],"expires_at":1685029931}') + } + }); + const localStoreItem = '[{"bidder":"client_a","attachment_point":"data","parameters":"{\\"client_a\\":{\\"user\\":{\\"ext\\":{\\"data\\":{\\"eids\\":[{\\"source\\":\\"\\",\\"uids\\":[{\\"id\\":\\"4254074976bb6a6d970f5f693bd8a75c\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemmd5\\"}},{\\"id\\":\\"d0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898\\",\\"atype\\":3,\\"ext\\":{\\"stype\\":\\"hemsha256\\"}}]}]}}}}}"}]' + expect(growthCodeRtdProvider.callServer(sampleConfig, localStoreItem, '1679188732', null)).to.equal(true); + ajaxStub.restore() + }); + + it('test no items response', function () { + const ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return (url, cbObj) => { + cbObj.success('{}') + } + }); + expect(growthCodeRtdProvider.callServer(sampleConfig, null, '1679188732', null)).to.equal(true); + ajaxStub.restore(); + }); + + it('ajax error response', function () { + const ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return (url, cbObj) => { + cbObj.error(); + } + }); + expect(growthCodeRtdProvider.callServer(sampleConfig, null, '1679188732', null)).to.equal(true); + ajaxStub.restore(); + }); + + it('test alterBid data merge into ortb2 data (bidder)', function() { + const gcData = + { + 'client_a': + { + 'user': + {'ext': + {'data': + {'eids': [ + {'source': 'test.com', + 'uids': [ + { + 'id': '4254074976bb6a6d970f5f693bd8a75c', + 'atype': 3, + 'ext': { + 'stype': 'hemmd5'} + }, { + 'id': 'd0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898', + 'atype': 3, + 'ext': { + 'stype': 'hemsha256' + } + } + ] + } + ] + } + } + } + } + }; + + const payload = [ + { + 'bidder': 'client_a', + 'attachment_point': 'data', + 'parameters': JSON.stringify(gcData) + }] + + const bidConfig = {ortb2Fragments: {bidder: {}}}; + growthCodeRtdProvider.addData(bidConfig, payload) + + expect(bidConfig.ortb2Fragments.bidder).to.deep.equal(gcData) + }); + }); +}); diff --git a/test/spec/modules/hybridBidAdapter_spec.js b/test/spec/modules/hybridBidAdapter_spec.js index 6c98264c06f..a0d479fb4dc 100644 --- a/test/spec/modules/hybridBidAdapter_spec.js +++ b/test/spec/modules/hybridBidAdapter_spec.js @@ -8,7 +8,11 @@ function getSlotConfigs(mediaTypes, params) { bidId: '2df8c0733f284e', bidder: 'hybrid', mediaTypes: mediaTypes, - transactionId: '31a58515-3634-4e90-9c96-f86196db1459' + ortb2Imp: { + ext: { + tid: '31a58515-3634-4e90-9c96-f86196db1459' + } + } } } diff --git a/test/spec/modules/hypelabBidAdapter_spec.js b/test/spec/modules/hypelabBidAdapter_spec.js new file mode 100644 index 00000000000..4522073a2db --- /dev/null +++ b/test/spec/modules/hypelabBidAdapter_spec.js @@ -0,0 +1,281 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { server } from '../../mocks/xhr'; + +import { + mediaSize, + spec, + BIDDER_CODE, + ENDPOINT_URL, + REQUEST_ROUTE, +} from 'modules/hypelabBidAdapter.js'; + +import { BANNER } from 'src/mediaTypes.js'; + +const mockValidBidRequest = { + bidder: 'hypelab', + params: { + property_slug: 'prebid', + placement_slug: 'test_placement', + uuid: '', + sdk_version: '0.1.0', + provider_name: 'react', + provider_version: '0.3.1', + }, + userIds: [], + mediaTypes: { + banner: { + sizes: [[728, 90]], + }, + }, + adUnitCode: 'test-div', + sizes: [[728, 90]], + bidId: '24d2b2c86c5e19', + bidderRequestId: '1d1f40b509f18', + auctionId: '3bf3b1fb-cb0a-4ee8-90ef-69b8e6e56dbd', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, +}; + +const mockValidBidRequests = [mockValidBidRequest]; + +const mockServerResponse = { + body: { + status: 'success', + data: { + currency: 'USD', + campaign_slug: '9dbe230882', + creative_set_slug: '842984f045', + creative_set: { + image: { + url: 'https://cloudfront.net/up/asset/12345', + height: 90, + width: 728, + }, + }, + cpm: 1.5, + html: "\n \n \n \n Ad\n \n \n \n \n \n \n \n \n \n \n ", + advertiser_domains: ['ogx.com'], + media_type: 'banner', + ttl: 360, + }, + }, +}; + +const mockBidderRequest = { + bidderCode: 'hypelab', + auctionId: '4462005b-ba06-49a9-a95d-0209c22f4606', + bidderRequestId: '1bf399761210ad', + bids: mockValidBidRequests, + auctionStart: 1684983987435, + timeout: 2000, + refererInfo: { + topmostLocation: 'https://example.com/hello_world.html', + location: 'https://example.com/hello_world.html', + canonicalUrl: null, + page: 'https://example.com/hello_world.html', + domain: null, + ref: null, + }, +}; + +const mockBidRequest = { + method: 'POST', + url: 'https://api.hypelab.com/v1/prebid_requests', + options: { + contentType: 'application/json', + withCredentials: false, + }, + data: { + property_slug: 'prebid', + placement_slug: 'test_placement', + provider_version: '0.0.1', + provider_name: 'prebid', + referrer: 'https://example.com', + sdk_version: '7.51.0-pre', + sizes: [[728, 90]], + wids: [], + uuid: 'tmp_c5abf809-47d6-40b9-8274-372c6d816dd8', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + }, + bidId: '2e02b562f700ae', +}; + +describe('hypelabBidAdapter', function () { + describe('mediaSize', function () { + describe('when given an invalid media object', function () { + expect(mediaSize({})).to.eql({ width: 0, height: 0 }); + }); + + describe('when given a valid media object', function () { + expect( + mediaSize({ creative_set: { image: { width: 728, height: 90 } } }) + ).to.eql({ width: 728, height: 90 }); + }); + }); + + describe('isBidRequestValid', function () { + describe('when given an invalid bid request', function () { + expect(spec.isBidRequestValid({})).to.equal(false); + }); + + describe('when given a valid bid request', function () { + expect(spec.isBidRequestValid(mockValidBidRequest)).to.equal(true); + }); + }); + + describe('Bidder code valid', function () { + expect(spec.code).to.equal(BIDDER_CODE); + }); + + describe('Media types valid', function () { + expect(spec.supportedMediaTypes).to.contain(BANNER); + }); + + describe('Bid request valid', function () { + expect(spec.isBidRequestValid(mockValidBidRequest)).to.equal(true); + }); + + describe('buildRequests', () => { + describe('returns a valid request', function () { + const result = spec.buildRequests( + mockValidBidRequests, + mockBidderRequest + ); + expect(result).to.be.an('array'); + + const first = result[0] || {}; + expect(first).to.be.an('object'); + expect(first.method).to.equal('POST'); + expect(first.url).to.be.a('string'); + expect(first.url).to.equal(ENDPOINT_URL + REQUEST_ROUTE); + + const data = first.data || {}; + expect(data).to.be.an('object'); + expect(data.property_slug).to.be.a('string'); + expect(data.placement_slug).to.be.a('string'); + expect(data.bidRequestsCount).to.be.a('number'); + expect(data.bidderRequestsCount).to.be.a('number'); + expect(data.bidderWinsCount).to.be.a('number'); + }); + + describe('should set uuid to the first id in userIdAsEids', () => { + mockValidBidRequests[0].userIdAsEids = [ + { + source: 'pubcid.org', + uids: [ + { + id: 'pubcid_id', + }, + ], + }, + { + source: 'criteo.com', + uids: [ + { + id: 'criteo_id', + }, + ], + }, + ]; + + const result = spec.buildRequests( + mockValidBidRequests, + mockBidderRequest + ); + + const data = result[0].data || {}; + expect(data.uuid).to.be.eq('pubcid_id'); + }); + }); + + describe('interpretResponse', () => { + describe('successfully interpret a valid response', function () { + const result = spec.interpretResponse(mockServerResponse, mockBidRequest); + + expect(result).to.be.an('array'); + const data = result[0] || {}; + expect(data).to.be.an('object'); + expect(data.requestId).to.be.a('string'); + expect(data.cpm).to.be.a('number'); + expect(data.width).to.be.a('number'); + expect(data.height).to.be.a('number'); + expect(data.creativeId).to.be.a('string'); + expect(data.currency).to.be.a('string'); + expect(data.netRevenue).to.be.a('boolean'); + expect(data.referrer).to.be.a('string'); + expect(data.ttl).to.be.a('number'); + expect(data.ad).to.be.a('string'); + expect(data.mediaType).to.be.a('string'); + expect(data.meta.advertiserDomains).to.be.an('array'); + }); + + describe('should return a blank array if cpm is not set', () => { + mockServerResponse.body.data.cpm = undefined; + const result = spec.interpretResponse(mockServerResponse, mockBidRequest); + expect(result).to.eql([]); + }); + }); + + describe('report', () => { + it('returns if REPORTING_ROUTE is not set', () => { + spec.REPORTING_ROUTE = ''; + expect(spec.report('test', {})).to.be.undefined; + }); + + it('makes a POST request if REPORTING_ROUTE is set', () => { + spec.report('test', {}, '/v1/events'); + + expect(server.requests[0].url).to.equals( + 'https://api.hypelab.com/v1/events' + ); + }); + }); + + describe('callbacks', () => { + let bid = {}; + let reportStub; + + beforeEach(() => (reportStub = sinon.stub(spec, 'report'))); + afterEach(() => reportStub.restore()); + + describe('onTimeout', () => { + it('should call report with the correct data', () => { + spec.onTimeout(bid); + + expect(reportStub.calledOnce).to.be.true; + expect(reportStub.getCall(0).args).to.eql(['timeout', bid]); + }); + }); + + describe('onSetTargeting', () => { + it('should call report with the correct data', () => { + spec.onSetTargeting(bid); + + expect(reportStub.calledOnce).to.be.true; + expect(reportStub.getCall(0).args).to.eql(['setTargeting', bid]); + }); + }); + + describe('onBidWon', () => { + it('should call report with the correct data', () => { + spec.onBidWon(bid); + + expect(reportStub.calledOnce).to.be.true; + expect(reportStub.getCall(0).args).to.eql(['bidWon', bid]); + }); + }); + + describe('onBidderError', () => { + it('should call report with the correct data', () => { + spec.onBidderError(bid); + + expect(reportStub.calledOnce).to.be.true; + expect(reportStub.getCall(0).args).to.eql(['bidderError', bid]); + }); + }); + }); +}); diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 51954f76356..9db26ed91ce 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -42,7 +42,9 @@ describe('ID5 ID System', function () { const ID5_STORED_OBJ = { 'universal_uid': ID5_STORED_ID, 'signature': ID5_STORED_SIGNATURE, - 'link_type': ID5_STORED_LINK_TYPE + 'ext': { + 'linkType': ID5_STORED_LINK_TYPE + } }; const ID5_RESPONSE_ID = 'newid5id'; const ID5_RESPONSE_SIGNATURE = 'abcdef'; @@ -50,7 +52,10 @@ describe('ID5 ID System', function () { const ID5_JSON_RESPONSE = { 'universal_uid': ID5_RESPONSE_ID, 'signature': ID5_RESPONSE_SIGNATURE, - 'link_type': ID5_RESPONSE_LINK_TYPE + 'link_type': ID5_RESPONSE_LINK_TYPE, + 'ext': { + 'linkType': ID5_RESPONSE_LINK_TYPE + } }; const ALLOWED_ID5_VENDOR_DATA = { purpose: { @@ -958,7 +963,9 @@ describe('ID5 ID System', function () { it('should not expose ID when everyone is in control group', function () { storedObject.ab_testing = {result: 'control'}; storedObject.universal_uid = ''; - storedObject.link_type = 0; + storedObject.ext = { + 'linkType': 0 + }; let decoded = id5IdSubmodule.decode(storedObject, testConfig); expect(decoded).is.deep.equal(expectedDecodedObjectWithoutIdAbOn); }); diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/imdsBidAdapter_spec.js similarity index 98% rename from test/spec/modules/synacormediaBidAdapter_spec.js rename to test/spec/modules/imdsBidAdapter_spec.js index 747dc4edc63..ce04fabe02b 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/imdsBidAdapter_spec.js @@ -1,9 +1,10 @@ import { assert, expect } from 'chai'; import { BANNER } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; -import { spec } from 'modules/synacormediaBidAdapter.js'; +import { spec } from 'modules/imdsBidAdapter.js'; +import * as utils from 'src/utils.js'; -describe('synacormediaBidAdapter ', function () { +describe('imdsBidAdapter ', function () { describe('isBidRequestValid', function () { let bid; beforeEach(function () { @@ -115,7 +116,7 @@ describe('synacormediaBidAdapter ', function () { }); describe('buildRequests', function () { let validBidRequestVideo = { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', tagId: '1234', @@ -140,7 +141,7 @@ describe('synacormediaBidAdapter ', function () { }; let bidderRequestVideo = { - bidderCode: 'synacormedia', + bidderCode: 'imds', auctionId: 'VideoAuctionId124', bidderRequestId: '117954d20d7c9c', auctionStart: 1553624929697, @@ -177,7 +178,7 @@ describe('synacormediaBidAdapter ', function () { }; let bidderRequest = { - auctionId: 'xyz123', + bidderRequestId: 'xyz123', refererInfo: { referer: 'https://test.com/foo/bar' } @@ -192,7 +193,7 @@ describe('synacormediaBidAdapter ', function () { }; let bidderRequestWithCCPA = { - auctionId: 'xyz123', + bidderRequestId: 'xyz123', refererInfo: { referer: 'https://test.com/foo/bar' }, @@ -299,7 +300,6 @@ describe('synacormediaBidAdapter ', function () { expect(reqVideo).to.have.property('url'); expect(reqVideo.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); expect(reqVideo.data).to.exist.and.to.be.an('object'); - expect(reqVideo.data.id).to.equal('VideoAuctionId124'); expect(reqVideo.data.imp).to.eql([expectedDataVideo1]); }); @@ -543,7 +543,7 @@ describe('synacormediaBidAdapter ', function () { }); it('should use all the video params in the impression request', function () { let validBidRequestVideo = { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', tagId: '1234', @@ -601,7 +601,7 @@ describe('synacormediaBidAdapter ', function () { }); it('should move any video params in the mediaTypes object to params.video object', function () { let validBidRequestVideo = { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', tagId: '1234', @@ -659,7 +659,7 @@ describe('synacormediaBidAdapter ', function () { }); it('should create params.video object if not present on bid request and move any video params in the mediaTypes object to it', function () { let validBidRequestVideo = { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', tagId: '1234' @@ -731,7 +731,7 @@ describe('synacormediaBidAdapter ', function () { describe('Bid Requests with placementId should be backward compatible ', function () { let validVideoBidReq = { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', placementId: 'demo1', @@ -772,7 +772,7 @@ describe('synacormediaBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'synacormedia', + bidderCode: 'imds', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110' }; @@ -793,7 +793,7 @@ describe('synacormediaBidAdapter ', function () { describe('Bid Requests with schain object ', function () { let validBidReq = { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', tagId: 'demo1', @@ -835,12 +835,12 @@ describe('synacormediaBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'synacormedia', + bidderCode: 'imds', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110', bidderRequestId: '16d438671bfbec', bids: [ { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', tagId: 'demo1', @@ -1313,7 +1313,7 @@ describe('synacormediaBidAdapter ', function () { describe('Bid Requests with price module should use if available', function () { let validVideoBidRequest = { - bidder: 'synacormedia', + bidder: 'imds', params: { bidfloor: '0.50', seatId: 'prebid', @@ -1356,7 +1356,7 @@ describe('synacormediaBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'synacormedia', + bidderCode: 'imds', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110' }; @@ -1383,7 +1383,7 @@ describe('synacormediaBidAdapter ', function () { describe('Bid Requests with gpid or anything in bid.ext should use if available', function () { let validVideoBidRequest = { - bidder: 'synacormedia', + bidder: 'imds', params: { seatId: 'prebid', placementId: 'demo1', @@ -1440,7 +1440,7 @@ describe('synacormediaBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'synacormedia', + bidderCode: 'imds', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110' }; diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index 1c3bd3197d0..f427f9e7624 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -119,6 +119,11 @@ describe('Improve Digital Adapter Tests', function () { }; const bidderRequest = { + ortb2: { + source: { + tid: 'mock-tid' + } + }, bids: [simpleBidRequest], }; @@ -236,7 +241,7 @@ describe('Improve Digital Adapter Tests', function () { expect(payload.tmax).not.to.exist; expect(payload.regs).to.not.exist; expect(payload.schain).to.not.exist; - sinon.assert.match(payload.source, {tid: 'f183e871-fbed-45f0-a427-c8a63c4c01eb'}) + sinon.assert.match(payload.source, {tid: 'mock-tid'}) expect(payload.device).to.be.an('object'); expect(payload.user).to.not.exist; sinon.assert.match(payload.imp, [ diff --git a/test/spec/modules/inmarBidAdapter_spec.js b/test/spec/modules/inmarBidAdapter_spec.js deleted file mode 100644 index d21fcbc377b..00000000000 --- a/test/spec/modules/inmarBidAdapter_spec.js +++ /dev/null @@ -1,242 +0,0 @@ -// import or require modules necessary for the test, e.g.: -import {expect} from 'chai'; // may prefer 'assert' in place of 'expect' -import { - spec -} from 'modules/inmarBidAdapter.js'; -import {config} from 'src/config.js'; - -describe('Inmar adapter tests', function () { - var DEFAULT_PARAMS_NEW_SIZES = [{ - adUnitCode: 'test-div', - bidId: '2c7c8e9c900244', - mediaTypes: { - banner: { - sizes: [ - [300, 250], [300, 600], [728, 90], [970, 250]] - } - }, - bidder: 'inmar', - params: { - partnerId: 12345 - }, - auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', - bidRequestsCount: 1, - bidderRequestId: '1858b7382993ca', - transactionId: '29df2112-348b-4961-8863-1b33684d95e6', - user: {} - }]; - - var DEFAULT_PARAMS_VIDEO = [{ - adUnitCode: 'test-div', - bidId: '2c7c8e9c900244', - mediaTypes: { - video: { - context: 'instream', // or 'outstream' - playerSize: [640, 480], - mimes: ['video/mp4'] - } - }, - bidder: 'inmar', - params: { - partnerId: 12345 - }, - auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', - bidRequestsCount: 1, - bidderRequestId: '1858b7382993ca', - transactionId: '29df2112-348b-4961-8863-1b33684d95e6', - user: {} - }]; - - var DEFAULT_PARAMS_WO_OPTIONAL = [{ - adUnitCode: 'test-div', - bidId: '2c7c8e9c900244', - sizes: [ - [300, 250], - [300, 600], - [728, 90], - [970, 250] - ], - bidder: 'inmar', - params: { - partnerId: 12345, - }, - auctionId: '851adee7-d843-48f9-a7e9-9ff00573fcbf', - bidRequestsCount: 1, - bidderRequestId: '1858b7382993ca', - transactionId: '29df2112-348b-4961-8863-1b33684d95e6' - }]; - - var BID_RESPONSE = { - body: { - cpm: 1.50, - ad: '', - meta: { - mediaType: 'banner', - }, - width: 300, - height: 250, - creativeId: '189198063', - netRevenue: true, - currency: 'USD', - ttl: 300, - dealId: 'dealId' - - } - }; - - var BID_RESPONSE_VIDEO = { - body: { - cpm: 1.50, - meta: { - mediaType: 'video', - }, - width: 1, - height: 1, - creativeId: '189198063', - netRevenue: true, - currency: 'USD', - ttl: 300, - vastUrl: 'https://vast.com/vast.xml', - dealId: 'dealId' - } - }; - - it('Verify build request to prebid 3.0 display test', function() { - const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { - gdprConsent: { - consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', - gdprApplies: true - }, - refererInfo: { - legacy: { - referer: 'https://domain.com', - numIframes: 0 - } - } - }); - - expect(request).to.have.property('method').and.to.equal('POST'); - const requestContent = JSON.parse(request.data); - expect(requestContent.bidRequests[0].params).to.have.property('partnerId').and.to.equal(12345); - expect(requestContent.bidRequests[0]).to.have.property('auctionId').and.to.equal('0cb3144c-d084-4686-b0d6-f5dbe917c563'); - expect(requestContent.bidRequests[0]).to.have.property('bidId').and.to.equal('2c7c8e9c900244'); - expect(requestContent.bidRequests[0]).to.have.property('bidRequestsCount').and.to.equal(1); - expect(requestContent.bidRequests[0]).to.have.property('bidder').and.to.equal('inmar'); - expect(requestContent.bidRequests[0]).to.have.property('bidderRequestId').and.to.equal('1858b7382993ca'); - expect(requestContent.bidRequests[0]).to.have.property('adUnitCode').and.to.equal('test-div'); - expect(requestContent.refererInfo).to.have.property('referer').and.to.equal('https://domain.com'); - expect(requestContent.bidRequests[0].mediaTypes.banner).to.have.property('sizes'); - expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[0]).to.have.ordered.members([300, 250]); - expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[1]).to.have.ordered.members([300, 600]); - expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[2]).to.have.ordered.members([728, 90]); - expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[3]).to.have.ordered.members([970, 250]); - expect(requestContent.bidRequests[0]).to.have.property('transactionId').and.to.equal('29df2112-348b-4961-8863-1b33684d95e6'); - expect(requestContent.refererInfo).to.have.property('numIframes').and.to.equal(0); - }) - - it('Verify interprete response', function () { - const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { - gdprConsent: { - consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', - gdprApplies: true - }, - refererInfo: { - referer: 'https://domain.com', - numIframes: 0 - } - }); - - const bids = spec.interpretResponse(BID_RESPONSE, request); - expect(bids).to.have.lengthOf(1); - const bid = bids[0]; - expect(bid.cpm).to.equal(1.50); - expect(bid.ad).to.equal(''); - expect(bid.meta.mediaType).to.equal('banner'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('189198063'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('USD'); - expect(bid.ttl).to.equal(300); - expect(bid.dealId).to.equal('dealId'); - }); - - it('no banner media response', function () { - const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { - gdprConsent: { - consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', - gdprApplies: true - }, - refererInfo: { - referer: 'https://domain.com', - numIframes: 0 - } - }); - - const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request); - const bid = bids[0]; - expect(bid.vastUrl).to.equal('https://vast.com/vast.xml'); - }); - - it('Verifies bidder_code', function () { - expect(spec.code).to.equal('inmar'); - }); - - it('Verifies bidder aliases', function () { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.equal('inm'); - }); - - it('Verifies if bid request is valid', function () { - expect(spec.isBidRequestValid(DEFAULT_PARAMS_NEW_SIZES[0])).to.equal(true); - expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid({ - params: {} - })).to.equal(false); - expect(spec.isBidRequestValid({ - params: { - } - })).to.equal(false); - expect(spec.isBidRequestValid({ - params: { - partnerId: 12345 - } - })).to.equal(true); - }); - - it('Verifies user syncs image', function () { - var syncs = spec.getUserSyncs({ - iframeEnabled: false, - pixelEnabled: true - }, [BID_RESPONSE], { - consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', - referer: 'http://domain.com', - gdprApplies: true - }) - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('image'); - - syncs = spec.getUserSyncs({ - iframeEnabled: false, - pixelEnabled: true - }, [BID_RESPONSE], { - consentString: '', - referer: 'http://domain.com', - gdprApplies: true - }) - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('image'); - - syncs = spec.getUserSyncs({ - iframeEnabled: false, - pixelEnabled: true - }, [], { - consentString: null, - referer: 'http://domain.com', - gdprApplies: true - }) - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('image'); - }); -}); diff --git a/test/spec/modules/inskinBidAdapter_spec.js b/test/spec/modules/inskinBidAdapter_spec.js deleted file mode 100644 index 3d0ec82fca5..00000000000 --- a/test/spec/modules/inskinBidAdapter_spec.js +++ /dev/null @@ -1,386 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/inskinBidAdapter.js'; -import { createBid } from 'src/bidfactory.js'; - -const ENDPOINT = 'https://mfad.inskinad.com/api/v2'; - -const REQUEST = { - 'bidderCode': 'inskin', - 'requestId': 'a4713c32-3762-4798-b342-4ab810ca770d', - 'bidderRequestId': '109f2a181342a9', - 'bidRequest': [{ - 'bidder': 'inskin', - 'params': { - 'networkId': 9874, - 'siteId': 730181, - 'publisherId': 123456 - }, - 'placementCode': 'div-gpt-ad-1487778092495-0', - 'sizes': [ - [728, 90], - [970, 90] - ], - 'bidId': '2b0f82502298c9', - 'bidderRequestId': '109f2a181342a9', - 'requestId': 'a4713c32-3762-4798-b342-4ab810ca770d' - }, - { - 'bidder': 'inskin', - 'params': { - 'networkId': 9874, - 'siteId': 730181 - }, - 'placementCode': 'div-gpt-ad-1487778092495-0', - 'sizes': [ - [728, 90], - [970, 90] - ], - 'bidId': '123', - 'bidderRequestId': '109f2a181342a9', - 'requestId': 'a4713c32-3762-4798-b342-4ab810ca770d' - }], - 'start': 1487883186070, - 'auctionStart': 1487883186069, - 'timeout': 3000 -}; - -const RESPONSE = { - 'headers': null, - 'body': { - 'user': { 'key': 'ue1-2d33e91b71e74929b4aeecc23f4376f1' }, - 'decisions': { - '2b0f82502298c9': { - 'adId': 2364764, - 'creativeId': 1950991, - 'flightId': 2788300, - 'campaignId': 542982, - 'clickUrl': 'https://mfad.inskinad.com/r', - 'impressionUrl': 'https://mfad.inskinad.com/i.gif', - 'contents': [{ - 'type': 'html', - 'body': '', - 'data': { - 'height': 90, - 'width': 728, - 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif', - 'fileName': 'b0ab77db8a7848c8b78931aed022a5ef.gif' - }, - 'template': 'image' - }], - 'height': 90, - 'width': 728, - 'events': [], - 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} - }, - '123': { - 'adId': 2364764, - 'creativeId': 1950991, - 'flightId': 2788300, - 'campaignId': 542982, - 'clickUrl': 'https://mfad.inskinad.com/r', - 'impressionUrl': 'https://mfad.inskinad.com/i.gif', - 'contents': [{ - 'type': 'html', - 'body': '', - 'data': { - 'customData': { - 'pubCPM': 1 - }, - 'height': 90, - 'width': 728, - 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif', - 'fileName': 'b0ab77db8a7848c8b78931aed022a5ef.gif' - }, - 'template': 'image' - }], - 'height': 90, - 'width': 728, - 'events': [], - 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} - } - } - } -}; - -const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; -const bidderRequest = { - bidderCode: 'inskin', - gdprConsent: { - consentString: consentString, - gdprApplies: true - }, - refererInfo: { - referer: 'https://www.inskinmedia.com' - } -}; - -describe('InSkin BidAdapter', function () { - let bidRequests; - let adapter = spec; - - beforeEach(function () { - bidRequests = [ - { - bidder: 'inskin', - params: { - networkId: '9874', - siteId: 'xxxxx' - }, - placementCode: 'header-bid-tag-1', - sizes: [[300, 250], [300, 600]], - bidId: '23acc48ad47af5', - requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', - bidderRequestId: '1c56ad30b9b8ca8', - transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' - } - ]; - }); - - describe('bid request validation', function () { - it('should accept valid bid requests', function () { - let bid = { - bidder: 'inskin', - params: { - networkId: '9874', - siteId: 'xxxxx' - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should accept valid bid requests with extra fields', function () { - let bid = { - bidder: 'inskin', - params: { - networkId: '9874', - siteId: 'xxxxx', - zoneId: 'xxxxx' - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should reject bid requests without siteId', function () { - let bid = { - bidder: 'inskin', - params: { - networkId: '9874' - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should reject bid requests without networkId', function () { - let bid = { - bidder: 'inskin', - params: { - siteId: '9874' - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests validation', function () { - it('creates request data', function () { - let request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request).to.exist.and.to.be.a('object'); - }); - - it('request to inskin should contain a url', function () { - let request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.url).to.have.string('inskinad.com'); - }); - - it('requires valid bids to make request', function () { - let request = spec.buildRequests([], bidderRequest); - expect(request.bidRequest).to.be.empty; - }); - - it('sends bid request to ENDPOINT via POST', function () { - let request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.method).to.have.string('POST'); - }); - - it('should add gdpr consent information to the request', function () { - bidderRequest.bids = bidRequests; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.consent.gdprConsentString).to.exist; - expect(payload.consent.gdprConsentRequired).to.exist; - expect(payload.consent.gdprConsentString).to.exist.and.to.equal(consentString); - expect(payload.consent.gdprConsentRequired).to.exist.and.to.be.true; - }); - - it('should not add keywords if TCF v2 purposes are granted', function () { - const bidderRequest2 = Object.assign({}, bidderRequest, { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - vendor: { - consents: { - 150: true - } - }, - purpose: { - consents: { - 1: true, - 2: true, - 3: true, - 4: true, - 5: true, - 6: true, - 7: true, - 8: true, - 9: true, - 10: true - } - } - }, - apiVersion: 2 - } - }); - - const request = spec.buildRequests(bidRequests, bidderRequest2); - const payload = JSON.parse(request.data); - - expect(payload.keywords).to.be.an('array').that.is.empty; - expect(payload.placements[0].properties.restrictions).to.be.undefined; - }); - - it('should add keywords if TCF v2 purposes are not granted', function () { - const bidderRequest2 = Object.assign({}, bidderRequest, { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - vendor: { - consents: { - 150: false - } - }, - purpose: { - consents: { - 1: true, - 2: true, - 3: true, - 4: true, - 5: true, - 6: true, - 7: true, - 8: true, - 9: true, - 10: true - } - } - }, - apiVersion: 2 - } - }); - - const request = spec.buildRequests(bidRequests, bidderRequest2); - const payload = JSON.parse(request.data); - - expect(payload.keywords).to.be.an('array').that.includes('cst-nocookies'); - expect(payload.keywords).to.be.an('array').that.includes('cst-nocontext'); - expect(payload.keywords).to.be.an('array').that.includes('cst-nodmp'); - expect(payload.keywords).to.be.an('array').that.includes('cst-nodata'); - expect(payload.keywords).to.be.an('array').that.includes('cst-noclicks'); - expect(payload.keywords).to.be.an('array').that.includes('cst-noresearch'); - - expect(payload.placements[0].properties.restrictions).to.be.an('array').that.includes('nocookies'); - expect(payload.placements[0].properties.restrictions).to.be.an('array').that.includes('nocontext'); - expect(payload.placements[0].properties.restrictions).to.be.an('array').that.includes('nodmp'); - expect(payload.placements[0].properties.restrictions).to.be.an('array').that.includes('nodata'); - expect(payload.placements[0].properties.restrictions).to.be.an('array').that.includes('noclicks'); - expect(payload.placements[0].properties.restrictions).to.be.an('array').that.includes('noresearch'); - }); - }); - describe('interpretResponse validation', function () { - it('response should have valid bidderCode', function () { - let bidRequest = spec.buildRequests(REQUEST.bidRequest, bidderRequest); - let bid = createBid(1, bidRequest.bidRequest[0]); - - expect(bid.bidderCode).to.equal('inskin'); - }); - - it('response should include objects for all bids', function () { - let bids = spec.interpretResponse(RESPONSE, REQUEST); - - expect(bids.length).to.equal(2); - }); - - it('registers bids', function () { - let bids = spec.interpretResponse(RESPONSE, REQUEST); - bids.forEach(b => { - expect(b).to.have.property('cpm'); - expect(b.cpm).to.be.above(0); - expect(b).to.have.property('requestId'); - expect(b).to.have.property('cpm'); - expect(b).to.have.property('width'); - expect(b).to.have.property('height'); - expect(b).to.have.property('ad'); - expect(b).to.have.property('currency', 'USD'); - expect(b).to.have.property('creativeId'); - expect(b).to.have.property('ttl', 360); - expect(b.meta).to.have.property('advertiserDomains'); - expect(b).to.have.property('netRevenue', true); - }); - }); - - it('cpm is correctly set', function () { - let bids = spec.interpretResponse(RESPONSE, REQUEST); - - expect(bids[0].cpm).to.equal(0.5); - expect(bids[1].cpm).to.equal(1); - }); - - it('handles nobid responses', function () { - let EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {'decisions': null}}) - let bids = spec.interpretResponse(EMPTY_RESP, REQUEST); - - expect(bids).to.be.empty; - }); - - it('handles no server response', function () { - let bids = spec.interpretResponse(null, REQUEST); - - expect(bids).to.be.empty; - }); - }); - describe('getUserSyncs', function () { - it('handles empty sync options', function () { - let opts = spec.getUserSyncs({}); - - expect(opts).to.be.empty; - }); - - it('should return two sync urls if pixel syncs are enabled', function () { - let syncOptions = {'pixelEnabled': true}; - let opts = spec.getUserSyncs(syncOptions); - - expect(opts.length).to.equal(2); - }); - - it('should return three sync urls if pixel and iframe syncs are enabled', function () { - let syncOptions = {'iframeEnabled': true, 'pixelEnabled': true}; - let opts = spec.getUserSyncs(syncOptions); - - expect(opts.length).to.equal(3); - }); - }); - describe('supply chain id', function () { - it('should use publisherId as sid', function () { - const request = spec.buildRequests(REQUEST.bidRequest, bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.rtb.schain.ext.sid).to.equal('123456'); - }); - }); -}); diff --git a/test/spec/modules/insticatorBidAdapter_spec.js b/test/spec/modules/insticatorBidAdapter_spec.js index fc7ed1833ac..de0358f2b46 100644 --- a/test/spec/modules/insticatorBidAdapter_spec.js +++ b/test/spec/modules/insticatorBidAdapter_spec.js @@ -72,7 +72,11 @@ describe('InsticatorBidAdapter', function () { let bidderRequest = { bidderRequestId, - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + ortb2: { + source: { + tid: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + } + }, timeout: 300, gdprConsent: { consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', @@ -248,7 +252,7 @@ describe('InsticatorBidAdapter', function () { expect(data.tmax).to.equal(bidderRequest.timeout); expect(data.source).to.have.all.keys('fd', 'tid', 'ext'); expect(data.source.fd).to.equal(1); - expect(data.source.tid).to.equal(bidderRequest.auctionId); + expect(data.source.tid).to.equal(bidderRequest.ortb2.source.tid); expect(data.source.ext).to.have.property('schain').to.deep.equal({ ver: '1.0', complete: 1, diff --git a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js index 3b3542d43b0..2c460156318 100644 --- a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js +++ b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js @@ -53,7 +53,7 @@ describe('Invisibly Analytics Adapter test suite', function () { hb_source: 'server', }, getStatusCode() { - return CONSTANTS.STATUS.NO_BID; + return CONSTANTS.STATUS.GOOD; }, }; diff --git a/test/spec/modules/ivsBidAdapter_spec.js b/test/spec/modules/ivsBidAdapter_spec.js index 79b3d6811f4..819c7480595 100644 --- a/test/spec/modules/ivsBidAdapter_spec.js +++ b/test/spec/modules/ivsBidAdapter_spec.js @@ -112,9 +112,7 @@ describe('ivsBidAdapter', function () { it('should contain the required parameters', function () { const bidRequest = spec.buildRequests(validBidRequests, validBidderRequest); const bidderRequest = bidRequest.data; - assert.equal(bidderRequest.id, validBidderRequest.auctionId); assert.ok(bidderRequest.site); - assert.ok(bidderRequest.source); assert.lengthOf(bidderRequest.imp, 1); }); }); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 874f5048ce0..783d273e425 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -389,11 +389,11 @@ describe('IndexexchangeAdapter', function () { ext: { tid: '173f49a8-7549-4218-a23c-e7ba59b47230', data: { - pbadslot: 'div-gpt-ad-1460505748562-0' + pbadslot: 'div-gpt-ad-1460505748562-1' } } }, - adUnitCode: 'div-gpt-ad-1460505748562-0', + adUnitCode: 'div-gpt-ad-1460505748562-1', transactionId: '273f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', @@ -729,6 +729,9 @@ describe('IndexexchangeAdapter', function () { ortb2: { site: { page: 'https://www.prebid.org' + }, + source: { + tid: 'mock-tid' } } }; @@ -1894,7 +1897,7 @@ describe('IndexexchangeAdapter', function () { expect(payload.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); expect(payload.imp).to.be.an('array'); expect(payload.imp).to.have.lengthOf(1); - expect(payload.source.tid).to.equal(DEFAULT_BANNER_VALID_BID[0].auctionId); + expect(payload.source.tid).to.equal(DEFAULT_OPTION.ortb2.source.tid); }); it('payload should have correct format and value for r.id when bidderRequestId is a number ', function () { @@ -2432,9 +2435,11 @@ describe('IndexexchangeAdapter', function () { bid1.bidId = '2f6g5s5e'; const bid2 = utils.deepClone(bid1); + bid2.adUnitCode = 'div-gpt-2' bid2.transactionId = 'tr2'; bid2.mediaTypes.banner.sizes = [[220, 221], [222, 223], [300, 250]]; const bid3 = utils.deepClone(bid1); + bid3.adUnitCode = 'div-gpt-3' bid3.transactionId = 'tr3'; bid3.mediaTypes.banner.sizes = [[330, 331], [332, 333], [300, 250]]; @@ -2570,6 +2575,36 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.placement).to.equal(2); }); + it('should use plcmt value when set in video.params', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.params.video.plcmt = 2; + const request = spec.buildRequests([bid], {})[0]; + const impression = extractPayload(request).imp[0]; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video.plcmt).to.equal(2); + }); + + it('invalid plcmt value when set in video.params', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.params.video.plcmt = 5; + const request = spec.buildRequests([bid], {})[0]; + const impression = extractPayload(request).imp[0]; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video.plcmt).to.be.undefined; + }); + + it('invalid plcmt value string when set in video.params', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.params.video.plcmt = '4'; + const request = spec.buildRequests([bid], {})[0]; + const impression = extractPayload(request).imp[0]; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video.plcmt).to.be.undefined; + }); + it('should set imp.ext.sid for video imps if params.id exists', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.params.id = 50; @@ -2596,7 +2631,8 @@ describe('IndexexchangeAdapter', function () { const impression = extractPayload(request).imp[0]; expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); - expect(impression.video.placement).to.equal(4); + expect(impression.video.placement).to.equal(3); + expect(extractPayload(request).ext.ixdiag.vpd).to.equal(true); }); it('should handle unexpected context', function () { diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index d692cc67e26..dae2640d224 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -76,7 +76,6 @@ describe('kargo adapter tests', function () { auctionId: '1234098', bidId: '1', adUnitCode: '101', - transactionId: '10101', sizes: [[320, 50], [300, 250], [300, 600]], mediaTypes: { banner: { @@ -135,6 +134,7 @@ describe('kargo adapter tests', function () { }, ortb2Imp: { ext: { + tid: '10101', data: { adServer: { name: 'gam', @@ -152,7 +152,6 @@ describe('kargo adapter tests', function () { }, bidId: '2', adUnitCode: '202', - transactionId: '20202', sizes: [[320, 50], [300, 250], [300, 600]], mediaTypes: { video: { @@ -164,6 +163,7 @@ describe('kargo adapter tests', function () { bidderWinsCount: 0, ortb2Imp: { ext: { + tid: '20202', data: { adServer: { name: 'gam', @@ -180,7 +180,6 @@ describe('kargo adapter tests', function () { }, bidId: '3', adUnitCode: '303', - transactionId: '30303', sizes: [[320, 50], [300, 250], [300, 600]], mediaTypes: { native: { @@ -189,6 +188,7 @@ describe('kargo adapter tests', function () { }, ortb2Imp: { ext: { + tid: '30303', data: { adServer: { name: 'gam', diff --git a/test/spec/modules/koblerBidAdapter_spec.js b/test/spec/modules/koblerBidAdapter_spec.js index 5fb0bef726e..2b5830f68d2 100644 --- a/test/spec/modules/koblerBidAdapter_spec.js +++ b/test/spec/modules/koblerBidAdapter_spec.js @@ -7,6 +7,7 @@ import {getRefererInfo} from 'src/refererDetection.js'; function createBidderRequest(auctionId, timeout, pageUrl) { return { + bidderRequestId: 'mock-uuid', auctionId: auctionId || 'c1243d83-0bed-4fdb-8c76-42b456be17d0', timeout: timeout || 2000, refererInfo: { @@ -37,6 +38,16 @@ function createValidBidRequest(params, bidId, sizes) { } describe('KoblerAdapter', function () { + let sandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore() + }); + describe('inherited functions', function () { it('exists and is a function', function () { const adapter = newBidder(spec); @@ -207,7 +218,7 @@ describe('KoblerAdapter', function () { const openRtbRequest = JSON.parse(result.data); expect(openRtbRequest.tmax).to.be.equal(timeout); - expect(openRtbRequest.id).to.be.equal(auctionId); + expect(openRtbRequest.id).to.exist; expect(openRtbRequest.site.page).to.be.equal(testUrl); }); @@ -435,7 +446,7 @@ describe('KoblerAdapter', function () { const openRtbRequest = JSON.parse(result.data); const expectedOpenRtbRequest = { - id: '9ff580cf-e10e-4b66-add7-40ac0c804e21', + id: 'mock-uuid', at: 1, tmax: 4500, cur: ['USD'], diff --git a/test/spec/modules/kulturemediaBidAdapter_spec.js b/test/spec/modules/kulturemediaBidAdapter_spec.js index 1872f6c171a..f21fe4a8810 100644 --- a/test/spec/modules/kulturemediaBidAdapter_spec.js +++ b/test/spec/modules/kulturemediaBidAdapter_spec.js @@ -530,7 +530,6 @@ describe('kulturemediaBidAdapter:', function () { let o = { requestId: serverResponse.seatbid[0].bid[0].impid, ad: '', - bidderCode: spec.code, cpm: serverResponse.seatbid[0].bid[0].price, creativeId: serverResponse.seatbid[0].bid[0].crid, vastXml: serverResponse.seatbid[0].bid[0].adm, diff --git a/test/spec/modules/limelightDigitalBidAdapter_spec.js b/test/spec/modules/limelightDigitalBidAdapter_spec.js index 5a92110abb4..0e6f4817e5e 100644 --- a/test/spec/modules/limelightDigitalBidAdapter_spec.js +++ b/test/spec/modules/limelightDigitalBidAdapter_spec.js @@ -24,7 +24,11 @@ describe('limelightDigitalAdapter', function () { sizes: [[300, 250]] } }, - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + ortb2Imp: { + ext: { + tid: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + } + }, userIdAsEids: [ { source: 'test1.org', @@ -64,7 +68,11 @@ describe('limelightDigitalAdapter', function () { placementCode: 'placement_1', auctionId: '482f88de-29ab-45c8-981a-d25e39454a34', sizes: [[350, 200]], - transactionId: '068867d1-46ec-40bb-9fa0-e24611786fb4', + ortb2Imp: { + ext: { + tid: '068867d1-46ec-40bb-9fa0-e24611786fb4', + } + }, userIdAsEids: [ { source: 'test2.org', @@ -110,7 +118,11 @@ describe('limelightDigitalAdapter', function () { placementCode: 'placement_2', auctionId: 'e4771143-6aa7-41ec-8824-ced4342c96c8', sizes: [[800, 600]], - transactionId: '738d5915-6651-43b9-9b6b-d50517350917', + ortb2Imp: { + ext: { + tid: '738d5915-6651-43b9-9b6b-d50517350917', + } + }, userIdAsEids: [ { source: 'test3.org', @@ -155,7 +167,11 @@ describe('limelightDigitalAdapter', function () { video: { playerSize: [800, 600] }, - transactionId: '738d5915-6651-43b9-9b6b-d50517350917', + ortb2Imp: { + ext: { + tid: '738d5915-6651-43b9-9b6b-d50517350917', + } + }, userIdAsEids: [ { source: 'test.org', @@ -647,7 +663,7 @@ function validateAdUnit(adUnit, bid) { expect(adUnit.id).to.equal(bid.params.adUnitId); expect(adUnit.bidId).to.equal(bid.bidId); expect(adUnit.type).to.equal(bid.params.adUnitType.toUpperCase()); - expect(adUnit.transactionId).to.equal(bid.transactionId); + expect(adUnit.transactionId).to.equal(bid.ortb2Imp.ext.tid); let bidSizes = []; if (bid.mediaTypes) { if (bid.mediaTypes.video && bid.mediaTypes.video.playerSize) { diff --git a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js index bab7a68287d..d07b48752c6 100644 --- a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +++ b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js @@ -71,7 +71,7 @@ const BID3 = { auctionId: '25c6d7f5-699a-4bfc-87c9-996f915341fa', mediaType: 'banner', getStatusCode() { - return CONSTANTS.STATUS.NO_BID; + return CONSTANTS.STATUS.GOOD; } }; diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index 6bbdf6e1705..52eaf8d7d76 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -36,6 +36,11 @@ describe('Livewrapped adapter tests', function () { bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + ortb2Imp: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + } + }, transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' } ], @@ -1211,7 +1216,6 @@ describe('Livewrapped adapter tests', function () { let expectedResponse = [{ requestId: '32e50fad901ae89', - bidderCode: 'livewrapped', cpm: 2.565917, width: 300, height: 250, @@ -1251,7 +1255,6 @@ describe('Livewrapped adapter tests', function () { let expectedResponse = [{ requestId: '32e50fad901ae89', - bidderCode: 'livewrapped', cpm: 2.565917, width: 300, height: 250, @@ -1293,7 +1296,6 @@ describe('Livewrapped adapter tests', function () { let expectedResponse = [{ requestId: '32e50fad901ae89', - bidderCode: 'livewrapped', cpm: 2.565917, width: 300, height: 250, @@ -1347,7 +1349,6 @@ describe('Livewrapped adapter tests', function () { let expectedResponse = [{ requestId: '32e50fad901ae89', - bidderCode: 'livewrapped', cpm: 2.565917, width: 300, height: 250, @@ -1359,7 +1360,6 @@ describe('Livewrapped adapter tests', function () { meta: undefined }, { requestId: '42e50fad901ae89', - bidderCode: 'livewrapped', cpm: 3.565917, width: 980, height: 240, @@ -1398,7 +1398,6 @@ describe('Livewrapped adapter tests', function () { let expectedResponse = [{ requestId: '32e50fad901ae89', - bidderCode: 'livewrapped', cpm: 2.565917, width: 300, height: 250, diff --git a/test/spec/modules/liveyieldAnalyticsAdapter_spec.js b/test/spec/modules/liveyieldAnalyticsAdapter_spec.js deleted file mode 100644 index 9ec4eaf9acd..00000000000 --- a/test/spec/modules/liveyieldAnalyticsAdapter_spec.js +++ /dev/null @@ -1,704 +0,0 @@ -import CONSTANTS from 'src/constants.json'; -import liveyield from 'modules/liveyieldAnalyticsAdapter.js'; -import { expect } from 'chai'; -const events = require('src/events'); - -const { - EVENTS: { BID_REQUESTED, BID_TIMEOUT, BID_RESPONSE, BID_WON } -} = CONSTANTS; - -describe('liveyield analytics adapter', function() { - const rtaCalls = []; - - window.rta = function() { - rtaCalls.push({ callArgs: arguments }); - }; - - beforeEach(function() { - sinon.stub(events, 'getEvents').returns([]); - }); - afterEach(function() { - events.getEvents.restore(); - }); - - describe('initialization', function() { - afterEach(function() { - rtaCalls.length = 0; - }); - it('it should require provider', function() { - liveyield.enableAnalytics({}); - expect(rtaCalls).to.be.empty; - }); - it('should require config.options', function() { - liveyield.enableAnalytics({ provider: 'liveyield' }); - expect(rtaCalls).to.be.empty; - }); - it('should require options.customerId', function() { - liveyield.enableAnalytics({ provider: 'liveyield', options: {} }); - expect(rtaCalls).to.be.empty; - }); - it('should require options.customerName', function() { - liveyield.enableAnalytics({ - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa' - } - }); - expect(rtaCalls).to.be.empty; - }); - it('should require options.customerSite', function() { - liveyield.enableAnalytics({ - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean' - } - }); - expect(rtaCalls).to.be.empty; - }); - it('should require options.sessionTimezoneOffset', function() { - liveyield.enableAnalytics({ - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com' - } - }); - expect(rtaCalls).to.be.empty; - }); - it("should throw error, when 'rta' function is not defined ", function() { - const keepMe = window.rta; - - delete window.rta; - - liveyield.enableAnalytics({ - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12 - } - }); - expect(rtaCalls).to.be.empty; - - window.rta = keepMe; - }); - it('should initialize when all required parameters are passed', function() { - liveyield.enableAnalytics({ - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12 - } - }); - expect(rtaCalls[0].callArgs['0']).to.match(/create/); - expect(rtaCalls[0].callArgs['1']).to.match( - /d6a6f8da-190f-47d6-ae11-f1a4469083fa/ - ); - expect(rtaCalls[0].callArgs['2']).to.match(/pubocean/); - expect(rtaCalls[0].callArgs['4']).to.match(/12/); - liveyield.disableAnalytics(); - }); - it('should allow to redefine rta function name', function() { - const keepMe = window.rta; - window.abc = keepMe; - delete window.rta; - liveyield.enableAnalytics({ - provider: 'liveyield', - options: { - rtaFunctionName: 'abc', - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'test', - customerSite: 'scribol.com', - sessionTimezoneOffset: 25 - } - }); - - liveyield.disableAnalytics(); - expect(rtaCalls[0].callArgs['0']).to.match(/create/); - expect(rtaCalls[0].callArgs['1']).to.match( - /d6a6f8da-190f-47d6-ae11-f1a4469083fa/ - ); - expect(rtaCalls[0].callArgs['2']).to.match(/test/); - expect(rtaCalls[0].callArgs['4']).to.match(/25/); - - window.rta = keepMe; - liveyield.disableAnalytics(); - }); - it('should handle custom parameters', function() { - liveyield.enableAnalytics({ - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'test2', - customerSite: 'scribol.com', - sessionTimezoneOffset: 38, - contentTitle: 'testTitle', - contentAuthor: 'testAuthor', - contentCategory: 'testCategory' - } - }); - - liveyield.disableAnalytics(); - expect(rtaCalls[0].callArgs['0']).to.match(/create/); - expect(rtaCalls[0].callArgs['2']).to.match(/test2/); - expect(rtaCalls[0].callArgs['4']).to.match(/38/); - expect(rtaCalls[0].callArgs['5'].contentTitle).to.match(/testTitle/); - expect(rtaCalls[0].callArgs['5'].contentAuthor).to.match(/testAuthor/); - expect(rtaCalls[0].callArgs['5'].contentCategory).to.match( - /testCategory/ - ); - liveyield.disableAnalytics(); - }); - }); - - describe('handling events', function() { - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12 - } - }; - beforeEach(function() { - rtaCalls.length = 0; - liveyield.enableAnalytics(options); - }); - afterEach(function() { - liveyield.disableAnalytics(); - }); - it('should handle BID_REQUESTED event', function() { - const bidRequest = { - bidderCode: 'appnexus', - bids: [ - { - params: { - placementId: '10433394' - }, - adUnitCode: 'div-gpt-ad-1438287399331-0', - transactionId: '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', - sizes: '300x250,300x600', - bidId: '2eddfdc0c791dc', - auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7' - } - ] - }; - - events.emit(BID_REQUESTED, bidRequest); - - expect(rtaCalls[1].callArgs['0']).to.equal('bidRequested'); - expect(rtaCalls[1].callArgs['1']).to.equal('div-gpt-ad-1438287399331-0'); - expect(rtaCalls[1].callArgs['2']).to.equal('appnexus'); - }); - it('should handle BID_REQUESTED event with invalid args', function() { - const bidRequest = { - bids: [ - { - params: { - placementId: '10433394' - }, - transactionId: '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', - sizes: '300x250,300x600', - bidId: '2eddfdc0c791dc', - auctionId: 'a5b849e5-87d7-4205-8300-d063084fcf' - }, - { - params: { - placementId: '31034023' - }, - transactionId: '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', - sizes: '300x250,300x600', - bidId: '3dkg0404fmd0', - auctionId: 'a5b849e5-87d7-4205-8300-d063084fcf' - } - ] - }; - events.emit(BID_REQUESTED, bidRequest); - expect(rtaCalls[1].callArgs['0']).to.equal('bidRequested'); - expect(rtaCalls[1].callArgs['1']).to.equal(undefined); - expect(rtaCalls[1].callArgs['2']).to.equal(undefined); - expect(rtaCalls[1].callArgs['0']).to.equal('bidRequested'); - }); - it('should handle BID_RESPONSE event', function() { - const bidResponse = { - height: 250, - statusMessage: 'Bid available', - adId: '2eddfdc0c791dc', - mediaType: 'banner', - source: 'client', - requestId: '2eddfdc0c791dc', - cpm: 0.5, - creativeId: 29681110, - currency: 'USD', - netRevenue: true, - ttl: 300, - auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7', - responseTimestamp: 1522265866110, - requestTimestamp: 1522265863600, - bidder: 'appnexus', - adUnitCode: 'div-gpt-ad-1438287399331-0', - timeToRespond: 2510, - size: '300x250' - }; - - events.emit(BID_RESPONSE, bidResponse); - expect(rtaCalls[1].callArgs['0']).to.equal('addBid'); - expect(rtaCalls[1].callArgs['1']).to.equal('div-gpt-ad-1438287399331-0'); - expect(rtaCalls[1].callArgs['2']).to.equal('appnexus'); - expect(rtaCalls[1].callArgs['3']).to.equal(500); - expect(rtaCalls[1].callArgs['4']).to.equal(false); - expect(rtaCalls[1].callArgs['5']).to.equal(false); - }); - it('should handle BID_RESPONSE event with undefined bidder and cpm', function() { - const bidResponse = { - height: 250, - statusMessage: 'Bid available', - adId: '2eddfdc0c791dc', - mediaType: 'banner', - source: 'client', - requestId: '2eddfdc0c791dc', - creativeId: 29681110, - currency: 'USD', - netRevenue: true, - ttl: 300, - auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7', - responseTimestamp: 1522265866110, - requestTimestamp: 1522265863600, - adUnitCode: 'div-gpt-ad-1438287399331-0', - timeToRespond: 2510, - size: '300x250' - }; - events.emit(BID_RESPONSE, bidResponse); - expect(rtaCalls[1].callArgs['0']).to.equal('addBid'); - expect(rtaCalls[1].callArgs['2']).to.equal('unknown'); - expect(rtaCalls[1].callArgs['3']).to.equal(0); - expect(rtaCalls[1].callArgs['4']).to.equal(true); - }); - it('should handle BID_RESPONSE event with undefined status message and adUnitCode', function() { - const bidResponse = { - height: 250, - adId: '2eddfdc0c791dc', - mediaType: 'banner', - source: 'client', - requestId: '2eddfdc0c791dc', - cpm: 0.5, - creativeId: 29681110, - currency: 'USD', - netRevenue: true, - ttl: 300, - auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7', - responseTimestamp: 1522265866110, - requestTimestamp: 1522265863600, - bidder: 'appnexus', - timeToRespond: 2510, - size: '300x250' - }; - events.emit(BID_RESPONSE, bidResponse); - expect(rtaCalls[1].callArgs['0']).to.equal('addBid'); - expect(rtaCalls[1].callArgs['1']).to.equal(undefined); - expect(rtaCalls[1].callArgs['3']).to.equal(0); - expect(rtaCalls[1].callArgs['5']).to.equal(true); - }); - it('should handle BID_TIMEOUT', function() { - const bidTimeout = [ - { - bidId: '2baa51527bd015', - bidder: 'bidderOne', - adUnitCode: '/19968336/header-bid-tag-0', - auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' - }, - { - bidId: '6fe3b4c2c23092', - bidder: 'bidderTwo', - adUnitCode: '/19968336/header-bid-tag-0', - auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' - } - ]; - events.emit(BID_TIMEOUT, bidTimeout); - expect(rtaCalls[1].callArgs['0']).to.equal('biddersTimeout'); - expect(rtaCalls[1].callArgs['1'].length).to.equal(2); - }); - it('should handle BID_WON event', function() { - const bidWon = { - adId: '4587fec4900b81', - mediaType: 'banner', - requestId: '4587fec4900b81', - cpm: 1.962, - creativeId: 2126, - currency: 'EUR', - netRevenue: true, - ttl: 302, - auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', - statusMessage: 'Bid available', - responseTimestamp: 1530628534437, - requestTimestamp: 1530628534219, - bidderCode: 'testbidder4', - adUnitCode: 'div-gpt-ad-1438287399331-0', - timeToRespond: 218, - size: '300x250', - status: 'rendered' - }; - events.emit(BID_WON, bidWon); - expect(rtaCalls[1].callArgs['0']).to.equal('resolveSlot'); - expect(rtaCalls[1].callArgs['1']).to.equal('div-gpt-ad-1438287399331-0'); - expect(rtaCalls[1].callArgs['2'].prebidWon).to.equal(true); - expect(rtaCalls[1].callArgs['2'].prebidPartner).to.equal('testbidder4'); - expect(rtaCalls[1].callArgs['2'].prebidValue).to.equal(1962); - }); - it('should throw error, invoking BID_WON event without adUnitCode', function() { - const bidWon = { - adId: '4587fec4900b81', - mediaType: 'banner', - requestId: '4587fec4900b81', - cpm: 1.962, - creativeId: 2126, - currency: 'EUR', - netRevenue: true, - ttl: 302, - auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', - statusMessage: 'Bid available', - responseTimestamp: 1530628534437, - requestTimestamp: 1530628534219, - timeToRespond: 218, - bidderCode: 'testbidder4', - size: '300x250', - status: 'rendered' - }; - events.emit(BID_WON, bidWon); - expect(rtaCalls[1]).to.be.undefined; - }); - it('should throw error, invoking BID_WON event without bidderCode', function() { - const bidWon = { - adId: '4587fec4900b81', - mediaType: 'banner', - requestId: '4587fec4900b81', - cpm: 1.962, - creativeId: 2126, - currency: 'EUR', - netRevenue: true, - ttl: 302, - auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', - statusMessage: 'Bid available', - responseTimestamp: 1530628534437, - requestTimestamp: 1530628534219, - adUnitCode: 'div-gpt-ad-1438287399331-0', - timeToRespond: 218, - size: '300x250', - status: 'rendered' - }; - events.emit(BID_WON, bidWon); - expect(rtaCalls[1]).to.be.undefined; - }); - }); - - describe('googletag use case', function() { - beforeEach(function() { - rtaCalls.length = 0; - }); - afterEach(function() { - liveyield.disableAnalytics(); - }); - it('should ignore BID_WON events when gpt is provided', function() { - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12, - googlePublisherTag: true, - wireGooglePublisherTag: function() { - return null; - } - } - }; - liveyield.enableAnalytics(options); - - const bidWon = { - adId: 'ignore_me', - mediaType: 'banner', - requestId: '4587fec4900b81', - cpm: 1.962, - creativeId: 2126, - currency: 'EUR', - netRevenue: true, - ttl: 302, - auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', - statusMessage: 'Bid available', - responseTimestamp: 1530628534437, - requestTimestamp: 1530628534219, - bidderCode: 'hello', - adUnitCode: 'div-gpt-ad-1438287399331-0', - timeToRespond: 218, - size: '300x250', - status: 'rendered' - }; - events.emit(BID_WON, bidWon); - - expect(rtaCalls.length).to.equal(1); - }); - it('should subscribe to slotRenderEnded', function() { - var googletag; - var callback; - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12, - googlePublisherTag: 'testGPT', - wireGooglePublisherTag: function(gpt, cb) { - googletag = gpt; - callback = cb; - } - } - }; - liveyield.enableAnalytics(options); - expect(googletag).to.equal('testGPT'); - expect(typeof callback).to.equal('function'); - }); - it('should handle BID_WON event for prebid', function() { - var call; - const slot = { - getResponseInformation: function() { - const dfpInfo = { - dfpAdvertiserId: 1, - dfpLineItemId: 2, - dfpCreativeId: 3 - }; - return dfpInfo; - }, - getTargeting: function(v) { - return ['4587fec4900b81']; - } - }; - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12, - googlePublisherTag: 'testGPT', - wireGooglePublisherTag: function(gpt, cb) { - call = cb; - }, - getHighestPrebidAdImpressionPartner: function(slot, version) { - return 'testbidder4'; - }, - getHighestPrebidAdImpressionValue: function(slot, version) { - return 12; - }, - postProcessResolution: function( - resolution, - slot, - hbPartner, - hbValue, - version - ) { - return resolution; - }, - getAdUnitNameByGooglePublisherTagSlot: function(slot, version) { - return 'testUnit'; - } - } - }; - liveyield.enableAnalytics(options); - const bidWon = { - adId: '4587fec4900b81' - }; - events.emit(BID_WON, bidWon); - call(slot); - expect(rtaCalls[1].callArgs['2'].prebidWon).to.equal(true); - expect(rtaCalls[1].callArgs['2'].prebidPartner).to.equal('testbidder4'); - }); - it('should handle BID_WON event for dfp', function() { - let call; - const slot = { - getResponseInformation: function() { - const dfpInfo = { - dfpAdvertiserId: 1, - dfpLineItemId: 2, - dfpCreativeId: 3 - }; - return dfpInfo; - } - }; - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12, - googlePublisherTag: 'testGPT', - wireGooglePublisherTag: function(gpt, cb) { - call = cb; - }, - getHighestPrebidAdImpressionPartner: function(slot, version) { - return 'testbidder4'; - }, - getHighestPrebidAdImpressionValue: function(slot, version) { - return 12; - }, - postProcessResolution: function(slot, version) { - return { partner: slot.prebidPartner, value: slot.prebidValue }; - }, - getAdUnitNameByGooglePublisherTagSlot: function(slot, version) { - return 'testUnit'; - }, - isPrebidAdImpression: function(slot) { - return true; - } - } - }; - liveyield.enableAnalytics(options); - call(slot); - expect(rtaCalls.length).to.equal(2); - expect(rtaCalls[1].callArgs[0]).to.equal('resolveSlot'); - expect(rtaCalls[1].callArgs[1]).to.equal('testUnit'); - expect(rtaCalls[1].callArgs[2].partner).to.equal('testbidder4'); - expect(rtaCalls[1].callArgs[2].value).to.equal(12000); - }); - it('should work with defaults: prebid won', () => { - let call; - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12, - googlePublisherTag: 'testGPT', - wireGooglePublisherTag: (gpt, cb) => (call = cb), - getAdUnitName: adUnitCode => - adUnitCode === 'PREBID_UNIT' ? 'ADUNIT' : null, - getAdUnitNameByGooglePublisherTagSlot: slot => - slot.getSlotElementId() === 'div-gpt-ad-1438287399331-0' - ? 'ADUNIT' - : null - } - }; - liveyield.enableAnalytics(options); - - const bidResponse = { - adId: 'defaults_with_cache', - statusMessage: 'Bid available', - cpm: 0.5, - bidder: 'appnexus', - adUnitCode: 'PREBID_UNIT' - }; - events.emit(BID_RESPONSE, bidResponse); - - const bidWon = { - adId: bidResponse.adId, - cpm: 1.962, // adjusted, this one shall be used, not the one from bidResponse - bidderCode: 'appnexus_from_bid_won_event', - adUnitCode: 'PREBID_UNIT' - }; - events.emit(BID_WON, bidWon); - - const slot = { - getTargeting: key => (key === 'hb_adid' ? [bidResponse.adId] : []), - getResponseInformation: () => null, - getSlotElementId: () => 'div-gpt-ad-1438287399331-0' - }; - call(slot); - - expect(rtaCalls[2].callArgs[0]).to.equal('resolveSlot'); - expect(rtaCalls[2].callArgs[1]).to.equal('ADUNIT'); - expect(rtaCalls[2].callArgs[2]).to.deep.equal({ - targetings: [], - prebidWon: true, - prebidPartner: 'appnexus_from_bid_won_event', - prebidValue: 1962 - }); - }); - it('should work with defaults: dfp won, prebid bid response', () => { - let call; - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12, - googlePublisherTag: 'testGPT', - wireGooglePublisherTag: (gpt, cb) => (call = cb), - getAdUnitName: adUnitCode => - adUnitCode === 'PREBID_UNIT' ? 'ADUNIT' : null, - getAdUnitNameByGooglePublisherTagSlot: slot => - slot.getSlotElementId() === 'div-gpt-ad-1438287399331-0' - ? 'ADUNIT' - : null - } - }; - liveyield.enableAnalytics(options); - - const bidResponse = { - adId: 'defaults_with_cache_no_prebid_win', - statusMessage: 'Bid available', - cpm: 0.5, - bidder: 'appnexus', - adUnitCode: 'PREBID_UNIT' - }; - events.emit(BID_RESPONSE, bidResponse); - - const slot = { - getTargeting: key => (key === 'hb_adid' ? [bidResponse.adId] : []), - getResponseInformation: () => null, - getSlotElementId: () => 'div-gpt-ad-1438287399331-0' - }; - call(slot); - - expect(rtaCalls[2].callArgs[0]).to.equal('resolveSlot'); - expect(rtaCalls[2].callArgs[1]).to.equal('ADUNIT'); - expect(rtaCalls[2].callArgs[2]).to.deep.equal({ - targetings: [], - prebidPartner: 'appnexus', - prebidValue: 500 - }); - }); - it('should work with defaults: dfp won, without prebid', () => { - let call; - const options = { - provider: 'liveyield', - options: { - customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', - customerName: 'pubocean', - customerSite: 'scribol.com', - sessionTimezoneOffset: 12, - googlePublisherTag: 'testGPT', - wireGooglePublisherTag: (gpt, cb) => (call = cb), - getAdUnitName: adUnitCode => - adUnitCode === 'PREBID_UNIT' ? 'ADUNIT' : null, - getAdUnitNameByGooglePublisherTagSlot: slot => - slot.getSlotElementId() === 'div-gpt-ad-1438287399331-0' - ? 'ADUNIT' - : null - } - }; - liveyield.enableAnalytics(options); - - const slot = { - getTargeting: key => (key === 'hb_adid' ? ['does-not-exist'] : []), - getResponseInformation: () => null, - getSlotElementId: () => 'div-gpt-ad-1438287399331-0' - }; - call(slot); - - expect(rtaCalls[1].callArgs[0]).to.equal('resolveSlot'); - expect(rtaCalls[1].callArgs[1]).to.equal('ADUNIT'); - expect(rtaCalls[1].callArgs[2]).to.deep.equal({ - targetings: [] - }); - }); - }); -}); diff --git a/test/spec/modules/logicadBidAdapter_spec.js b/test/spec/modules/logicadBidAdapter_spec.js index 6abca6b2e98..3c1383781b9 100644 --- a/test/spec/modules/logicadBidAdapter_spec.js +++ b/test/spec/modules/logicadBidAdapter_spec.js @@ -35,7 +35,52 @@ describe('LogicadAdapter', function () { third: 'fakesharedid' } }] - }] + }], + ortb2: { + device: { + sua: { + source: 2, + platform: { + brand: 'Windows', + version: ['10', '0', '0'] + }, + browsers: [ + { + brand: 'Chromium', + version: ['112', '0', '5615', '20'] + }, + { + brand: 'Google Chrome', + version: ['112', '0', '5615', '20'] + }, + { + brand: 'Not:A-Brand', + version: ['99', '0', '0', '0'] + } + ], + mobile: 0, + model: '', + bitness: '64', + architecture: 'x86' + } + }, + user: { + data: [ + { + ext: { + segtax: 600, + segclass: '2206021246' + }, + segment: [ + { + id: '1' + } + ], + name: 'cd.ladsp.com' + } + ] + } + } }]; const nativeBidRequests = [{ bidder: 'logicad', @@ -77,7 +122,52 @@ describe('LogicadAdapter', function () { third: 'fakesharedid' } }] - }] + }], + ortb2: { + device: { + sua: { + source: 2, + platform: { + brand: 'Windows', + version: ['10', '0', '0'] + }, + browsers: [ + { + brand: 'Chromium', + version: ['112', '0', '5615', '20'] + }, + { + brand: 'Google Chrome', + version: ['112', '0', '5615', '20'] + }, + { + brand: 'Not:A-Brand', + version: ['99', '0', '0', '0'] + } + ], + mobile: 0, + model: '', + bitness: '64', + architecture: 'x86' + } + }, + user: { + data: [ + { + ext: { + segtax: 600, + segclass: '2206021246' + }, + segment: [ + { + id: '1' + } + ], + name: 'cd.ladsp.com' + } + ] + } + } }]; const bidderRequest = { refererInfo: { @@ -184,6 +274,36 @@ describe('LogicadAdapter', function () { expect(data.auctionId).to.equal('18fd8b8b0bd757'); expect(data.eids[0].source).to.equal('sharedid.org'); expect(data.eids[0].uids[0].id).to.equal('fakesharedid'); + + expect(data.sua.source).to.equal(2); + expect(data.sua.platform.brand).to.equal('Windows'); + expect(data.sua.platform.version[0]).to.equal('10'); + expect(data.sua.platform.version[1]).to.equal('0'); + expect(data.sua.platform.version[2]).to.equal('0'); + expect(data.sua.browsers[0].brand).to.equal('Chromium'); + expect(data.sua.browsers[0].version[0]).to.equal('112'); + expect(data.sua.browsers[0].version[1]).to.equal('0'); + expect(data.sua.browsers[0].version[2]).to.equal('5615'); + expect(data.sua.browsers[0].version[3]).to.equal('20'); + expect(data.sua.browsers[1].brand).to.equal('Google Chrome'); + expect(data.sua.browsers[1].version[0]).to.equal('112'); + expect(data.sua.browsers[1].version[1]).to.equal('0'); + expect(data.sua.browsers[1].version[2]).to.equal('5615'); + expect(data.sua.browsers[1].version[3]).to.equal('20'); + expect(data.sua.browsers[2].brand).to.equal('Not:A-Brand'); + expect(data.sua.browsers[2].version[0]).to.equal('99'); + expect(data.sua.browsers[2].version[1]).to.equal('0'); + expect(data.sua.browsers[2].version[2]).to.equal('0'); + expect(data.sua.browsers[2].version[3]).to.equal('0'); + expect(data.sua.mobile).to.equal(0); + expect(data.sua.model).to.equal(''); + expect(data.sua.bitness).to.equal('64'); + expect(data.sua.architecture).to.equal('x86'); + + expect(data.userData[0].name).to.equal('cd.ladsp.com'); + expect(data.userData[0].segment[0].id).to.equal('1'); + expect(data.userData[0].ext.segtax).to.equal(600); + expect(data.userData[0].ext.segclass).to.equal('2206021246'); }); }); diff --git a/test/spec/modules/loglyliftBidAdapter_spec.js b/test/spec/modules/loglyliftBidAdapter_spec.js index 8ff2eb97463..9805561442a 100644 --- a/test/spec/modules/loglyliftBidAdapter_spec.js +++ b/test/spec/modules/loglyliftBidAdapter_spec.js @@ -11,6 +11,11 @@ describe('loglyliftBidAdapter', function () { }, adUnitCode: '/19968336/prebid_native_example_1', transactionId: '10aee457-617c-4572-ab5b-99df1d73ccb4', + ortb2Imp: { + ext: { + tid: '10aee457-617c-4572-ab5b-99df1d73ccb4', + } + }, sizes: [[300, 250], [300, 600]], bidderRequestId: '15da3afd9632d7', auctionId: 'f890b7d9-e787-4237-ac21-6d8554abac9f', @@ -29,6 +34,11 @@ describe('loglyliftBidAdapter', function () { }, adUnitCode: '/19968336/prebid_native_example_1', transactionId: '10aee457-617c-4572-ab5b-99df1d73ccb4', + ortb2Imp: { + ext: { + tid: '10aee457-617c-4572-ab5b-99df1d73ccb4', + } + }, sizes: [ [] ], diff --git a/test/spec/modules/luponmediaBidAdapter_spec.js b/test/spec/modules/luponmediaBidAdapter_spec.js index c8d4c18c407..1441abc0fe8 100755 --- a/test/spec/modules/luponmediaBidAdapter_spec.js +++ b/test/spec/modules/luponmediaBidAdapter_spec.js @@ -142,7 +142,12 @@ describe('luponmediaBidAdapter', function () { 'https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines' ] }, - 'start': 1587413920835 + 'start': 1587413920835, + ortb2: { + source: { + tid: 'mock-tid' + } + }, }; it('sends bid request to ENDPOINT via POST', function () { @@ -150,7 +155,23 @@ describe('luponmediaBidAdapter', function () { let dynRes = JSON.parse(requests.data); expect(requests.url).to.equal(ENDPOINT_URL); expect(requests.method).to.equal('POST'); - expect(requests.data).to.equal('{"id":"585d96a5-bd93-4a89-b8ea-0f546f3aaa82","test":0,"source":{"tid":"585d96a5-bd93-4a89-b8ea-0f546f3aaa82","ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"novi.ba","sid":"199424","hp":1}]}}},"tmax":1500,"imp":[{"id":"268a30af10dd6f","secure":1,"ext":{"luponmedia":{"siteId":303522,"keyId":"4o2c4"}},"banner":{"format":[{"w":300,"h":250}]}}],"ext":{"prebid":{"targeting":{"includewinners":true,"includebidderkeys":false}}},"user":{"id":"' + dynRes.user.id + '","buyeruid":"8d8b16cb-1383-4a0f-b4bb-0be28464d974"},"site":{"page":"https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines"}}'); + expect(JSON.parse(requests.data)).to.deep.include({ + 'test': 0, + 'source': { + tid: 'mock-tid', + 'ext': {'schain': {'ver': '1.0', 'complete': 1, 'nodes': [{'asi': 'novi.ba', 'sid': '199424', 'hp': 1}]}} + }, + 'tmax': 1500, + 'imp': [{ + 'id': '268a30af10dd6f', + 'secure': 1, + 'ext': {'luponmedia': {'siteId': 303522, 'keyId': '4o2c4'}}, + 'banner': {'format': [{'w': 300, 'h': 250}]} + }], + 'ext': {'prebid': {'targeting': {'includewinners': true, 'includebidderkeys': false}}}, + 'user': {'id': dynRes.user.id, 'buyeruid': '8d8b16cb-1383-4a0f-b4bb-0be28464d974'}, + 'site': {'page': 'https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines'} + }); }); }); diff --git a/test/spec/modules/magniteAnalyticsAdapter_spec.js b/test/spec/modules/magniteAnalyticsAdapter_spec.js index 9183a883f63..ae63f19f46b 100644 --- a/test/spec/modules/magniteAnalyticsAdapter_spec.js +++ b/test/spec/modules/magniteAnalyticsAdapter_spec.js @@ -25,7 +25,8 @@ const { BID_WON, BID_TIMEOUT, BILLABLE_EVENT, - SEAT_NON_BID + SEAT_NON_BID, + BID_REJECTED } } = CONSTANTS; @@ -2147,4 +2148,95 @@ describe('magnite analytics adapter', function () { }); }); }); + + describe('BID_REJECTED events', () => { + let bidRejectedArgs; + + const runBidRejectedAuction = () => { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_REJECTED, bidRejectedArgs) + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + clock.tick(rubiConf.analyticsBatchTimeout + 1000); + }; + beforeEach(() => { + magniteAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001 + } + }); + bidRejectedArgs = utils.deepClone(MOCK.BID_RESPONSE); + }); + + it('updates the bid to be rejected by floors', () => { + bidRejectedArgs.floorData = { + floorValue: 0.5, + floorRule: 'banner', + floorRuleValue: 0.5, + floorCurrency: 'USD', + cpmAfterAdjustments: 0.15, + enforcements: { + enforceJS: true, + enforcePBS: false, + floorDeals: false, + bidAdjustment: true + }, + matchedFields: { + mediaType: 'banner' + } + } + bidRejectedArgs.rejectionReason = 'Bid does not meet price floor'; + + runBidRejectedAuction(); + let message = JSON.parse(server.requests[0].requestBody); + + expect(message.auctions[0].adUnits[0].bids[0]).to.deep.equal({ + bidder: 'rubicon', + bidId: '23fcd8cf4bf0d7', + source: 'client', + status: 'rejected-ipf', + clientLatencyMillis: 271, + httpLatencyMillis: 240, + bidResponse: { + bidPriceUSD: 0.15, + mediaType: 'banner', + dimensions: { + width: 300, + height: 250 + }, + floorValue: 0.5, + floorRuleValue: 0.5, + rejectionReason: 'Bid does not meet price floor' + } + }); + }); + + it('does general rejection', () => { + bidRejectedArgs + bidRejectedArgs.rejectionReason = 'this bid is rejected'; + + runBidRejectedAuction(); + let message = JSON.parse(server.requests[0].requestBody); + + expect(message.auctions[0].adUnits[0].bids[0]).to.deep.equal({ + bidder: 'rubicon', + bidId: '23fcd8cf4bf0d7', + source: 'client', + status: 'rejected', + clientLatencyMillis: 271, + httpLatencyMillis: 240, + bidResponse: { + bidPriceUSD: 3.4, + mediaType: 'banner', + dimensions: { + width: 300, + height: 250 + }, + rejectionReason: 'this bid is rejected' + } + }); + }); + }); }); diff --git a/test/spec/modules/mass_spec.js b/test/spec/modules/mass_spec.js deleted file mode 100644 index e7fc66991ca..00000000000 --- a/test/spec/modules/mass_spec.js +++ /dev/null @@ -1,141 +0,0 @@ -import { expect } from 'chai'; -import { - init, - addBidResponseHook, - addListenerOnce, - isMassBid, - useDefaultMatch, - useDefaultRender, - updateRenderers, - listenerAdded, - isEnabled -} from 'modules/mass'; -import { logInfo } from 'src/utils.js'; - -// mock a MASS bid: -const mockedMassBids = [ - { - bidder: 'ix', - bidId: 'mass-bid-1', - requestId: 'mass-bid-1', - bidderRequestId: 'bidder-request-id-1', - dealId: 'MASS1234', - ad: 'mass://provider/product/etc...', - meta: {} - }, - { - bidder: 'ix', - bidId: 'mass-bid-2', - requestId: 'mass-bid-2', - bidderRequestId: 'bidder-request-id-1', - dealId: '1234', - ad: 'mass://provider/product/etc...', - meta: { - mass: true - } - }, -]; - -// mock non-MASS bids: -const mockedNonMassBids = [ - { - bidder: 'ix', - bidId: 'non-mass-bid-1', - requstId: 'non-mass-bid-1', - bidderRequestId: 'bidder-request-id-1', - dealId: 'MASS1234', - ad: '', - meta: { - mass: true - } - }, - { - bidder: 'ix', - bidId: 'non-mass-bid-2', - requestId: 'non-mass-bid-2', - bidderRequestId: 'bidder-request-id-1', - dealId: '1234', - ad: 'mass://provider/product/etc...', - meta: {} - }, -]; - -const noop = function() {}; - -describe('MASS Module', function() { - it('should be enabled by default', function() { - expect(isEnabled).to.equal(true); - }); - - it('can be disabled', function() { - init({enabled: false}); - expect(isEnabled).to.equal(false); - }); - - it('should only affect MASS bids', function() { - init({renderUrl: 'https://...'}); - mockedNonMassBids.forEach(function(mockedBid) { - const originalBid = Object.assign({}, mockedBid); - const bid = Object.assign({}, originalBid); - - addBidResponseHook(noop, 'ad-code-id', bid); - - expect(bid).to.deep.equal(originalBid); - }); - }); - - it('should only update the ad markup field', function() { - init({renderUrl: 'https://...'}); - mockedMassBids.forEach(function(mockedBid) { - const originalBid = Object.assign({}, mockedBid); - const bid = Object.assign({}, originalBid); - - addBidResponseHook(noop, 'ad-code-id', bid); - - expect(bid.ad).to.not.equal(originalBid.ad); - - delete bid.ad; - delete originalBid.ad; - - expect(bid).to.deep.equal(originalBid); - }); - }); - - it('should add a message listener', function() { - addListenerOnce(); - expect(listenerAdded).to.equal(true); - }); - - it('should support custom renderers', function() { - init({ - renderUrl: 'https://...', - custom: [ - { - dealIdPattern: /abc/, - render: function() {} - } - ] - }); - - const renderers = updateRenderers(); - - expect(renderers.length).to.equal(2); - }); - - it('should match bids by deal ID with the default matcher', function() { - const match = useDefaultMatch(/abc/); - - expect(match({dealId: 'abc'})).to.equal(true); - expect(match({dealId: 'xyz'})).to.equal(false); - }); - - it('should have a default renderer', function() { - // SOT needs at least one script to be on the page; ugly solution, but good enough since mass is to be removed in v8 - document.body.appendChild(document.createElement('script')); - const render = useDefaultRender('https://example.com/render.js', 'abc'); - render({}); - - expect(window.abc.loaded).to.equal(true); - expect(window.abc.queue.length).to.equal(1); - }); -}); diff --git a/test/spec/modules/mediaforceBidAdapter_spec.js b/test/spec/modules/mediaforceBidAdapter_spec.js index 22f584306a9..61e5678b03b 100644 --- a/test/spec/modules/mediaforceBidAdapter_spec.js +++ b/test/spec/modules/mediaforceBidAdapter_spec.js @@ -97,7 +97,11 @@ describe('mediaforce bid adapter', function () { } } }, - transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + ortb2Imp: { + ext: { + tid: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + } + } }; const multiBid = [ @@ -127,7 +131,11 @@ describe('mediaforce bid adapter', function () { sizes: [[300, 250], [600, 400]] } }, - transactionId: transactionId || 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + ortb2Imp: { + ext: { + tid: transactionId || 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + } + }, } }); @@ -196,7 +204,7 @@ describe('mediaforce bid adapter', function () { bidfloor: bid.params.bidfloor, ext: { mediaforce: { - transactionId: bid.transactionId + transactionId: bid.ortb2Imp.ext.tid, } }, banner: {w: 300, h: 250}, diff --git a/test/spec/modules/mediafuseBidAdapter_spec.js b/test/spec/modules/mediafuseBidAdapter_spec.js index bb9bbd34d1d..dd2b5df70bd 100644 --- a/test/spec/modules/mediafuseBidAdapter_spec.js +++ b/test/spec/modules/mediafuseBidAdapter_spec.js @@ -811,8 +811,8 @@ describe('MediaFuseAdapter', function () { sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' }); - expect(payload.device.geo).to.exist; - expect(payload.device.geo).to.deep.equal({ + expect(payload.device.geo).to.not.exist; + expect(payload.device.geo).to.not.deep.equal({ lat: 40.0964439, lng: -75.3009142 }); @@ -1021,16 +1021,13 @@ describe('MediaFuseAdapter', function () { }) describe('interpretResponse', function () { - let bfStub; let bidderSettingsStorage; before(function() { - bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); bidderSettingsStorage = $$PREBID_GLOBAL$$.bidderSettings; }); after(function() { - bfStub.restore(); $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsStorage; }); @@ -1275,7 +1272,6 @@ describe('MediaFuseAdapter', function () { } }] }; - bfStub.returns('1'); let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(result[0]).to.have.property('vastUrl'); diff --git a/test/spec/modules/mediakeysBidAdapter_spec.js b/test/spec/modules/mediakeysBidAdapter_spec.js index 75c7f42cf58..99eaff3f378 100644 --- a/test/spec/modules/mediakeysBidAdapter_spec.js +++ b/test/spec/modules/mediakeysBidAdapter_spec.js @@ -131,7 +131,11 @@ describe('mediakeysBidAdapter', function () { const bidderRequest = { bidderCode: 'mediakeys', - auctionId: '84212956-c377-40e8-b000-9885a06dc692', + ortb2: { + source: { + tid: '84212956-c377-40e8-b000-9885a06dc692', + } + }, bidderRequestId: '1c1b642f803242', bids: [ bid @@ -208,7 +212,7 @@ describe('mediakeysBidAdapter', function () { // openRTB 2.5 expect(data.at).to.equal(1); expect(data.cur[0]).to.equal('USD'); // default currency - expect(data.source.tid).to.equal(bidderRequest.auctionId); + expect(data.source.tid).to.equal(bidderRequest.ortb2.source.tid); expect(data.imp.length).to.equal(1); expect(data.imp[0].id).to.equal(bidRequests[0].bidId); diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index 9180bbad68c..bb90eded230 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -16,7 +16,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + ortb2Imp: { + ext: { + tid: '277b631f-92f5-4844-8b19-ea13c095d3f1' + } + }, 'mediaTypes': { 'banner': { 'sizes': [[300, 250]], @@ -38,7 +42,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: { + ext: { + tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + } + }, 'mediaTypes': { 'banner': { 'sizes': [[300, 251]], @@ -64,6 +72,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', + ortb2Imp: { + ext: { + tid: '277b631f-92f5-4844-8b19-ea13c095d3f1', + } + }, 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', 'mediaTypes': { 'banner': { @@ -87,7 +100,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: { + ext: { + tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + } + }, 'mediaTypes': { 'banner': { 'sizes': [[300, 251]], @@ -112,7 +129,6 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', 'mediaTypes': { 'banner': { 'sizes': [[300, 250]], @@ -121,7 +137,12 @@ let VALID_BID_REQUEST = [{ 'bidId': '28f8f8130a583e', 'bidderRequestId': '1e9b1f07797c1c', 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', - 'ortb2Imp': { 'ext': { 'data': { 'pbadslot': '/12345/my-gpt-tag-0' } } }, + 'ortb2Imp': { + 'ext': { + tid: '277b631f-92f5-4844-8b19-ea13c095d3f1', + 'data': {'pbadslot': '/12345/my-gpt-tag-0'} + } + }, 'bidRequestsCount': 1 }, { 'bidder': 'medianet', @@ -136,7 +157,6 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', 'mediaTypes': { 'banner': { 'sizes': [[300, 251]], @@ -146,7 +166,12 @@ let VALID_BID_REQUEST = [{ 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', - 'ortb2Imp': { 'ext': { 'data': { 'pbadslot': '/12345/my-gpt-tag-0' } } }, + 'ortb2Imp': { + 'ext': { + tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + 'data': {'pbadslot': '/12345/my-gpt-tag-0'} + } + }, 'bidRequestsCount': 1 }], VALID_BID_REQUEST_WITH_USERID = [{ @@ -165,7 +190,11 @@ let VALID_BID_REQUEST = [{ britepoolid: '82efd5e1-816b-4f87-97f8-044f407e2911' }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + ortb2Imp: { + ext: { + tid: '277b631f-92f5-4844-8b19-ea13c095d3f1', + } + }, 'mediaTypes': { 'banner': { 'sizes': [[300, 250]], @@ -188,7 +217,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: { + ext: { + tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + } + }, 'mediaTypes': { 'banner': { 'sizes': [[300, 251]], @@ -214,7 +247,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + ortb2Imp: { + ext: { + tid: '277b631f-92f5-4844-8b19-ea13c095d3f1', + } + }, 'sizes': [[300, 250]], 'mediaTypes': { 'banner': { @@ -237,7 +274,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: { + ext: { + tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + } + }, 'sizes': [[300, 251]], 'mediaTypes': { 'banner': { @@ -261,7 +302,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + ortb2Imp: { + ext: { + tid: '277b631f-92f5-4844-8b19-ea13c095d3f1', + } + }, 'sizes': [[300, 250]], 'mediaTypes': { 'banner': { @@ -314,7 +359,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: { + ext: { + tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + } + }, 'sizes': [[300, 251]], 'mediaTypes': { 'banner': { @@ -389,6 +438,7 @@ let VALID_BID_REQUEST = [{ 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', + ortb2Imp: VALID_BID_REQUEST_INVALID_BIDFLOOR[0].ortb2Imp, 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-0', @@ -422,6 +472,7 @@ let VALID_BID_REQUEST = [{ } }, { 'id': '3f97ca71b1e5c2', + ortb2Imp: VALID_BID_REQUEST_INVALID_BIDFLOOR[1].ortb2Imp, 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-123', @@ -477,6 +528,7 @@ let VALID_BID_REQUEST = [{ 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', + ortb2Imp: VALID_NATIVE_BID_REQUEST[0].ortb2Imp, 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-0', @@ -510,6 +562,7 @@ let VALID_BID_REQUEST = [{ } }, { 'id': '3f97ca71b1e5c2', + ortb2Imp: VALID_NATIVE_BID_REQUEST[1].ortb2Imp, 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-123', @@ -567,6 +620,7 @@ let VALID_BID_REQUEST = [{ 'imp': [{ 'id': '28f8f8130a583e', 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + ortb2Imp: VALID_BID_REQUEST[0].ortb2Imp, 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-0', 'visibility': 1, @@ -599,6 +653,7 @@ let VALID_BID_REQUEST = [{ }, { 'id': '3f97ca71b1e5c2', 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: VALID_BID_REQUEST[1].ortb2Imp, 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-123', 'visibility': 1, @@ -656,6 +711,7 @@ let VALID_BID_REQUEST = [{ 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', + ortb2Imp: VALID_BID_REQUEST_WITH_USERID[0].ortb2Imp, 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', 'tagid': 'crid', 'ext': { @@ -690,6 +746,7 @@ let VALID_BID_REQUEST = [{ } }, { 'id': '3f97ca71b1e5c2', + ortb2Imp: VALID_BID_REQUEST_WITH_USERID[1].ortb2Imp, 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', 'tagid': 'crid', 'ext': { @@ -747,6 +804,7 @@ let VALID_BID_REQUEST = [{ 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', + ortb2Imp: VALID_BID_REQUEST_WITH_CRID[0].ortb2Imp, 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', 'tagid': 'crid', 'ext': { @@ -781,6 +839,7 @@ let VALID_BID_REQUEST = [{ } }, { 'id': '3f97ca71b1e5c2', + ortb2Imp: VALID_BID_REQUEST_WITH_CRID[1].ortb2Imp, 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', 'tagid': 'crid', 'ext': { @@ -1103,7 +1162,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + ortb2Imp: { + ext: { + tid: '277b631f-92f5-4844-8b19-ea13c095d3f1', + } + }, 'sizes': [300, 250], 'mediaTypes': { 'banner': { @@ -1126,7 +1189,11 @@ let VALID_BID_REQUEST = [{ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: { + ext: { + tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + } + }, 'sizes': [300, 251], 'mediaTypes': { 'banner': { @@ -1178,6 +1245,7 @@ let VALID_BID_REQUEST = [{ 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', + ortb2Imp: VALID_BID_REQUEST[0].ortb2Imp, 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-0', @@ -1210,6 +1278,7 @@ let VALID_BID_REQUEST = [{ } }, { 'id': '3f97ca71b1e5c2', + ortb2Imp: VALID_BID_REQUEST[1].ortb2Imp, 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', 'ext': { 'dfp_id': 'div-gpt-ad-1460505748561-123', @@ -1306,7 +1375,7 @@ describe('Media.net bid adapter', function () { it('should build valid payload on bid', function () { let requestObj = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); - expect(JSON.parse(requestObj.data)).to.deep.equal(VALID_PAYLOAD); + expect(JSON.parse(requestObj.data)).to.deep.include(VALID_PAYLOAD); }); it('should accept size as a one dimensional array', function () { @@ -1353,7 +1422,7 @@ describe('Media.net bid adapter', function () { bidreq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); actual = JSON.parse(bidreq.data).imp[0].ortb2Imp; - assert.equal(actual, undefined) + expect(actual).to.deep.equal(VALID_BID_REQUEST[0].ortb2Imp); }); it('should have userid in bid request', function () { diff --git a/test/spec/modules/mediasniperBidAdapter_spec.js b/test/spec/modules/mediasniperBidAdapter_spec.js index 21ce5297c8f..30437205067 100644 --- a/test/spec/modules/mediasniperBidAdapter_spec.js +++ b/test/spec/modules/mediasniperBidAdapter_spec.js @@ -209,7 +209,7 @@ describe('mediasniperBidAdapter', function () { // openRTB 2.5 expect(data.cur[0]).to.equal(DEFAULT_CURRENCY); - expect(data.id).to.equal(bidderRequest.auctionId); + expect(data.id).to.exist; expect(data.imp.length).to.equal(1); expect(data.imp[0].id).to.equal(bidRequests[0].bidId); diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 346d02d91d0..4e131024d84 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -1,16 +1,16 @@ import {expect} from 'chai'; import {spec} from 'modules/mediasquareBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {config} from 'src/config.js'; -import * as utils from 'src/utils.js'; -import { requestBidsHook } from 'modules/consentManagement.js'; describe('MediaSquare bid adapter tests', function () { var DEFAULT_PARAMS = [{ adUnitCode: 'banner-div', bidId: 'aaaa1234', auctionId: 'bbbb1234', - transactionId: 'cccc1234', + ortb2Imp: { + ext: { + tid: 'cccc1234', + } + }, mediaTypes: { banner: { sizes: [ @@ -98,6 +98,7 @@ describe('MediaSquare bid adapter tests', function () { 'code': 'test/publishername_atf_desktop_rg_pave', 'bid_id': 'aaaa1234', 'adomain': ['test.com'], + 'context': 'instream', }], }}; @@ -161,7 +162,10 @@ describe('MediaSquare bid adapter tests', function () { expect(bid.ttl).to.equal(300); expect(bid.requestId).to.equal('aaaa1234'); expect(bid.mediasquare).to.exist; + expect(bid.mediasquare.bidder).to.exist; expect(bid.mediasquare.bidder).to.equal('msqClassic'); + expect(bid.mediasquare.context).to.exist; + expect(bid.mediasquare.context).to.equal('instream'); expect(bid.mediasquare.code).to.equal([DEFAULT_PARAMS[0].params.owner, DEFAULT_PARAMS[0].params.code].join('/')); expect(bid.meta).to.exist; expect(bid.meta.advertiserDomains).to.exist; diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js index 98edfce20bc..bd6d04a6312 100644 --- a/test/spec/modules/microadBidAdapter_spec.js +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -12,7 +12,11 @@ describe('microadBidAdapter', () => { spot: 'spot-code' }, bidId: 'bid-id', - transactionId: 'transaction-id' + ortb2Imp: { + ext: { + tid: 'transaction-id' + } + } }; describe('isBidRequestValid', () => { @@ -224,7 +228,7 @@ describe('microadBidAdapter', () => { }); }); - it('should add geo parameter to response if request parameters contain geo', () => { + it('should not add geo parameter to response even if request parameters contain geo', () => { const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { params: { spot: 'spot-code', @@ -233,7 +237,7 @@ describe('microadBidAdapter', () => { }); const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); requests.forEach(request => { - expect(request.data).to.deep.equal( + expect(request.data).to.not.deep.equal( Object.assign({}, expectedResultTemplate, { cbt: request.data.cbt, geo: '35.655275,139.693771' diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index d2b9bc68fa6..0d12e422cd7 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -59,7 +59,8 @@ describe('minutemediaAdapter', function () { 'mediaTypes': { 'video': { 'playerSize': [[640, 480]], - 'context': 'instream' + 'context': 'instream', + 'plcmt': 1 } }, 'vastXml': '"..."' @@ -116,6 +117,11 @@ describe('minutemediaAdapter', function () { expect(request.method).to.equal('POST'); }); + it('sends the plcmt to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].plcmt).to.equal(1); + }); + it('sends bid request to TEST ENDPOINT via POST', function () { const request = spec.buildRequests(testModeBidRequests, bidderRequest); expect(request.url).to.equal(TEST_ENDPOINT); @@ -294,6 +300,68 @@ describe('minutemediaAdapter', function () { expect(request.data.bids[0]).to.have.property('floorPrice', 1.5); }); + it('should check sua param in bid request', function() { + const sua = { + 'platform': { + 'brand': 'macOS', + 'version': ['12', '4', '0'] + }, + 'browsers': [ + { + 'brand': 'Chromium', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Google Chrome', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Not;A=Brand', + 'version': [ '99', '0', '0', '0' ] + } + ], + 'mobile': 0, + 'model': '', + 'bitness': '64', + 'architecture': 'x86' + } + const bid = utils.deepClone(bidRequests[0]); + bid.ortb2 = { + 'device': { + 'sua': { + 'platform': { + 'brand': 'macOS', + 'version': [ '12', '4', '0' ] + }, + 'browsers': [ + { + 'brand': 'Chromium', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Google Chrome', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Not;A=Brand', + 'version': [ '99', '0', '0', '0' ] + } + ], + 'mobile': 0, + 'model': '', + 'bitness': '64', + 'architecture': 'x86' + } + } + } + const requestWithSua = spec.buildRequests([bid], bidderRequest); + const data = requestWithSua.data; + expect(data.bids[0].sua).to.exist; + expect(data.bids[0].sua).to.deep.equal(sua); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].sua).to.not.exist; + }); + describe('COPPA Param', function() { it('should set coppa equal 0 in bid request if coppa is set to false', function() { const request = spec.buildRequests(bidRequests, bidderRequest); diff --git a/test/spec/modules/minutemediaplusBidAdapter_spec.js b/test/spec/modules/minutemediaplusBidAdapter_spec.js index 359a97465e4..33ec194c61f 100644 --- a/test/spec/modules/minutemediaplusBidAdapter_spec.js +++ b/test/spec/modules/minutemediaplusBidAdapter_spec.js @@ -35,7 +35,6 @@ const BID = { } }, 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', 'sizes': [[300, 250], [300, 600]], 'bidderRequestId': '1fdb5ff1b6eaa7', 'auctionId': 'auction_id', @@ -47,7 +46,8 @@ const BID = { 'mediaTypes': [BANNER], 'ortb2Imp': { 'ext': { - 'gpid': '1234567890' + 'gpid': '1234567890', + tid: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', } } }; @@ -60,7 +60,11 @@ const VIDEO_BID = { 'bidRequestsCount': 4, 'bidderRequestsCount': 3, 'bidderWinsCount': 1, - 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', + ortb2Imp: { + ext: { + tid: '56e184c6-bde9-497b-b9b9-cf47a61381ee', + } + }, 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', 'params': { 'subDomain': SUB_DOMAIN, diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 7f40e7d89f8..564788c8b56 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -173,9 +173,9 @@ describe('nextMillenniumBidAdapterTests', function() { }); it('validate_generated_params', function() { - const request = spec.buildRequests(bidRequestData); + const request = spec.buildRequests(bidRequestData, {bidderRequestId: 'mock-uuid'}); expect(request[0].bidId).to.equal('bid1234'); - expect(JSON.parse(request[0].data).id).to.equal('b06c5141-fe8f-4cdf-9d7d-54415490a917'); + expect(JSON.parse(request[0].data).id).to.exist; }); it('use parameters group_id', function() { diff --git a/test/spec/modules/nexx360BidAdapter_spec.js b/test/spec/modules/nexx360BidAdapter_spec.js index 9ba3ac0e5a4..7c2cea99a46 100644 --- a/test/spec/modules/nexx360BidAdapter_spec.js +++ b/test/spec/modules/nexx360BidAdapter_spec.js @@ -354,7 +354,6 @@ describe('Nexx360 bid adapter tests', function () { const request = spec.buildRequests(displayBids, bidderRequest); const requestContent = request.data; expect(request).to.have.property('method').and.to.equal('POST'); - expect(requestContent.id).to.be.eql('2e684815-b44e-4e04-b812-56da54adbe74'); expect(requestContent.cur[0]).to.be.eql('USD'); expect(requestContent.imp.length).to.be.eql(2); expect(requestContent.imp[0].id).to.be.eql('44a2706ac3574'); diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index 2d48dca5ebb..d25363de9c9 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -62,6 +62,7 @@ describe('OguryBidAdapter', function () { ]; bidderRequest = { + bidderRequestId: 'mock-uuid', auctionId: bidRequests[0].auctionId, gdprConsent: {consentString: 'myConsentString', vendorData: {}, gdprApplies: true}, }; @@ -262,7 +263,7 @@ describe('OguryBidAdapter', function () { const defaultTimeout = 1000; const expectedRequestObject = { - id: bidRequests[0].auctionId, + id: 'mock-uuid', at: 1, tmax: defaultTimeout, imp: [{ diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 5bd65cf0fd5..701fee5f6d9 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -15,7 +15,11 @@ describe('onetag', function () { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', - 'transactionId': 'qwerty123', + ortb2Imp: { + ext: { + tid: 'qwerty123' + } + }, 'schain': { 'validation': 'off', 'config': { diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js deleted file mode 100644 index 1b1adeb8807..00000000000 --- a/test/spec/modules/openxAnalyticsAdapter_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from 'chai'; -import openxAdapter from 'modules/openxAnalyticsAdapter.js'; -import * as utils from 'src/utils.js'; - -describe('openx analytics adapter', function() { - describe('deprecation message', function () { - let spy; - beforeEach(function () { - spy = sinon.spy(utils, 'logWarn'); - }); - - afterEach(function() { - utils.logWarn.restore(); - }); - - it('should warn on enable', function() { - openxAdapter.enableAnalytics(); - expect(spy.firstCall.args[0]).to.match(/OpenX Analytics has been deprecated/); - }); - }); -}); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index d8ea79ac698..f2cff7f470c 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec, REQUEST_URL, SYNC_URL, DEFAULT_PH} from 'modules/openxOrtbBidAdapter.js'; +import {spec, REQUEST_URL, SYNC_URL, DEFAULT_PH} from 'modules/openxBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from 'src/mediaTypes.js'; import {config} from 'src/config.js'; diff --git a/test/spec/modules/openxOrtbBidAdapter_spec.js b/test/spec/modules/openxOrtbBidAdapter_spec.js deleted file mode 100644 index d8ea79ac698..00000000000 --- a/test/spec/modules/openxOrtbBidAdapter_spec.js +++ /dev/null @@ -1,1615 +0,0 @@ -import {expect} from 'chai'; -import {spec, REQUEST_URL, SYNC_URL, DEFAULT_PH} from 'modules/openxOrtbBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from 'src/mediaTypes.js'; -import {config} from 'src/config.js'; -import * as utils from 'src/utils.js'; -// load modules that register ORTB processors -import 'src/prebid.js' -import 'modules/currency.js'; -import 'modules/userId/index.js'; -import 'modules/multibid/index.js'; -import 'modules/priceFloors.js'; -import 'modules/consentManagement.js'; -import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; -import {deepClone} from 'src/utils.js'; -import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; -import {hook} from '../../../src/hook.js'; - -const DEFAULT_SYNC = SYNC_URL + '?ph=' + DEFAULT_PH; - -const BidRequestBuilder = function BidRequestBuilder(options) { - const defaults = { - request: { - auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', - adUnitCode: 'adunit-code', - bidder: 'openx' - }, - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - sizes: [[300, 250], [300, 600]], - }; - - const request = { - ...defaults.request, - ...options - }; - - this.withParams = (options) => { - request.params = { - ...defaults.params, - ...options - }; - return this; - }; - - this.build = () => request; -}; - -const BidderRequestBuilder = function BidderRequestBuilder(options) { - const defaults = { - bidderCode: 'openx', - auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', - bidderRequestId: '7g36s867Tr4xF90X', - timeout: 3000, - refererInfo: { - numIframes: 0, - reachedTop: true, - referer: 'http://test.io/index.html?pbjs_debug=true' - } - }; - - const request = { - ...defaults, - ...options - }; - - this.build = () => request; -}; - -describe('OpenxRtbAdapter', function () { - before(() => { - hook.ready(); - }); - - const adapter = newBidder(spec); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid()', function () { - describe('when request is for a banner ad', function () { - let bannerBid; - beforeEach(function () { - bannerBid = { - bidder: 'openx', - params: {}, - adUnitCode: 'adunit-code', - mediaTypes: {banner: {}}, - sizes: [[300, 250], [300, 600]], - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475' - }; - }); - - it('should return false when there is no delivery domain', function () { - bannerBid.params = {'unit': '12345678'}; - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - describe('when there is a delivery domain', function () { - beforeEach(function () { - bannerBid.params = {delDomain: 'test-delivery-domain'} - }); - - it('should return false when there is no ad unit id and size', function () { - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - it('should return true if there is an adunit id ', function () { - bannerBid.params.unit = '12345678'; - expect(spec.isBidRequestValid(bannerBid)).to.equal(true); - }); - - it('should return true if there is no adunit id and sizes are defined', function () { - bannerBid.mediaTypes.banner.sizes = [720, 90]; - expect(spec.isBidRequestValid(bannerBid)).to.equal(true); - }); - - it('should return false if no sizes are defined ', function () { - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - it('should return false if sizes empty ', function () { - bannerBid.mediaTypes.banner.sizes = []; - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - }); - }); - - describe('when request is for a multiformat ad', function () { - describe('and request config uses mediaTypes video and banner', () => { - const multiformatBid = { - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250]] - }, - video: { - playerSize: [300, 250] - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return true multisize when required params found', function () { - expect(spec.isBidRequestValid(multiformatBid)).to.equal(true); - }); - }); - }); - - describe('when request is for a video ad', function () { - describe('and request config uses mediaTypes', () => { - const videoBidWithMediaTypes = { - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let videoBidWithMediaTypes = Object.assign({}, videoBidWithMediaTypes); - videoBidWithMediaTypes.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); - }); - }); - describe('and request config uses both delDomain and platform', () => { - const videoBidWithDelDomainAndPlatform = { - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain', - platform: '1cabba9e-cafe-3665-beef-f00f00f00f00' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(videoBidWithDelDomainAndPlatform)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let videoBidWithMediaTypes = Object.assign({}, videoBidWithDelDomainAndPlatform); - videoBidWithMediaTypes.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); - }); - }); - describe('and request config uses mediaType', () => { - const videoBidWithMediaType = { - 'bidder': 'openx', - 'params': { - 'unit': '12345678', - 'delDomain': 'test-del-domain' - }, - 'adUnitCode': 'adunit-code', - 'mediaType': 'video', - 'sizes': [640, 480], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let videoBidWithMediaType = Object.assign({}, videoBidWithMediaType); - delete videoBidWithMediaType.params; - videoBidWithMediaType.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(false); - }); - }); - }); - }); - - describe('buildRequests()', function () { - let bidRequestsWithMediaTypes; - let bidRequestsWithPlatform; - let mockBidderRequest; - - beforeEach(function () { - mockBidderRequest = {refererInfo: {}}; - - bidRequestsWithMediaTypes = [{ - bidder: 'openx', - params: { - unit: '11', - delDomain: 'test-del-domain', - platform: '1cabba9e-cafe-3665-beef-f00f00f00f00', - }, - adUnitCode: '/adunit-code/test-path', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - ortb2Imp: { - ext: { - ae: 2 - } - } - }, { - bidder: 'openx', - params: { - unit: '22', - delDomain: 'test-del-domain', - platform: '1cabba9e-cafe-3665-beef-f00f00f00f00', - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - bidId: 'test-bid-id-2', - bidderRequestId: 'test-bid-request-2', - auctionId: 'test-auction-2', - transactionId: 'test-transactionId-2' - }]; - }); - - context('common requests checks', function() { - it('should be able to handle multiformat requests', () => { - const multiformat = utils.deepClone(bidRequestsWithMediaTypes[0]); - multiformat.mediaTypes.video = { - context: 'outstream', - playerSize: [640, 480] - } - const requests = spec.buildRequests([multiformat], mockBidderRequest); - const outgoingFormats = requests.flatMap(rq => rq.data.imp.flatMap(imp => ['banner', 'video'].filter(k => imp[k] != null))); - const expected = FEATURES.VIDEO ? ['banner', 'video'] : ['banner'] - expect(outgoingFormats).to.have.members(expected); - }) - - it('should send bid request to openx url via POST', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].url).to.equal(REQUEST_URL); - expect(request[0].method).to.equal('POST'); - }); - - it('should send delivery domain, if available', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.ext.delDomain).to.equal(bidRequestsWithMediaTypes[0].params.delDomain); - expect(request[0].data.ext.platformId).to.be.undefined; - }); - - it('should send platform id, if available', function () { - bidRequestsWithMediaTypes[0].params.platform = '1cabba9e-cafe-3665-beef-f00f00f00f00'; - bidRequestsWithMediaTypes[1].params.platform = '1cabba9e-cafe-3665-beef-f00f00f00f00'; - - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.ext.platform).to.equal(bidRequestsWithMediaTypes[0].params.platform); - expect(request[1].data.ext.platform).to.equal(bidRequestsWithMediaTypes[0].params.platform); - }); - - it('should send openx adunit codes', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.imp[0].tagid).to.equal(bidRequestsWithMediaTypes[0].params.unit); - expect(request[1].data.imp[0].tagid).to.equal(bidRequestsWithMediaTypes[1].params.unit); - }); - - it('should send out custom params on bids that have customParams specified', function () { - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - params: { - unit: '12345678', - delDomain: 'test-del-domain', - customParams: {'Test1': 'testval1+', 'test2': ['testval2/', 'testval3']} - } - } - ); - - mockBidderRequest.bids = [bidRequest]; - const request = spec.buildRequests([bidRequest], mockBidderRequest); - expect(request[0].data.imp[0].ext.customParams).to.equal(bidRequest.params.customParams); - }) - - describe('floors', function () { - it('should send out custom floors on bids that have customFloors, no currency as account currency is used', function () { - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - params: { - unit: '12345678', - delDomain: 'test-del-domain', - customFloor: 1.500 - } - } - ); - - const request = spec.buildRequests([bidRequest], mockBidderRequest); - expect(request[0].data.imp[0].bidfloor).to.equal(bidRequest.params.customFloor); - expect(request[0].data.imp[0].bidfloorcur).to.equal(undefined); - }); - - context('with floors module', function () { - let adServerCurrencyStub; - - beforeEach(function () { - adServerCurrencyStub = sinon - .stub(config, 'getConfig') - .withArgs('currency.adServerCurrency') - }); - - afterEach(function () { - config.getConfig.restore(); - }); - - it('should send out floors on bids in USD', function () { - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - getFloor: () => { - return { - currency: 'USD', - floor: 9.99 - } - } - } - ); - - const request = spec.buildRequests([bidRequest], mockBidderRequest); - expect(request[0].data.imp[0].bidfloor).to.equal(9.99); - expect(request[0].data.imp[0].bidfloorcur).to.equal('USD'); - }); - - it('should send not send floors', function () { - adServerCurrencyStub.returns('EUR'); - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - getFloor: () => { - return { - currency: 'BTC', - floor: 9.99 - } - } - } - ); - - const request = spec.buildRequests([bidRequest], mockBidderRequest); - expect(request[0].data.imp[0].bidfloor).to.equal(undefined) - expect(request[0].data.imp[0].bidfloorcur).to.equal(undefined) - }); - }) - }) - - describe('FPD', function() { - let bidRequests; - const mockBidderRequest = {refererInfo: {}}; - - beforeEach(function () { - bidRequests = [{ - bidder: 'openx', - params: { - unit: '12345678-banner', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-1' - }, { - bidder: 'openx', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - params: { - unit: '12345678-video', - delDomain: 'test-del-domain' - }, - 'adUnitCode': 'adunit-code', - - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-2' - }]; - }); - - it('ortb2.site should be merged in the request', function() { - const request = spec.buildRequests(bidRequests, { - ...mockBidderRequest, - 'ortb2': { - site: { - domain: 'page.example.com', - cat: ['IAB2'], - sectioncat: ['IAB2-2'] - } - } - }); - let data = request[0].data; - expect(data.site.domain).to.equal('page.example.com'); - expect(data.site.cat).to.deep.equal(['IAB2']); - expect(data.site.sectioncat).to.deep.equal(['IAB2-2']); - }); - - it('ortb2.user should be merged in the request', function() { - const request = spec.buildRequests(bidRequests, { - ...mockBidderRequest, - 'ortb2': { - user: { - yob: 1985 - } - } - }); - let data = request[0].data; - expect(data.user.yob).to.equal(1985); - }); - - describe('ortb2Imp', function() { - describe('ortb2Imp.ext.data.pbadslot', function() { - beforeEach(function () { - if (bidRequests[0].hasOwnProperty('ortb2Imp')) { - delete bidRequests[0].ortb2Imp; - } - }); - - it('should not send if imp[].ext.data object is invalid', function() { - bidRequests[0].ortb2Imp = { - ext: {} - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext).to.not.have.property('data'); - }); - - it('should not send if imp[].ext.data.pbadslot is undefined', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - if (data.imp[0].ext.data) { - expect(data.imp[0].ext.data).to.not.have.property('pbadslot'); - } else { - expect(data.imp[0].ext).to.not.have.property('data'); - } - }); - - it('should send if imp[].ext.data.pbadslot is string', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - pbadslot: 'abcd' - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext.data).to.have.property('pbadslot'); - expect(data.imp[0].ext.data.pbadslot).to.equal('abcd'); - }); - }); - - describe('ortb2Imp.ext.data.adserver', function() { - beforeEach(function () { - if (bidRequests[0].hasOwnProperty('ortb2Imp')) { - delete bidRequests[0].ortb2Imp; - } - }); - - it('should not send if imp[].ext.data object is invalid', function() { - bidRequests[0].ortb2Imp = { - ext: {} - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext).to.not.have.property('data'); - }); - - it('should not send if imp[].ext.data.adserver is undefined', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - if (data.imp[0].ext.data) { - expect(data.imp[0].ext.data).to.not.have.property('adserver'); - } else { - expect(data.imp[0].ext).to.not.have.property('data'); - } - }); - - it('should send', function() { - let adSlotValue = 'abc'; - bidRequests[0].ortb2Imp = { - ext: { - data: { - adserver: { - name: 'GAM', - adslot: adSlotValue - } - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext.data.adserver.name).to.equal('GAM'); - expect(data.imp[0].ext.data.adserver.adslot).to.equal(adSlotValue); - }); - }); - - describe('ortb2Imp.ext.data.other', function() { - beforeEach(function () { - if (bidRequests[0].hasOwnProperty('ortb2Imp')) { - delete bidRequests[0].ortb2Imp; - } - }); - - it('should not send if imp[].ext.data object is invalid', function() { - bidRequests[0].ortb2Imp = { - ext: {} - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext).to.not.have.property('data'); - }); - - it('should not send if imp[].ext.data.other is undefined', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - if (data.imp[0].ext.data) { - expect(data.imp[0].ext.data).to.not.have.property('other'); - } else { - expect(data.imp[0].ext).to.not.have.property('data'); - } - }); - - it('ortb2Imp.ext.data.other', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - other: 1234 - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext.data.other).to.equal(1234); - }); - }); - }); - - describe('with user agent client hints', function () { - it('should add device.sua if available', function () { - const bidderRequestWithUserAgentClientHints = { refererInfo: {}, - ortb2: { - device: { - sua: { - source: 2, - platform: { - brand: 'macOS', - version: [ '12', '4', '0' ] - }, - browsers: [ - { - brand: 'Chromium', - version: [ '106', '0', '5249', '119' ] - }, - { - brand: 'Google Chrome', - version: [ '106', '0', '5249', '119' ] - }, - { - brand: 'Not;A=Brand', - version: [ '99', '0', '0', '0' ] - }], - mobile: 0, - model: 'Pro', - bitness: '64', - architecture: 'x86' - } - } - }}; - - let request = spec.buildRequests(bidRequests, bidderRequestWithUserAgentClientHints); - expect(request[0].data.device.sua).to.exist; - expect(request[0].data.device.sua).to.deep.equal(bidderRequestWithUserAgentClientHints.ortb2.device.sua); - const bidderRequestWithoutUserAgentClientHints = {refererInfo: {}, ortb2: {}}; - request = spec.buildRequests(bidRequests, bidderRequestWithoutUserAgentClientHints); - expect(request[0].data.device?.sua).to.not.exist; - }); - }); - }); - - context('when there is a consent management framework', function () { - let bidRequests; - let mockConfig; - let bidderRequest; - - beforeEach(function () { - bidRequests = [{ - bidder: 'openx', - params: { - unit: '12345678-banner', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-1' - }, { - bidder: 'openx', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - params: { - unit: '12345678-video', - delDomain: 'test-del-domain' - }, - 'adUnitCode': 'adunit-code', - - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-2' - }]; - }); - - describe('us_privacy', function () { - beforeEach(function () { - bidderRequest = { - uspConsent: '1YYN', - refererInfo: {} - }; - - sinon.stub(config, 'getConfig').callsFake((key) => { - return utils.deepAccess(mockConfig, key); - }); - }); - - afterEach(function () { - config.getConfig.restore(); - }); - - it('should send a signal to specify that US Privacy applies to this request', function () { - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs.ext.us_privacy).to.equal('1YYN'); - expect(request[1].data.regs.ext.us_privacy).to.equal('1YYN'); - }); - - it('should not send the regs object, when consent string is undefined', function () { - delete bidderRequest.uspConsent; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs?.us_privacy).to.not.exist; - }); - }); - - describe('GDPR', function () { - beforeEach(function () { - bidderRequest = { - gdprConsent: { - consentString: 'test-gdpr-consent-string', - addtlConsent: 'test-addtl-consent-string', - gdprApplies: true - }, - refererInfo: {} - }; - - mockConfig = { - consentManagement: { - cmpApi: 'iab', - timeout: 1111, - allowAuctionWithoutConsent: 'cancel' - } - }; - - sinon.stub(config, 'getConfig').callsFake((key) => { - return utils.deepAccess(mockConfig, key); - }); - }); - - afterEach(function () { - config.getConfig.restore(); - }); - - it('should send a signal to specify that GDPR applies to this request', function () { - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs.ext.gdpr).to.equal(1); - expect(request[1].data.regs.ext.gdpr).to.equal(1); - }); - - it('should send the consent string', function () { - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - expect(request[1].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - }); - - it('should send the addtlConsent string', function () { - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.user.ext.ConsentedProvidersSettings.consented_providers).to.equal(bidderRequest.gdprConsent.addtlConsent); - expect(request[1].data.user.ext.ConsentedProvidersSettings.consented_providers).to.equal(bidderRequest.gdprConsent.addtlConsent); - }); - - it('should send a signal to specify that GDPR does not apply to this request', function () { - bidderRequest.gdprConsent.gdprApplies = false; - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs.ext.gdpr).to.equal(0); - expect(request[1].data.regs.ext.gdpr).to.equal(0); - }); - - it('when GDPR application is undefined, should not send a signal to specify whether GDPR applies to this request, ' + - 'but can send consent data, ', function () { - delete bidderRequest.gdprConsent.gdprApplies; - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs?.ext?.gdpr).to.not.be.ok; - expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - expect(request[1].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - }); - - it('when consent string is undefined, should not send the consent string, ', function () { - delete bidderRequest.gdprConsent.consentString; - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.imp[0].ext.consent).to.equal(undefined); - expect(request[1].data.imp[0].ext.consent).to.equal(undefined); - }); - }); - }); - - context('coppa', function() { - it('when there are no coppa param settings, should not send a coppa flag', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.regs?.coppa).to.be.not.ok; - }); - - it('should send a coppa flag there is when there is coppa param settings in the bid requests', function () { - let mockConfig = { - coppa: true - }; - - sinon.stub(config, 'getConfig').callsFake((key) => { - return utils.deepAccess(mockConfig, key); - }); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.regs.coppa).to.equal(1); - }); - - it('should send a coppa flag there is when there is coppa param settings in the bid params', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - request.params = {coppa: true}; - expect(request[0].data.regs.coppa).to.equal(1); - }); - - after(function () { - config.getConfig.restore() - }); - }); - - context('do not track (DNT)', function() { - let doNotTrackStub; - - beforeEach(function () { - doNotTrackStub = sinon.stub(utils, 'getDNT'); - }); - afterEach(function() { - doNotTrackStub.restore(); - }); - - it('when there is a do not track, should send a dnt', function () { - doNotTrackStub.returns(1); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(1); - }); - - it('when there is not do not track, don\'t send dnt', function () { - doNotTrackStub.returns(0); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(0); - }); - - it('when there is no defined do not track, don\'t send dnt', function () { - doNotTrackStub.returns(null); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(0); - }); - }); - - context('supply chain (schain)', function () { - let bidRequests; - let schainConfig; - const supplyChainNodePropertyOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain']; - - beforeEach(function () { - schainConfig = { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'exchange1.com', - sid: '1234', - hp: 1, - rid: 'bid-request-1', - name: 'publisher', - domain: 'publisher.com' - // omitted ext - }, - { - asi: 'exchange2.com', - sid: 'abcd', - hp: 1, - rid: 'bid-request-2', - // name field missing - domain: 'intermediary.com' - }, - { - asi: 'exchange3.com', - sid: '4321', - hp: 1, - // request id - // name field missing - domain: 'intermediary-2.com' - } - ] - }; - - bidRequests = [{ - bidder: 'openx', - params: { - unit: '11', - delDomain: 'test-del-domain' - }, - adUnitCode: '/adunit-code/test-path', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - schain: schainConfig - }]; - }); - - it('should send a supply chain object', function () { - const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain).to.equal(schainConfig); - }); - - it('should send the supply chain object with the right version', function () { - const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain.ver).to.equal(schainConfig.ver); - }); - - it('should send the supply chain object with the right complete value', function () { - const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain.complete).to.equal(schainConfig.complete); - }); - }); - - context('when there are userid providers', function () { - const userIdAsEids = [ - { - source: 'adserver.org', - uids: [{ - id: 'some-random-id-value', - atype: 1, - ext: { - rtiPartner: 'TDID' - } - }] - }, - { - source: 'id5-sync.com', - uids: [{ - id: 'some-random-id-value', - atype: 1 - }] - }, - { - source: 'sharedid.org', - uids: [{ - id: 'some-random-id-value', - atype: 1, - ext: { - third: 'some-random-id-value' - } - }] - } - ]; - - it(`should send the user id under the extended ids`, function () { - const bidRequestsWithUserId = [{ - bidder: 'openx', - params: { - unit: '11', - delDomain: 'test-del-domain' - }, - userId: { - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - userIdAsEids: userIdAsEids - }]; - // enrich bid request with userId key/value - - const request = spec.buildRequests(bidRequestsWithUserId, mockBidderRequest); - expect(request[0].data.user.ext.eids).to.equal(userIdAsEids); - }); - - it(`when no user ids are available, it should not send any extended ids`, function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data).to.not.have.any.keys('user'); - }); - }); - - context('FLEDGE', function() { - it('when FLEDGE is enabled, should send whatever is set in ortb2imp.ext.ae in all bid requests', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, { - ...mockBidderRequest, - fledgeEnabled: true - }); - expect(request[0].data.imp[0].ext.ae).to.equal(2); - }); - }); - }); - - context('banner', function () { - it('should send bid request with a mediaTypes specified with banner type', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.imp[0]).to.have.any.keys(BANNER); - }); - }); - - if (FEATURES.VIDEO) { - context('video', function () { - it('should send bid request with a mediaTypes specified with video type', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[1].data.imp[0]).to.have.any.keys(VIDEO); - }); - - it('Update imp.video with OpenRTB options from mimeTypes and params', function() { - const bid01 = new BidRequestBuilder({ - adUnitCode: 'adunit-code-01', - mediaTypes: { - banner: { sizes: [[300, 250]] }, - video: { - context: 'outstream', - playerSize: [[300, 250]], - mimes: ['video/mp4'], - protocols: [8] - } - }, - }).withParams({ - // options in video, will merge - video: { - skip: 1, - skipafter: 4, - minduration: 10, - maxduration: 30 - } - }).build(); - - const bidderRequest = new BidderRequestBuilder().build(); - const expected = { - mimes: ['video/mp4'], - skip: 1, - skipafter: 4, - minduration: 10, - maxduration: 30, - placement: 4, - protocols: [8], - w: 300, - h: 250 - }; - const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests).to.have.lengthOf(2); - expect(requests[1].data.imp[0].video).to.deep.equal(expected); - }); - }); - } - }); - - describe('interpretResponse()', function () { - let bidRequestConfigs; - let bidRequest; - let bidResponse; - let bid; - - context('when there is an nbr response', function () { - let bids; - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - - bidResponse = {nbr: 0}; // Unknown error - bids = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - it('should not return any bids', function () { - expect(bids.length).to.equal(0); - }); - }); - - context('when no seatbid in response', function () { - let bids; - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - - bidResponse = {ext: {}, id: 'test-bid-id'}; - bids = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - it('should not return any bids', function () { - expect(bids.length).to.equal(0); - }); - }); - - context('when there is no response', function () { - let bids; - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - - bidResponse = ''; // Unknown error - bids = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - it('should not return any bids', function () { - expect(bids.length).to.equal(0); - }); - }); - - const SAMPLE_BID_REQUESTS = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - const SAMPLE_BID_RESPONSE = { - seatbid: [{ - bid: [{ - impid: 'test-bid-id', - price: 2, - w: 300, - h: 250, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup', - adomain: ['brand.com'], - ext: { - dsp_id: '123', - buyer_id: '456', - brand_id: '789', - paf: { - content_id: 'paf_content_id' - } - } - }] - }], - cur: 'AUS', - ext: { - paf: { - transmission: {version: '12'} - } - } - }; - - context('when there is a response, the common response properties', function () { - beforeEach(function () { - bidRequestConfigs = deepClone(SAMPLE_BID_REQUESTS); - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - bidResponse = deepClone(SAMPLE_BID_RESPONSE); - - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; - }); - - it('should return a price', function () { - expect(bid.cpm).to.equal(bidResponse.seatbid[0].bid[0].price); - }); - - it('should return a request id', function () { - expect(bid.requestId).to.equal(bidResponse.seatbid[0].bid[0].impid); - }); - - it('should return width and height for the creative', function () { - expect(bid.width).to.equal(bidResponse.seatbid[0].bid[0].w); - expect(bid.height).to.equal(bidResponse.seatbid[0].bid[0].h); - }); - - it('should return a creativeId', function () { - expect(bid.creativeId).to.equal(bidResponse.seatbid[0].bid[0].crid); - }); - - it('should return an ad', function () { - expect(bid.ad).to.equal(bidResponse.seatbid[0].bid[0].adm); - }); - - it('should return a deal id if it exists', function () { - expect(bid.dealId).to.equal(bidResponse.seatbid[0].bid[0].dealid); - }); - - it('should have a time-to-live of 5 minutes', function () { - expect(bid.ttl).to.equal(300); - }); - - it('should always return net revenue', function () { - expect(bid.netRevenue).to.equal(true); - }); - - it('should return a currency', function () { - expect(bid.currency).to.equal(bidResponse.cur); - }); - - it('should return a brand ID', function () { - expect(bid.meta.brandId).to.equal(bidResponse.seatbid[0].bid[0].ext.brand_id); - }); - - it('should return a dsp ID', function () { - expect(bid.meta.networkId).to.equal(bidResponse.seatbid[0].bid[0].ext.dsp_id); - }); - - it('should return a buyer ID', function () { - expect(bid.meta.advertiserId).to.equal(bidResponse.seatbid[0].bid[0].ext.buyer_id); - }); - - it('should return adomain', function () { - expect(bid.meta.advertiserDomains).to.equal(bidResponse.seatbid[0].bid[0].adomain); - }); - - it('should return paf fields', function () { - const paf = { - transmission: {version: '12'}, - content_id: 'paf_content_id' - } - expect(bid.meta.paf).to.deep.equal(paf); - }); - }); - - context('when there is more than one response', () => { - let bids; - beforeEach(function () { - bidRequestConfigs = deepClone(SAMPLE_BID_REQUESTS); - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - bidResponse = deepClone(SAMPLE_BID_RESPONSE); - bidResponse.seatbid[0].bid.push(deepClone(bidResponse.seatbid[0].bid[0])); - bidResponse.seatbid[0].bid[1].ext.paf.content_id = 'second_paf' - - bids = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - it('should not confuse paf content_id', () => { - expect(bids.map(b => b.meta.paf.content_id)).to.eql(['paf_content_id', 'second_paf']); - }); - }) - - context('when the response is a banner', function() { - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - - bidResponse = { - seatbid: [{ - bid: [{ - impid: 'test-bid-id', - price: 2, - w: 300, - h: 250, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup' - }] - }], - cur: 'AUS' - }; - - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; - }); - - it('should return the proper mediaType', function () { - it('should return a creativeId', function () { - expect(bid.mediaType).to.equal(Object.keys(bidRequestConfigs[0].mediaTypes)[0]); - }); - }); - }); - - if (FEATURES.VIDEO) { - context('when the response is a video', function() { - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [[640, 360], [854, 480]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - - bidResponse = { - seatbid: [{ - bid: [{ - impid: 'test-bid-id', - price: 2, - w: 854, - h: 480, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup', - }] - }], - cur: 'AUS' - }; - }); - - it('should return the proper mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; - expect(bid.mediaType).to.equal(Object.keys(bidRequestConfigs[0].mediaTypes)[0]); - }); - - it('should return the proper mediaType', function () { - const winUrl = 'https//my.win.url'; - bidResponse.seatbid[0].bid[0].nurl = winUrl - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; - - expect(bid.vastUrl).to.equal(winUrl); - }); - }); - } - - context('when the response contains FLEDGE interest groups config', function() { - let response; - - beforeEach(function () { - sinon.stub(config, 'getConfig') - .withArgs('fledgeEnabled') - .returns(true); - - bidRequestConfigs = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - - bidResponse = { - seatbid: [{ - bid: [{ - impid: 'test-bid-id', - price: 2, - w: 300, - h: 250, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup' - }] - }], - cur: 'AUS', - ext: { - fledge_auction_configs: { - 'test-bid-id': { - seller: 'codinginadtech.com', - interestGroupBuyers: ['somedomain.com'], - sellerTimeout: 0, - perBuyerSignals: { - 'somedomain.com': { - base_bid_micros: 0.1, - disallowed_advertiser_ids: [ - '1234', - '2345' - ], - multiplier: 1.3, - use_bid_multiplier: true, - win_reporting_id: '1234567asdf' - } - } - } - } - } - }; - - response = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - afterEach(function () { - config.getConfig.restore(); - }); - - it('should return FLEDGE auction_configs alongside bids', function () { - expect(response).to.have.property('bids'); - expect(response).to.have.property('fledgeAuctionConfigs'); - expect(response.fledgeAuctionConfigs.length).to.equal(1); - expect(response.fledgeAuctionConfigs[0].bidId).to.equal('test-bid-id'); - }); - }); - }); - - describe('user sync', function () { - it('should register the default image pixel if no pixels available', function () { - let syncs = spec.getUserSyncs( - {pixelEnabled: true}, - [] - ); - expect(syncs).to.deep.equal([{type: 'image', url: DEFAULT_SYNC}]); - }); - - it('should register custom syncUrl when exists', function () { - let syncs = spec.getUserSyncs( - {pixelEnabled: true}, - [{body: {ext: {delDomain: 'www.url.com'}}}] - ); - expect(syncs).to.deep.equal([{type: 'image', url: 'https://www.url.com/w/1.0/pd'}]); - }); - - it('should register custom syncUrl when exists', function () { - let syncs = spec.getUserSyncs( - {pixelEnabled: true}, - [{body: {ext: {platform: 'abc'}}}] - ); - expect(syncs).to.deep.equal([{type: 'image', url: SYNC_URL + '?ph=abc'}]); - }); - - it('when iframe sync is allowed, it should register an iframe sync', function () { - let syncs = spec.getUserSyncs( - {iframeEnabled: true}, - [] - ); - expect(syncs).to.deep.equal([{type: 'iframe', url: DEFAULT_SYNC}]); - }); - - it('should prioritize iframe over image for user sync', function () { - let syncs = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [] - ); - expect(syncs).to.deep.equal([{type: 'iframe', url: DEFAULT_SYNC}]); - }); - - describe('when gdpr applies', function () { - let gdprConsent; - let gdprPixelUrl; - const consentString = 'gdpr-pixel-consent'; - const gdprApplies = '1'; - beforeEach(() => { - gdprConsent = { - consentString, - gdprApplies: true - }; - - gdprPixelUrl = `${SYNC_URL}&gdpr=${gdprApplies}&gdpr_consent=${consentString}`; - }); - - it('when there is a response, it should have the gdpr query params', () => { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - gdprConsent - ); - - expect(url).to.have.string(`gdpr_consent=${consentString}`); - expect(url).to.have.string(`gdpr=${gdprApplies}`); - }); - - it('should not send signals if no consent object is available', function () { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - ); - expect(url).to.not.have.string('gdpr_consent='); - expect(url).to.not.have.string('gdpr='); - }); - }); - - describe('when ccpa applies', function () { - let usPrivacyConsent; - let uspPixelUrl; - const privacyString = 'TEST'; - beforeEach(() => { - usPrivacyConsent = 'TEST'; - uspPixelUrl = `${DEFAULT_SYNC}&us_privacy=${privacyString}` - }); - it('should send the us privacy string, ', () => { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - undefined, - usPrivacyConsent - ); - expect(url).to.have.string(`us_privacy=${privacyString}`); - }); - - it('should not send signals if no consent string is available', function () { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - ); - expect(url).to.not.have.string('us_privacy='); - }); - }); - }); -}); diff --git a/test/spec/modules/operaadsBidAdapter_spec.js b/test/spec/modules/operaadsBidAdapter_spec.js index 45bc8995a5c..37d4a2c7bc0 100644 --- a/test/spec/modules/operaadsBidAdapter_spec.js +++ b/test/spec/modules/operaadsBidAdapter_spec.js @@ -1,7 +1,7 @@ -import { expect } from 'chai'; -import { spec } from 'modules/operaadsBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; +import {expect} from 'chai'; +import {spec} from 'modules/operaadsBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {BANNER, NATIVE, VIDEO} from 'src/mediaTypes.js'; describe('Opera Ads Bid Adapter', function () { describe('Test isBidRequestValid', function () { @@ -234,7 +234,7 @@ describe('Opera Ads Bid Adapter', function () { requestData = JSON.parse(req.data); }).to.not.throw(); - expect(requestData.id).to.equal(bidderRequest.auctionId); + expect(requestData.id).to.exist; expect(requestData.tmax).to.equal(bidderRequest.timeout); expect(requestData.test).to.equal(0); expect(requestData.imp).to.be.an('array').that.have.lengthOf(1); diff --git a/test/spec/modules/optidigitalBidAdapter_spec.js b/test/spec/modules/optidigitalBidAdapter_spec.js index 8c222650f7e..caa12483ea9 100755 --- a/test/spec/modules/optidigitalBidAdapter_spec.js +++ b/test/spec/modules/optidigitalBidAdapter_spec.js @@ -557,7 +557,6 @@ describe('optidigitalAdapterTests', function () { let expectedResponse = [ { 'placementId': 'Billboard_Top', - 'transactionId': 'cf5faec3-fcee-4f26-80ae-fc8b6cf23b7d', 'requestId': '83fb53a5e67f49', 'ttl': 150, 'creativeId': 'mobile_pos_2', @@ -572,7 +571,6 @@ describe('optidigitalAdapterTests', function () { } }, { 'placementId': 'Billboard_Bottom', - 'transactionId': 'df5faec3-fcee-4f26-80ae-fc8b6cf23b7d', 'requestId': '93fb53a5e67f49', 'ttl': 150, 'creativeId': 'mobile_pos_2', diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index 0d1396866e7..e7a0c8becfb 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -9,7 +9,11 @@ describe('orbidderBidAdapter', () => { const defaultBidRequestBanner = { bidId: 'd66fa86787e0b0ca900a96eacfd5f0bb', auctionId: 'ccc4c7cdfe11cfbd74065e6dd28413d8', - transactionId: 'd58851660c0c4461e4aa06344fc9c0c6', + ortb2Imp: { + ext: { + tid: 'd58851660c0c4461e4aa06344fc9c0c6', + } + }, bidRequestCount: 1, adUnitCode: 'adunit-code', sizes: [[300, 250], [300, 600]], @@ -27,7 +31,11 @@ describe('orbidderBidAdapter', () => { const defaultBidRequestNative = { bidId: 'd66fa86787e0b0ca900a96eacfd5f0bc', auctionId: 'ccc4c7cdfe11cfbd74065e6dd28413d9', - transactionId: 'd58851660c0c4461e4aa06344fc9c0c7', + ortb2Imp: { + ext: { + tid: 'd58851660c0c4461e4aa06344fc9c0c7', + } + }, bidRequestCount: 1, adUnitCode: 'adunit-code-native', sizes: [], @@ -170,7 +178,7 @@ describe('orbidderBidAdapter', () => { expect(request.data.bidId).to.equal(defaultBidRequestBanner.bidId); expect(request.data.auctionId).to.equal(defaultBidRequestBanner.auctionId); - expect(request.data.transactionId).to.equal(defaultBidRequestBanner.transactionId); + expect(request.data.transactionId).to.equal(defaultBidRequestBanner.ortb2Imp.ext.tid); expect(request.data.bidRequestCount).to.equal(defaultBidRequestBanner.bidRequestCount); expect(request.data.adUnitCode).to.equal(defaultBidRequestBanner.adUnitCode); expect(request.data.pageUrl).to.equal('https://localhost:9876/'); @@ -187,7 +195,7 @@ describe('orbidderBidAdapter', () => { expect(nativeRequest.data.bidId).to.equal(defaultBidRequestNative.bidId); expect(nativeRequest.data.auctionId).to.equal(defaultBidRequestNative.auctionId); - expect(nativeRequest.data.transactionId).to.equal(defaultBidRequestNative.transactionId); + expect(nativeRequest.data.transactionId).to.equal(defaultBidRequestNative.ortb2Imp.ext.tid); expect(nativeRequest.data.bidRequestCount).to.equal(defaultBidRequestNative.bidRequestCount); expect(nativeRequest.data.adUnitCode).to.equal(defaultBidRequestNative.adUnitCode); expect(nativeRequest.data.pageUrl).to.equal('https://localhost:9876/'); diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index f5ce00ed8df..ab72ff85ab0 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec } from 'modules/outbrainBidAdapter.js'; import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr'; import { createEidsArray } from 'modules/userId/eids.js'; +import * as utils from 'src/utils.js'; describe('Outbrain Adapter', function () { describe('Bid request and response', function () { @@ -227,6 +228,7 @@ describe('Outbrain Adapter', function () { }) const commonBidderRequest = { + bidderRequestId: 'mock-uuid', refererInfo: { page: 'https://example.com/' } @@ -263,6 +265,7 @@ describe('Outbrain Adapter', function () { ] } const expectedData = { + id: 'mock-uuid', site: { page: 'https://example.com/', publisher: { @@ -305,6 +308,7 @@ describe('Outbrain Adapter', function () { ...displayBidRequestParams, } const expectedData = { + id: 'mock-uuid', site: { page: 'https://example.com/', publisher: { @@ -352,6 +356,7 @@ describe('Outbrain Adapter', function () { ...videoBidRequestParams, } const expectedData = { + id: 'mock-uuid', site: { page: 'https://example.com/', publisher: { diff --git a/test/spec/modules/oxxionAnalyticsAdapter_spec.js b/test/spec/modules/oxxionAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..63e929d7160 --- /dev/null +++ b/test/spec/modules/oxxionAnalyticsAdapter_spec.js @@ -0,0 +1,324 @@ +import oxxionAnalytics from 'modules/oxxionAnalyticsAdapter.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; +let adapterManager = require('src/adapterManager').default; +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('Oxxion Analytics', function () { + let timestamp = new Date() - 256; + let auctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; + let timeout = 1500; + + let bidTimeout = [ + { + 'bidId': '5fe418f2d70364', + 'bidder': 'appnexusAst', + 'adUnitCode': 'tag_200124_banner', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b' + } + ]; + + const auctionEnd = { + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'timestamp': 1647424261187, + 'auctionEnd': 1647424261714, + 'auctionStatus': 'completed', + 'adUnits': [ + { + 'code': 'tag_200124_banner', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 600 + ] + ] + } + }, + 'bids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': 123456 + } + }, + { + 'bidder': 'appnexusAst', + 'params': { + 'placementId': 234567 + } + } + ], + 'sizes': [ + [ + 300, + 600 + ] + ], + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40' + } + ], + 'adUnitCodes': [ + 'tag_200124_banner' + ], + 'bidderRequests': [ + { + 'bidderCode': 'appnexus', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'bidderRequestId': '11dc6ff6378de7', + 'bids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': 123456 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'tag_200124_banner', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', + 'sizes': [ + [ + 300, + 600 + ] + ], + 'bidId': '34a63e5d5378a3', + 'bidderRequestId': '11dc6ff6378de7', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1647424261187, + 'timeout': 1000, + 'gdprConsent': { + 'consentString': 'CONSENT', + 'gdprApplies': true, + 'apiVersion': 2, + 'vendorData': 'a lot of borring stuff', + }, + 'start': 1647424261189 + }, + ], + 'noBids': [ + { + 'bidder': 'appnexusAst', + 'params': { + 'placementId': 10471298 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'tag_200124_banner', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', + 'sizes': [ + [ + 300, + 600 + ] + ], + 'bidId': '5fe418f2d70364', + 'bidderRequestId': '4229a45ab8ea87', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'bidsReceived': [ + { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 600, + 'statusMessage': 'Bid available', + 'adId': '7a4ced80f33d33', + 'requestId': '34a63e5d5378a3', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 27.4276, + 'creativeId': '158534630', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 2000, + 'ad': 'some html', + 'meta': { + 'advertiserDomains': [ + 'example.com' + ] + }, + 'originalCpm': 25.02521, + 'originalCurrency': 'EUR', + 'responseTimestamp': 1647424261559, + 'requestTimestamp': 1647424261189, + 'bidder': 'appnexus', + 'adUnitCode': 'tag_200124_banner', + 'timeToRespond': 370, + 'pbLg': '5.00', + 'pbMg': '20.00', + 'pbHg': '20.00', + 'pbAg': '20.00', + 'pbDg': '20.00', + 'pbCg': '20.000000', + 'size': '300x600', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '7a4ced80f33d33', + 'hb_pb': '20.000000', + 'hb_size': '300x600', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'example.com' + } + } + ], + 'winningBids': [ + + ], + 'timeout': 1000 + }; + + let bidWon = { + 'bidderCode': 'appnexus', + 'width': 970, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '65d16ef039a97a', + 'requestId': '2bd3e8ff8a113f', + 'transactionId': '8b2a8629-d1ea-4bb1-aff0-e335b96dd002', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 27.4276, + 'creativeId': '158533702', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 2000, + 'ad': 'some html', + 'meta': { + 'advertiserDomains': [ + 'example.com' + ] + }, + 'originalCpm': 25.02521, + 'originalCurrency': 'EUR', + 'responseTimestamp': 1647424261558, + 'requestTimestamp': 1647424261189, + 'bidder': 'appnexus', + 'adUnitCode': 'tag_200123_banner', + 'timeToRespond': 369, + 'originalBidder': 'appnexus', + 'pbLg': '5.00', + 'pbMg': '20.00', + 'pbHg': '20.00', + 'pbAg': '20.00', + 'pbDg': '20.00', + 'pbCg': '20.000000', + 'size': '970x250', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '65d16ef039a97a', + 'hb_pb': '20.000000', + 'hb_size': '970x250', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'example.com' + }, + 'status': 'rendered', + 'params': [ + { + 'placementId': 123456 + } + ] + }; + + after(function () { + oxxionAnalytics.disableAnalytics(); + }); + + describe('main test flow', function () { + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + sinon.spy(oxxionAnalytics, 'track'); + }); + afterEach(function () { + events.getEvents.restore(); + oxxionAnalytics.disableAnalytics(); + oxxionAnalytics.track.restore(); + }); + + it('test auctionEnd', function () { + adapterManager.registerAnalyticsAdapter({ + code: 'oxxion', + adapter: oxxionAnalytics + }); + + adapterManager.enableAnalytics({ + provider: 'oxxion', + options: { + domain: 'test' + } + }); + + events.emit(constants.EVENTS.BID_REQUESTED, auctionEnd['bidderRequests'][0]); + events.emit(constants.EVENTS.BID_RESPONSE, auctionEnd['bidsReceived'][0]); + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + expect(server.requests.length).to.equal(1); + let message = JSON.parse(server.requests[0].requestBody); + expect(message).to.have.property('auctionEnd').exist; + expect(message.auctionEnd).to.have.lengthOf(1); + expect(message.auctionEnd[0]).to.have.property('bidsReceived').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidsReceived[0]).not.to.have.property('ad'); + expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('meta'); + expect(message.auctionEnd[0].bidsReceived[0].meta).to.have.property('advertiserDomains'); + expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('adId'); + expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).not.to.have.property('vendorData'); + sinon.assert.callCount(oxxionAnalytics.track, 5); + }); + + it('test bidWon', function() { + adapterManager.registerAnalyticsAdapter({ + code: 'oxxion', + adapter: oxxionAnalytics + }); + + adapterManager.enableAnalytics({ + provider: 'oxxion', + options: { + domain: 'test' + } + }); + events.emit(constants.EVENTS.BID_WON, bidWon); + expect(server.requests.length).to.equal(1); + let message = JSON.parse(server.requests[0].requestBody); + expect(message).not.to.have.property('ad'); + expect(message).to.have.property('adId') + expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); + // sinon.assert.callCount(oxxionAnalytics.track, 1); + }); + }); +}); diff --git a/test/spec/modules/oxxionRtdProvider_spec.js b/test/spec/modules/oxxionRtdProvider_spec.js index 776076cc215..5bd8756bb6f 100644 --- a/test/spec/modules/oxxionRtdProvider_spec.js +++ b/test/spec/modules/oxxionRtdProvider_spec.js @@ -114,7 +114,8 @@ describe('oxxionRtdProvider', () => { auctionEnd.bidsReceived = bids; it('call everything', function() { oxxionSubmodule.getBidRequestData(request, null, moduleConfig); - oxxionSubmodule.onAuctionEndEvent(auctionEnd, moduleConfig); + oxxionSubmodule.onBidResponseEvent(auctionEnd.bidsReceived[0], moduleConfig); + oxxionSubmodule.onBidResponseEvent(auctionEnd.bidsReceived[1], moduleConfig); }); it('check vastImpUrl', function() { expect(auctionEnd.bidsReceived[0]).to.have.property('vastImpUrl'); @@ -136,7 +137,7 @@ describe('oxxionRtdProvider', () => { expect(inlineImpressions).to.have.lengthOf.above(0); }); it('check cpmIncrement', function() { - expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(encodeURI('cpmIncrement=1')); + expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(encodeURI('cpmIncrement=0')); }); }); }); diff --git a/test/spec/modules/pairIdSystem_spec.js b/test/spec/modules/pairIdSystem_spec.js index 4f75666affe..0bb42e56c25 100644 --- a/test/spec/modules/pairIdSystem_spec.js +++ b/test/spec/modules/pairIdSystem_spec.js @@ -3,11 +3,11 @@ import * as utils from 'src/utils.js'; describe('pairId', function () { let sandbox; - let logErrorStub; + let logInfoStub; beforeEach(() => { sandbox = sinon.sandbox.create(); - logErrorStub = sandbox.stub(utils, 'logError'); + logInfoStub = sandbox.stub(utils, 'logInfo'); }); afterEach(() => { sandbox.restore(); @@ -15,7 +15,7 @@ describe('pairId', function () { it('should log an error if no ID is found when getId', function() { pairIdSubmodule.getId({ params: {} }); - expect(logErrorStub.calledOnce).to.be.true; + expect(logInfoStub.calledOnce).to.be.true; }); it('should read pairId from local storage if exists', function() { @@ -65,4 +65,17 @@ describe('pairId', function () { }}) expect(id).to.be.deep.equal({id: pairIds}) }) + + it('should not get data from storage if local storage and cookies are disabled', function () { + sandbox.stub(storage, 'localStorageIsEnabled').returns(false); + sandbox.stub(storage, 'cookiesAreEnabled').returns(false); + let id = pairIdSubmodule.getId({ + params: { + liveramp: { + storageKey: 'lr_pairId_custom' + } + } + }) + expect(id).to.equal(undefined) + }) }); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 6427b917134..895c3754876 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -26,6 +26,7 @@ import 'modules/consentManagement.js'; import 'modules/consentManagementUsp.js'; import 'modules/schain.js'; import 'modules/fledgeForGpt.js'; +import * as redactor from 'src/activities/redactor.js'; import {hook} from '../../../src/hook.js'; import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; import {auctionManager} from '../../../src/auctionManager.js'; @@ -636,16 +637,103 @@ describe('S2S Adapter', function () { resetSyncedStatus(); }); - it('should set id and source.tid to auction ID', function () { - config.setConfig({ s2sConfig: CONFIG }); + describe('FPD redaction', () => { + let sandbox, ortb2Fragments, redactorMocks, s2sReq; - adapter.callBids(OUTSTREAM_VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + beforeEach(() => { + sandbox = sinon.sandbox.create(); + redactorMocks = {}; + sandbox.stub(redactor, 'redactor').callsFake((params) => { + if (!redactorMocks.hasOwnProperty(params.component)) { + redactorMocks[params.component] = { + ortb2: sinon.stub().callsFake(o => o), + bidRequest: sinon.stub().callsFake(o => o) + } + } + return redactorMocks[params.component]; + }) + ortb2Fragments = { + global: { + mock: 'value' + }, + bidder: { + appnexus: { + mock: 'A' + } + } + } + const s2sConfig = { + ...CONFIG, + }; + config.setConfig({s2sConfig}); + s2sReq = { + ...REQUEST, + s2sConfig + }; + }); - const requestBid = JSON.parse(server.requests[0].requestBody); - expect(requestBid.id).to.equal(BID_REQUESTS[0].auctionId); - expect(requestBid.source.tid).to.equal(BID_REQUESTS[0].auctionId); + afterEach(() => { + sandbox.restore(); + }) + + function callBids() { + adapter.callBids({ + ...s2sReq, + ortb2Fragments + }, BID_REQUESTS, addBidResponse, done, ajax); + } + + it('should be applied to ortb2Fragments', () => { + callBids(); + sinon.assert.calledWithMatch(redactorMocks['prebid.pbsBidAdapter'].ortb2, ortb2Fragments.global); + Object.entries(ortb2Fragments.bidder).forEach(([bidder, ortb2]) => { + sinon.assert.calledWith(redactorMocks[`bidder.${bidder}`].ortb2, ortb2); + }); + }); + + it('should be applied to ad units', () => { + callBids(); + s2sReq.ad_units.forEach(au => { + sinon.assert.calledWith(redactorMocks['prebid.pbsBidAdapter'].bidRequest, au); + au.bids.forEach((bid) => { + sinon.assert.calledWith(redactorMocks[`bidder.${bid.bidder}`].bidRequest, bid); + }) + }) + }) }); + describe('transaction IDs', () => { + let s2sReq; + beforeEach(() => { + s2sReq = { + ...REQUEST, + ortb2Fragments: {global: {source: {tid: 'mock-tid'}}}, + ad_units: REQUEST.ad_units.map(au => ({...au, ortb2Imp: {ext: {tid: 'mock-tid'}}})) + }; + BID_REQUESTS[0].bids[0].ortb2Imp = {ext: {tid: 'mock-tid'}}; + }); + + function makeRequest() { + adapter.callBids(s2sReq, BID_REQUESTS, addBidResponse, done, ajax); + return JSON.parse(server.requests[0].requestBody); + } + + it('should not be set when transmitTid is not allowed, with ext.prebid.createtids: false', () => { + config.setConfig({ s2sConfig: CONFIG, enableTIDs: false }); + const req = makeRequest(); + expect(req.source.tid).to.not.exist; + expect(req.imp[0].ext.tid).to.not.exist; + expect(req.ext.prebid.createtids).to.equal(false); + }); + + it('should be picked from FPD otherwise', () => { + config.setConfig({s2sConfig: CONFIG, enableTIDs: true}); + const req = makeRequest(); + expect(req.source.tid).to.eql('mock-tid'); + expect(req.imp[0].ext.tid).to.eql('mock-tid'); + }) + }) + it('should set tmax to s2sConfig.timeout', () => { const cfg = {...CONFIG, timeout: 123}; config.setConfig({s2sConfig: cfg}); @@ -1405,11 +1493,9 @@ describe('S2S Adapter', function () { const ortbReq = JSON.parse(requestBid.imp[0].native.request); expect(ortbReq).to.deep.equal({ ...ORTB_NATIVE_REQ, - 'context': 1, - 'plcmttype': 1, 'eventtrackers': [{ event: 1, - methods: [1] + methods: [1, 2] }], }); expect(requestBid.imp[0].native.ver).to.equal('1.2'); @@ -1643,19 +1729,17 @@ describe('S2S Adapter', function () { const requestBid = JSON.parse(server.requests[0].requestBody); - expect(requestBid.ext).to.deep.equal({ - prebid: { - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true - }, - channel: { - name: 'pbjs', - version: 'v$prebid.version$' - } + sinon.assert.match(requestBid.ext.prebid, { + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true + }, + channel: { + name: 'pbjs', + version: 'v$prebid.version$' } - }); + }) }); it('skips dynamic aliases to request when skipPbsAliasing enabled', function () { @@ -1682,17 +1766,15 @@ describe('S2S Adapter', function () { const requestBid = JSON.parse(server.requests[0].requestBody); - expect(requestBid.ext).to.deep.equal({ - prebid: { - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true - }, - channel: { - name: 'pbjs', - version: 'v$prebid.version$' - } + sinon.assert.match(requestBid.ext.prebid, { + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true + }, + channel: { + name: 'pbjs', + version: 'v$prebid.version$' } }); }); diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index f232631d73d..e9ce4c8ccd3 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -286,7 +286,7 @@ describe('the price floors module', function () { let myBidRequest = { ...basicBidRequest }; // should take adunit floormin first even if lower - utils.deepSetValue(myBidRequest, 'ortb2Imp.ext.prebid.floorMin', 2.2); + utils.deepSetValue(myBidRequest, 'ortb2Imp.ext.prebid.floors.floorMin', 2.2); expect(getFirstMatchingFloor(inputFloorData, myBidRequest, { mediaType: 'banner', size: '*' })).to.deep.equal({ floorMin: 2.2, floorRuleValue: 1.0, @@ -297,7 +297,7 @@ describe('the price floors module', function () { delete inputFloorData.matchingInputs; // should take adunit floormin if higher - utils.deepSetValue(myBidRequest, 'ortb2Imp.ext.prebid.floorMin', 3.0); + utils.deepSetValue(myBidRequest, 'ortb2Imp.ext.prebid.floors.floorMin', 3.0); expect(getFirstMatchingFloor(inputFloorData, myBidRequest, { mediaType: 'banner', size: '*' })).to.deep.equal({ floorMin: 3.0, floorRuleValue: 1.0, @@ -582,7 +582,7 @@ describe('the price floors module', function () { const validateBidRequests = (getFloorExpected, FloorDataExpected) => { exposedAdUnits.forEach(adUnit => adUnit.bids.forEach(bid => { expect(bid.hasOwnProperty('getFloor')).to.equal(getFloorExpected); - expect(bid.floorData).to.deep.equal(FloorDataExpected); + sinon.assert.match(bid.floorData, FloorDataExpected); })); }; const runStandardAuction = (adUnits = [getAdUnitMock('test_div_1')]) => { @@ -918,16 +918,8 @@ describe('the price floors module', function () { floorProvider: 'floorprovider' }); }); - it('should not overwrite previous data object if the new one is bad', function () { + it('should ignore and reset floor data when provided with invalid data', function () { handleSetFloorsConfig({...basicFloorConfig}); - handleSetFloorsConfig({ - ...basicFloorConfig, - data: undefined - }); - handleSetFloorsConfig({ - ...basicFloorConfig, - data: 5 - }); handleSetFloorsConfig({ ...basicFloorConfig, data: { @@ -937,17 +929,7 @@ describe('the price floors module', function () { } }); runStandardAuction(); - validateBidRequests(true, { - skipped: false, - floorMin: undefined, - modelVersion: 'basic model', - modelWeight: 10, - modelTimestamp: 1606772895, - location: 'setConfig', - skipRate: 0, - fetchStatus: undefined, - floorProvider: undefined - }); + validateBidRequests(false, sinon.match({location: 'noData', skipped: true})); }); it('should dynamically add new schema fileds and functions if added via setConfig', function () { let deviceSpoof; @@ -1835,7 +1817,7 @@ describe('the price floors module', function () { transactionId: 'au', }; beforeEach(function () { - returnedBidResponse = {}; + returnedBidResponse = null; reject = sinon.stub().returns({status: 'rejected'}); indexStub = sinon.stub(auctionManager, 'index'); indexStub.get(() => stubAuctionIndex({adUnits: [adUnit]})); @@ -1867,7 +1849,7 @@ describe('the price floors module', function () { _floorDataForAuction[AUCTION_ID].data.values = { 'banner': 1.0 }; runBidResponse(); expect(reject.calledOnce).to.be.true; - expect(returnedBidResponse.status).to.equal('rejected'); + expect(returnedBidResponse).to.not.exist; }); it('if it finds a rule and does not floor should update the bid accordingly', function () { _floorDataForAuction[AUCTION_ID] = utils.deepClone(basicFloorConfig); diff --git a/test/spec/modules/pubCommonId_spec.js b/test/spec/modules/pubCommonId_spec.js deleted file mode 100644 index 7c539014cc5..00000000000 --- a/test/spec/modules/pubCommonId_spec.js +++ /dev/null @@ -1,353 +0,0 @@ -import { - requestBidHook, - getCookie, - setCookie, - setConfig, - isPubcidEnabled, - getExpInterval, - initPubcid, - setStorageItem, - getStorageItem, - removeStorageItem, - getPubcidConfig } from 'modules/pubCommonId.js'; -import { getAdUnits } from 'test/fixtures/fixtures.js'; -import * as auctionModule from 'src/auction.js'; -import { registerBidder } from 'src/adapters/bidderFactory.js'; -import * as utils from 'src/utils.js'; - -let events = require('src/events'); -let constants = require('src/constants.json'); - -var assert = require('chai').assert; -var expect = require('chai').expect; - -const ID_NAME = '_pubcid'; -const EXP = '_exp'; -const TIMEOUT = 2000; - -const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89a-f][0-9a-f]{3}-[0-9a-f]{12}$/; - -function cleanUp() { - window.document.cookie = ID_NAME + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; - localStorage.removeItem(ID_NAME); - localStorage.removeItem(ID_NAME + EXP); -} - -describe('Publisher Common ID', function () { - afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); - }); - describe('Decorate adUnits', function () { - beforeEach(function() { - cleanUp(); - }); - afterEach(function() { - cleanUp(); - }); - - it('Check same cookie', function () { - let adUnits1 = getAdUnits(); - let adUnits2 = getAdUnits(); - let innerAdUnits1; - let innerAdUnits2; - let pubcid; - - expect(getCookie(ID_NAME)).to.be.null; // there should be no cookie initially - expect(localStorage.getItem(ID_NAME)).to.be.null; // there should be no local storage item either - - requestBidHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); - pubcid = localStorage.getItem(ID_NAME); // local storage item is created after requestbidHook - - innerAdUnits1.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - expect(bid.crumbs.pubcid).to.equal(pubcid); - }); - }); - - // verify cookie is null - expect(getCookie(ID_NAME)).to.be.null; - - // verify same pubcid is preserved - requestBidHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); - assert.deepEqual(innerAdUnits1, innerAdUnits2); - }); - - it('Check different cookies', function () { - let adUnits1 = getAdUnits(); - let adUnits2 = getAdUnits(); - let innerAdUnits1; - let innerAdUnits2; - let pubcid1; - let pubcid2; - - requestBidHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); - pubcid1 = localStorage.getItem(ID_NAME); // get first pubcid - removeStorageItem(ID_NAME); // remove storage - - expect(pubcid1).to.not.be.null; - - innerAdUnits1.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - expect(bid.crumbs.pubcid).to.equal(pubcid1); - }); - }); - - requestBidHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); - pubcid2 = localStorage.getItem(ID_NAME); // get second pubcid - - innerAdUnits2.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - expect(bid.crumbs.pubcid).to.equal(pubcid2); - }); - }); - - expect(pubcid2).to.not.be.null; - expect(pubcid1).to.not.equal(pubcid2); - }); - - it('Check new cookie', function () { - let adUnits = getAdUnits(); - let innerAdUnits; - let pubcid = utils.generateUUID(); - - setCookie(ID_NAME, pubcid, 600); - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - innerAdUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - expect(bid.crumbs.pubcid).to.equal(pubcid); - }); - }); - }); - - it('Replicate cookie to storage', function() { - let adUnits = getAdUnits(); - let innerAdUnits; - let pubcid = utils.generateUUID(); - - setCookie(ID_NAME, pubcid, 600); - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - expect(getStorageItem(ID_NAME)).to.equal(pubcid); - }); - - it('Does not replicate storage to cookie', function() { - let adUnits = getAdUnits(); - let innerAdUnits; - let pubcid = utils.generateUUID(); - - setStorageItem(ID_NAME, pubcid, 600); - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - expect(getCookie(ID_NAME)).to.be.null; - }); - - it('Cookie only', function() { - setConfig({type: 'cookie'}); - let adUnits = getAdUnits(); - let innerAdUnits; - - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - expect(getCookie(ID_NAME)).to.match(uuidPattern); - expect(getStorageItem(ID_NAME)).to.be.null; - }); - - it('Storage only', function() { - setConfig({type: 'html5'}); - let adUnits = getAdUnits(); - let innerAdUnits; - - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - expect(getCookie(ID_NAME)).to.be.null; - expect(getStorageItem(ID_NAME)).to.match(uuidPattern); - }); - - it('Bad id recovery', function() { - let adUnits = getAdUnits(); - let innerAdUnits; - - setStorageItem(ID_NAME, 'undefined', 600); - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - expect(getStorageItem(ID_NAME)).to.match(uuidPattern); - }); - }); - - describe('Configuration', function () { - beforeEach(() => { - setConfig(); - cleanUp(); - }); - afterEach(() => { - setConfig(); - cleanUp(); - }); - - it('empty config', function () { - // this should work as usual - setConfig({}); - let adUnits = getAdUnits(); - let innerAdUnits; - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - let pubcid = localStorage.getItem(ID_NAME); - innerAdUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - expect(bid.crumbs.pubcid).to.equal(pubcid); - }); - }); - }); - - it('disable', function () { - setConfig({enable: false}); - let adUnits = getAdUnits(); - let unmodified = getAdUnits(); - let innerAdUnits; - expect(isPubcidEnabled()).to.be.false; - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - expect(getCookie(ID_NAME)).to.be.null; - assert.deepEqual(innerAdUnits, unmodified); - setConfig({enable: true}); // reset - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - innerAdUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - }); - }); - }); - - it('change expiration time', function () { - setConfig({expInterval: 100}); - expect(getExpInterval()).to.equal(100); - let adUnits = getAdUnits(); - let innerAdUnits; - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - innerAdUnits.every((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - }); - }); - }); - }); - - describe('Invoking requestBid', function () { - let createAuctionStub; - let adUnits; - let adUnitCodes; - let capturedReqs; - let sampleSpec = { - code: 'sampleBidder', - isBidRequestValid: () => {}, - buildRequest: (reqs) => {}, - interpretResponse: () => {}, - getUserSyncs: () => {} - }; - - beforeEach(function () { - adUnits = [{ - code: 'adUnit-code', - mediaTypes: { - banner: {}, - native: {}, - }, - sizes: [[300, 200], [300, 600]], - bids: [ - {bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}} - ] - }]; - adUnitCodes = ['adUnit-code']; - let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: TIMEOUT}); - createAuctionStub = sinon.stub(auctionModule, 'newAuction'); - createAuctionStub.returns(auction); - initPubcid(); - registerBidder(sampleSpec); - }); - - afterEach(function () { - auctionModule.newAuction.restore(); - }); - - it('test hook', function() { - $$PREBID_GLOBAL$$.requestBids({adUnits}); - adUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('crumbs.pubcid'); - }); - }); - }); - }); - - describe('Storage item functions', () => { - beforeEach(() => { cleanUp(); }); - afterEach(() => { cleanUp(); }); - - it('Test set', () => { - const key = ID_NAME; - const val = 'test-set-value'; - // Set item in localStorage - const now = Date.now(); - setStorageItem(key, val, 100); - // Check both item and expiry time are stored - const expVal = localStorage.getItem(key + EXP); - const storedVal = localStorage.getItem(key); - // Verify expiry - expect(expVal).to.not.be.null; - const expDate = new Date(expVal); - expect((expDate.getTime() - now) / 1000).to.be.closeTo(100 * 60, 5); - // Verify value - expect(storedVal).to.equal(val); - }); - - it('Test get and remove', () => { - const key = ID_NAME; - const val = 'test-get-remove'; - setStorageItem(key, val, 10); - expect(getStorageItem(key)).to.equal(val); - removeStorageItem(key); - expect(getStorageItem(key)).to.be.null; - }); - - it('Test expiry', () => { - const key = ID_NAME; - const val = 'test-expiry'; - setStorageItem(key, val, -1); - expect(localStorage.getItem(key)).to.equal(val); - expect(getStorageItem(key)).to.be.null; - expect(localStorage.getItem(key)).to.be.null; - }); - }); - - describe('event callback', () => { - beforeEach(() => { - setConfig(); - cleanUp(); - sinon.stub(events, 'getEvents').returns([]); - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(() => { - setConfig(); - cleanUp(); - events.getEvents.restore(); - utils.triggerPixel.restore(); - }); - it('auction end trigger', () => { - setConfig({ - pixelUrl: '/any/url' - }); - - let adUnits = getAdUnits(); - let innerAdUnits; - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - expect(utils.triggerPixel.called).to.be.false; - events.emit(constants.EVENTS.AUCTION_END, {}); - expect(utils.triggerPixel.called).to.be.true; - expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/url'); - }); - }); -}); diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index 1d1b0e65ec8..86c8794dc4c 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -5,6 +5,7 @@ import { config } from 'src/config.js'; import { VIDEO } from 'src/mediaTypes.js'; import { deepClone, parseQueryStringParameters } from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; +import * as utils from 'src/utils.js'; const { code, @@ -175,7 +176,7 @@ describe('pubGENIUS adapter', () => { method: 'POST', url: 'https://auction.adpearl.io/prebid/auction', data: { - id: 'fake-auction-id', + id: 'fakebidderrequestid', imp: [ { id: 'fakebidid', diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index 048244ee4fb..c56ed565c43 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -443,6 +443,13 @@ describe('pubmatic analytics adapter', function () { expect(data.eg).to.equal('1.23'); expect(data.en).to.equal('1.23'); expect(data.piid).to.equal('partnerImpressionID-1'); + expect(data.plt).to.equal('1'); + expect(data.psz).to.equal('640x480'); + expect(data.tgid).to.equal('15'); + expect(data.orig).to.equal('www.test.com'); + expect(data.ss).to.equal('1'); + expect(data.fskp).to.equal('0'); + expect(data.af).to.equal('video'); }); it('bidCpmAdjustment: USD: Logger: best case + win tracker', function() { diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index beb8a248d13..12805c7786d 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -79,7 +79,11 @@ describe('PubMatic adapter', function () { bidId: '23acc48ad47af5', requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', bidderRequestId: '1c56ad30b9b8ca8', - transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', + ortb2Imp: { + ext: { + tid: '92489f71-1bf2-49a0-adf9-000cea934729', + } + }, schain: schainConfig } ]; @@ -1125,7 +1129,12 @@ describe('PubMatic adapter', function () { it('Request params check', function () { let request = spec.buildRequests(bidRequests, { - auctionId: 'new-auction-id' + auctionId: 'new-auction-id', + ortb2: { + source: { + tid: 'source-tid' + } + } }); let data = JSON.parse(request.data); expect(data.at).to.equal(1); // auction type @@ -1135,13 +1144,13 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude - expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version - expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId - expect(data.source.tid).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId + expect(data.source.tid).to.equal('source-tid'); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID @@ -1388,12 +1397,12 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude - expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version - expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID @@ -1599,12 +1608,12 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude - expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Longitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Longitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version - expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal('new-auction-id'); // OpenWrap: Wrapper Impression ID expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID @@ -1635,12 +1644,12 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude - expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version - expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID @@ -1667,12 +1676,12 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude - expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude - expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version - expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID @@ -2697,10 +2706,10 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(multipleMediaRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(multipleMediaRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(multipleMediaRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude - expect(data.device.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude - expect(data.user.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude - expect(data.user.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(multipleMediaRequests[0].transactionId); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(multipleMediaRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID diff --git a/test/spec/modules/pubwiseBidAdapter_spec.js b/test/spec/modules/pubwiseBidAdapter_spec.js index 780cc8b8fdb..49e36c05d1e 100644 --- a/test/spec/modules/pubwiseBidAdapter_spec.js +++ b/test/spec/modules/pubwiseBidAdapter_spec.js @@ -101,6 +101,7 @@ const sampleValidBannerBidRequest = { }, ortb2Imp: { ext: { + tid: '2001a8b2-3bcf-417d-b64f-92641dae21e0', data: { adserver: { name: 'gam', @@ -125,7 +126,6 @@ const sampleValidBannerBidRequest = { } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '2001a8b2-3bcf-417d-b64f-92641dae21e0', 'sizes': [ [ 300, @@ -179,6 +179,7 @@ const sampleValidBidRequests = [ }, ortb2Imp: { ext: { + tid: '2c8cd034-f068-4419-8c30-f07292c0d17b', data: { adserver: { name: 'gam', @@ -213,7 +214,6 @@ const sampleValidBidRequests = [ } }, 'adUnitCode': 'div-gpt-ad-1460505748561-1', - 'transactionId': '2c8cd034-f068-4419-8c30-f07292c0d17b', 'sizes': [], 'bidId': '30ab7516a51a7c', 'bidderRequestId': '18a45bff5ff705', @@ -252,6 +252,7 @@ const sampleBidderBannerRequest = { }, ortb2Imp: { ext: { + tid: '2001a8b2-3bcf-417d-b64f-92641dae21e0', data: { adserver: { name: 'gam', @@ -272,7 +273,6 @@ const sampleBidderBannerRequest = { } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '2001a8b2-3bcf-417d-b64f-92641dae21e0', 'sizes': [ [ 300, @@ -299,7 +299,11 @@ const sampleBidderBannerRequest = { const sampleBidderRequest = { 'bidderCode': 'pubwise', - 'auctionId': '9f20663c-4629-4b5c-bff6-ff3aa8319358', + ortb2: { + source: { + tid: '9f20663c-4629-4b5c-bff6-ff3aa8319358', + } + }, 'bidderRequestId': '18a45bff5ff705', 'bids': [ sampleBidderBannerRequest, @@ -564,8 +568,8 @@ describe('PubWiseAdapter', function () { it('should handle complex bidRequest', function() { let request = spec.buildRequests(sampleValidBidRequests, sampleBidderRequest); expect(request.bidderRequest).to.equal(sampleBidderRequest, "Bid Request Doesn't Match Sample"); - expect(request.data.source.tid).to.equal(sampleBidderRequest.auctionId, 'AuctionId -> source.tid Mismatch'); - expect(request.data.imp[0].ext.tid).to.equal(sampleBidderRequest.bids[0].transactionId, 'TransactionId -> ext.tid Mismatch'); + expect(request.data.source.tid).to.equal(sampleBidderRequest.ortb2.source.tid, 'source.tid -> source.tid Mismatch'); + expect(request.data.imp[0].ext.tid).to.equal(sampleBidderRequest.bids[0].ortb2Imp.ext.tid, 'ext.tid -> ext.tid Mismatch'); }); it('must conform to API for buildRequests', function() { let request = spec.buildRequests(sampleValidBidRequests); diff --git a/test/spec/modules/realTimeDataModule_spec.js b/test/spec/modules/realTimeDataModule_spec.js index f9c41b2fda0..938e2e2f3c1 100644 --- a/test/spec/modules/realTimeDataModule_spec.js +++ b/test/spec/modules/realTimeDataModule_spec.js @@ -128,7 +128,7 @@ describe('Real time module', function () { it('should be able to modify bid request', function (done) { rtdModule.setBidRequestsData(() => { assert(getBidRequestDataSpy.calledTwice); - assert(getBidRequestDataSpy.calledWith({bidRequest: {}})); + assert(getBidRequestDataSpy.calledWith(sinon.match({bidRequest: {}}))); done(); }, {bidRequest: {}}) }); diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 0f2f9abd583..36d03c01138 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -39,7 +39,11 @@ describe('RelaidoAdapter', function () { bidId: '2ed93003f7bb99', bidderRequestId: '1c50443387a1f2', auctionId: '413ed000-8c7a-4ba1-a1fa-9732e006f8c3', - transactionId: '5c2d064c-7b76-42e8-a383-983603afdc45', + ortb2Imp: { + ext: { + tid: '5c2d064c-7b76-42e8-a383-983603afdc45', + } + }, bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0 @@ -243,7 +247,7 @@ describe('RelaidoAdapter', function () { expect(request.bidder_request_id).to.equal(bidRequest.bidderRequestId); expect(data.bid_requests_count).to.equal(bidRequest.bidRequestsCount); expect(request.bid_id).to.equal(bidRequest.bidId); - expect(request.transaction_id).to.equal(bidRequest.transactionId); + expect(request.transaction_id).to.equal(bidRequest.ortb2Imp.ext.tid); expect(request.media_type).to.equal('video'); expect(data.uuid).to.equal(relaido_uuid); expect(data.pv).to.equal('$prebid.version$'); diff --git a/test/spec/modules/revcontentBidAdapter_spec.js b/test/spec/modules/revcontentBidAdapter_spec.js index e6ef4adec87..ca4e7bc4e4b 100644 --- a/test/spec/modules/revcontentBidAdapter_spec.js +++ b/test/spec/modules/revcontentBidAdapter_spec.js @@ -266,8 +266,6 @@ describe('revcontent adapter', function () { it('should set correct native params', function () { const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); assert.equal(result.mediaType, 'native'); assert.equal(result.requestId, '294a7f446202848'); assert.equal(result.cpm, '0.1'); @@ -279,8 +277,6 @@ describe('revcontent adapter', function () { bidRequest.bid[0].params.size.height = 90; const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); assert.equal(result.mediaType, 'native'); assert.equal(result.requestId, '294a7f446202848'); assert.equal(result.cpm, '0.1'); @@ -292,8 +288,6 @@ describe('revcontent adapter', function () { bidRequest.bid[0].params.size.height = 600; const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); assert.equal(result.mediaType, 'native'); assert.equal(result.requestId, '294a7f446202848'); assert.equal(result.cpm, '0.1'); @@ -304,8 +298,6 @@ describe('revcontent adapter', function () { bidRequest.bid[0].params.template = '

{title}

SEE MORE
'; const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); assert.equal(result.mediaType, 'native'); assert.equal(result.requestId, '294a7f446202848'); assert.equal(result.cpm, '0.1'); @@ -317,8 +309,6 @@ describe('revcontent adapter', function () { bidRequest.bid[0].params.size.height = 200; const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); assert.equal(result.mediaType, 'native'); assert.equal(result.requestId, '294a7f446202848'); assert.equal(result.cpm, '0.1'); diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 86d3df8be59..ea45ff7e0b0 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -25,6 +25,41 @@ describe('Richaudience adapter tests', function () { auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', bidRequestsCount: 1, bidderRequestId: '1858b7382993ca', + ortb2Imp: { + ext: { + tid: '29df2112-348b-4961-8863-1b33684d95e6', + } + }, + user: {} + }]; + + var DEFAULT_PARAMS_NEW_SIZES_GPID = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + ortb2Imp: { + ext: { + gpid: '/19968336/header-bid-tag-1#example-2', + data: { + pbadslot: '/19968336/header-bid-tag-1#example-2' + } + } + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], [300, 600], [728, 90], [970, 250]] + } + }, + bidder: 'richaudience', + params: { + bidfloor: 0.5, + pid: 'ADb1f40rmi', + supplyType: 'site', + keywords: 'key1=value1;key2=value2' + }, + auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', transactionId: '29df2112-348b-4961-8863-1b33684d95e6', user: {} }]; @@ -236,6 +271,7 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('numIframes').and.to.equal(0); expect(typeof requestContent.scr_rsl === 'string') expect(typeof requestContent.cpuc === 'number') + expect(typeof requestContent.gpid === 'string') expect(requestContent).to.have.property('kws').and.to.equal('key1=value1;key2=value2'); }) @@ -831,6 +867,18 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('schain').to.deep.equal(schain); }) + it('should pass gpid', function() { + const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES_GPID, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: {} + }) + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('gpid').and.to.equal('/19968336/header-bid-tag-1#example-2'); + }) + describe('userSync', function () { it('Verifies user syncs iframe include', function () { config.setConfig({ diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js deleted file mode 100644 index 05b6cc6b6f4..00000000000 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ /dev/null @@ -1,2540 +0,0 @@ -import rubiconAnalyticsAdapter, { - SEND_TIMEOUT, - parseBidResponse, - getHostNameFromReferer, - storage, - rubiConf, - resetRubiConf -} from 'modules/rubiconAnalyticsAdapter.js'; -import CONSTANTS from 'src/constants.json'; -import { config } from 'src/config.js'; -import { server } from 'test/mocks/xhr.js'; -import * as mockGpt from '../integration/faker/googletag.js'; -import { - setConfig, - addBidResponseHook, -} from 'modules/currency.js'; - -let Ajv = require('ajv'); -let schema = require('./rubiconAnalyticsSchema.json'); -let ajv = new Ajv({ - allErrors: true -}); - -let validator = ajv.compile(schema); - -function validate(message) { - validator(message); - expect(validator.errors).to.deep.equal(null); -} - -let events = require('src/events.js'); -let utils = require('src/utils.js'); - -const { - EVENTS: { - AUCTION_INIT, - AUCTION_END, - BID_REQUESTED, - BID_RESPONSE, - BIDDER_DONE, - BID_WON, - BID_TIMEOUT, - SET_TARGETING, - BILLABLE_EVENT - } -} = CONSTANTS; - -const BID = { - 'bidder': 'rubicon', - 'width': 640, - 'height': 480, - 'mediaType': 'video', - 'statusMessage': 'Bid available', - 'bidId': '2ecff0db240757', - 'adId': 'fake_ad_id', - 'source': 'client', - 'requestId': '2ecff0db240757', - 'currency': 'USD', - 'creativeId': '3571560', - 'cpm': 1.22752, - 'ttl': 300, - 'netRevenue': false, - 'ad': '', - 'rubiconTargeting': { - 'rpfl_elemid': '/19968336/header-bid-tag-0', - 'rpfl_14062': '2_tier0100' - }, - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'responseTimestamp': 1519149629415, - 'requestTimestamp': 1519149628471, - 'adUnitCode': '/19968336/header-bid-tag-0', - 'timeToRespond': 944, - 'pbLg': '1.00', - 'pbMg': '1.20', - 'pbHg': '1.22', - 'pbAg': '1.20', - 'pbDg': '1.22', - 'pbCg': '', - 'size': '640x480', - 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '2ecff0db240757', - 'hb_pb': 1.20, - 'hb_size': '640x480', - 'hb_source': 'client' - }, - getStatusCode() { - return 1; - } -}; - -const BID2 = Object.assign({}, BID, { - adUnitCode: '/19968336/header-bid-tag1', - bidId: '3bd4ebb1c900e2', - adId: 'fake_ad_id', - requestId: '3bd4ebb1c900e2', - width: 728, - height: 90, - mediaType: 'banner', - cpm: 1.52, - source: 'server', - seatBidId: 'aaaa-bbbb-cccc-dddd', - rubiconTargeting: { - 'rpfl_elemid': '/19968336/header-bid-tag1', - 'rpfl_14062': '2_tier0100' - }, - adserverTargeting: { - 'hb_bidder': 'rubicon', - 'hb_adid': '3bd4ebb1c900e2', - 'hb_pb': '1.500', - 'hb_size': '728x90', - 'hb_source': 'server' - } -}); - -const BID3 = Object.assign({}, BID, { - adUnitCode: '/19968336/siderail-tag1', - bidId: '5fg6hyy4r879f0', - adId: 'fake_ad_id', - requestId: '5fg6hyy4r879f0', - width: 300, - height: 250, - mediaType: 'banner', - cpm: 2.01, - source: 'server', - seatBidId: 'aaaa-bbbb-cccc-dddd', - rubiconTargeting: { - 'rpfl_elemid': '/19968336/siderail-tag1', - 'rpfl_14062': '15_tier0200' - }, - adserverTargeting: { - 'hb_bidder': 'rubicon', - 'hb_adid': '5fg6hyy4r879f0', - 'hb_pb': '2.00', - 'hb_size': '300x250', - 'hb_source': 'server' - } -}); - -const BID4 = Object.assign({}, BID, { - adUnitCode: '/19968336/header-bid-tag1', - bidId: '3bd4ebb1c900e2', - adId: 'fake_ad_id', - requestId: '3bd4ebb1c900e2', - width: 728, - height: 90, - mediaType: 'banner', - cpm: 1.52, - source: 'server', - pbsBidId: 'zzzz-yyyy-xxxx-wwww', - seatBidId: 'aaaa-bbbb-cccc-dddd', - rubiconTargeting: { - 'rpfl_elemid': '/19968336/header-bid-tag1', - 'rpfl_14062': '2_tier0100' - }, - adserverTargeting: { - 'hb_bidder': 'rubicon', - 'hb_adid': '3bd4ebb1c900e2', - 'hb_pb': '1.500', - 'hb_size': '728x90', - 'hb_source': 'server' - } -}); - -const floorMinRequest = { - 'bidder': 'rubicon', - 'params': { - 'accountId': '14062', - 'siteId': '70608', - 'zoneId': '335918', - 'userId': '12346', - 'keywords': ['a', 'b', 'c'], - 'inventory': { 'rating': '4-star', 'prodtype': 'tech' }, - 'visitor': { 'ucat': 'new', 'lastsearch': 'iphone' }, - 'position': 'atf' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250]] - } - }, - 'adUnitCode': '/19968336/siderail-tag1', - 'transactionId': 'c435626g-9e3f-401a-bee1-d56aec29a1d4', - 'sizes': [[300, 250]], - 'bidId': '5fg6hyy4r879f0', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' -}; - -const MOCK = { - SET_TARGETING: { - [BID.adUnitCode]: BID.adserverTargeting, - [BID2.adUnitCode]: BID2.adserverTargeting, - [BID3.adUnitCode]: BID3.adserverTargeting - }, - AUCTION_INIT: { - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'timestamp': 1519767010567, - 'auctionStatus': 'inProgress', - 'adUnits': [{ - 'code': '/19968336/header-bid-tag1', - 'sizes': [[640, 480]], - 'bids': [{ - 'bidder': 'rubicon', - 'params': { - 'accountId': 1001, 'siteId': 113932, 'zoneId': 535512 - } - }], - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' - } - ], - 'adUnitCodes': ['/19968336/header-bid-tag1'], - 'bidderRequests': [{ - 'bidderCode': 'rubicon', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'bidderRequestId': '1be65d7958826a', - 'bids': [{ - 'bidder': 'rubicon', - 'params': { - 'accountId': 1001, 'siteId': 113932, 'zoneId': 535512 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'timeout': 3000, - 'refererInfo': { - 'page': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - } - ], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 3000, - 'config': { - 'accountId': 1001, 'endpoint': '//localhost:9999/event' - } - }, - BID_REQUESTED: { - 'bidderCode': 'rubicon', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'bidderRequestId': '1be65d7958826a', - 'bids': [ - { - 'bidder': 'rubicon', - 'params': { - 'accountId': '1001', - 'siteId': '70608', - 'zoneId': '335918', - 'userId': '12346', - 'keywords': ['a', 'b', 'c'], - 'inventory': 'test', - 'visitor': { 'ucat': 'new', 'lastsearch': 'iphone' }, - 'position': 'btf', - 'video': { - 'language': 'en', - 'playerHeight': 480, - 'playerWidth': 640, - 'size_id': 203, - 'skip': 1, - 'skipdelay': 15, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - } - }, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': [640, 480] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-0', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'src': 'client' - }, - { - 'bidder': 'rubicon', - 'params': { - 'accountId': '14062', - 'siteId': '70608', - 'zoneId': '335918', - 'userId': '12346', - 'keywords': ['a', 'b', 'c'], - 'inventory': { 'rating': '4-star', 'prodtype': 'tech' }, - 'visitor': { 'ucat': 'new', 'lastsearch': 'iphone' }, - 'position': 'atf' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[1000, 300], [970, 250], [728, 90]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag1', - 'transactionId': 'c116413c-9e3f-401a-bee1-d56aec29a1d4', - 'sizes': [[1000, 300], [970, 250], [728, 90]], - 'bidId': '3bd4ebb1c900e2', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'src': 's2s' - } - ], - 'auctionStart': 1519149536560, - 'timeout': 5000, - 'start': 1519149562216, - 'refererInfo': { - 'page': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - }, - BID_RESPONSE: [ - BID, - BID2, - BID3 - ], - AUCTION_END: { - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' - }, - BID_WON: [ - Object.assign({}, BID, { - 'status': 'rendered' - }), - Object.assign({}, BID2, { - 'status': 'rendered' - }), - Object.assign({}, BID3, { - 'status': 'rendered' - }) - ], - BIDDER_DONE: { - 'bidderCode': 'rubicon', - 'serverResponseTimeMs': 42, - 'bids': [ - BID, - Object.assign({}, BID2, { - 'serverResponseTimeMs': 42, - }), - Object.assign({}, BID3, { - 'serverResponseTimeMs': 55, - }) - ] - }, - BID_TIMEOUT: [ - { - 'bidId': '2ecff0db240757', - 'bidder': 'rubicon', - 'adUnitCode': '/19968336/header-bid-tag-0', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' - } - ] -}; - -const STUBBED_UUID = '12345678-1234-1234-1234-123456789abc'; - -const ANALYTICS_MESSAGE = { - 'channel': 'web', - 'integration': 'pbjs', - 'version': '$prebid.version$', - 'referrerUri': 'http://www.test.com/page.html', - 'session': { - 'expires': 1519788613781, - 'id': STUBBED_UUID, - 'start': 1519767013781 - }, - 'timestamps': { - 'auctionEnded': 1519767013781, - 'eventTime': 1519767013781, - 'prebidLoaded': rubiconAnalyticsAdapter.MODULE_INITIALIZED_TIME - }, - 'trigger': 'allBidWons', - 'referrerHostname': 'www.test.com', - 'auctions': [ - { - - 'auctionEnd': 1519767013781, - 'auctionStart': 1519767010567, - 'bidderOrder': ['rubicon'], - 'requestId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'clientTimeoutMillis': 3000, - 'serverTimeoutMillis': 1000, - 'accountId': 1001, - 'samplingFactor': 1, - 'adUnits': [ - { - 'adUnitCode': '/19968336/header-bid-tag-0', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'videoAdFormat': 'outstream', - 'mediaTypes': [ - 'video' - ], - 'dimensions': [ - { - 'width': 640, - 'height': 480 - } - ], - 'status': 'success', - 'accountId': 1001, - 'siteId': 70608, - 'zoneId': 335918, - 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '2ecff0db240757', - 'hb_pb': '1.200', - 'hb_size': '640x480', - 'hb_source': 'client' - }, - 'bids': [ - { - 'bidder': 'rubicon', - 'bidId': '2ecff0db240757', - 'status': 'success', - 'source': 'client', - 'clientLatencyMillis': 3214, - 'params': { - 'accountId': '1001', - 'siteId': '70608', - 'zoneId': '335918' - }, - 'bidResponse': { - 'bidPriceUSD': 1.22752, - 'dimensions': { - 'width': 640, - 'height': 480 - }, - 'mediaType': 'video' - } - } - ] - }, - { - 'adUnitCode': '/19968336/header-bid-tag1', - 'transactionId': 'c116413c-9e3f-401a-bee1-d56aec29a1d4', - 'mediaTypes': [ - 'banner' - ], - 'dimensions': [ - { - 'width': 1000, - 'height': 300 - }, - { - 'width': 970, - 'height': 250 - }, - { - 'width': 728, - 'height': 90 - } - ], - 'status': 'success', - 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '3bd4ebb1c900e2', - 'hb_pb': '1.500', - 'hb_size': '728x90', - 'hb_source': 'server' - }, - 'bids': [ - { - 'bidder': 'rubicon', - 'bidId': 'aaaa-bbbb-cccc-dddd', - 'status': 'success', - 'source': 'server', - 'clientLatencyMillis': 3214, - 'serverLatencyMillis': 42, - 'params': { - 'accountId': '14062', - 'siteId': '70608', - 'zoneId': '335918' - }, - 'bidResponse': { - 'bidPriceUSD': 1.52, - 'dimensions': { - 'width': 728, - 'height': 90 - }, - 'mediaType': 'banner' - } - } - ] - } - ] - } - ], - 'bidsWon': [ - { - 'bidder': 'rubicon', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'adUnitCode': '/19968336/header-bid-tag-0', - 'bidId': '2ecff0db240757', - 'status': 'success', - 'source': 'client', - 'clientLatencyMillis': 3214, - 'samplingFactor': 1, - 'accountId': 1001, - 'siteId': 70608, - 'zoneId': 335918, - 'params': { - 'accountId': '1001', - 'siteId': '70608', - 'zoneId': '335918' - }, - 'videoAdFormat': 'outstream', - 'mediaTypes': [ - 'video' - ], - 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '2ecff0db240757', - 'hb_pb': '1.200', - 'hb_size': '640x480', - 'hb_source': 'client' - }, - 'bidResponse': { - 'bidPriceUSD': 1.22752, - 'dimensions': { - 'width': 640, - 'height': 480 - }, - 'mediaType': 'video' - }, - 'bidwonStatus': 'success' - }, - { - 'bidder': 'rubicon', - 'transactionId': 'c116413c-9e3f-401a-bee1-d56aec29a1d4', - 'adUnitCode': '/19968336/header-bid-tag1', - 'bidId': 'aaaa-bbbb-cccc-dddd', - 'status': 'success', - 'source': 'server', - 'clientLatencyMillis': 3214, - 'serverLatencyMillis': 42, - 'samplingFactor': 1, - 'accountId': 1001, - 'params': { - 'accountId': '14062', - 'siteId': '70608', - 'zoneId': '335918' - }, - 'mediaTypes': [ - 'banner' - ], - 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '3bd4ebb1c900e2', - 'hb_pb': '1.500', - 'hb_size': '728x90', - 'hb_source': 'server' - }, - 'bidResponse': { - 'bidPriceUSD': 1.52, - 'dimensions': { - 'width': 728, - 'height': 90 - }, - 'mediaType': 'banner' - }, - 'bidwonStatus': 'success' - } - ], - 'wrapper': { - 'name': '10000_fakewrapper_test' - } -}; - -function performStandardAuction(gptEvents, auctionId = MOCK.AUCTION_INIT.auctionId) { - events.emit(AUCTION_INIT, { ...MOCK.AUCTION_INIT, auctionId }); - events.emit(BID_REQUESTED, { ...MOCK.BID_REQUESTED, auctionId }); - events.emit(BID_RESPONSE, { ...MOCK.BID_RESPONSE[0], auctionId }); - events.emit(BID_RESPONSE, { ...MOCK.BID_RESPONSE[1], auctionId }); - events.emit(BIDDER_DONE, { ...MOCK.BIDDER_DONE, auctionId }); - events.emit(AUCTION_END, { ...MOCK.AUCTION_END, auctionId }); - - if (gptEvents && gptEvents.length) { - gptEvents.forEach(gptEvent => mockGpt.emitEvent(gptEvent.eventName, gptEvent.params)); - } - - events.emit(SET_TARGETING, { ...MOCK.SET_TARGETING, auctionId }); - events.emit(BID_WON, { ...MOCK.BID_WON[0], auctionId }); - events.emit(BID_WON, { ...MOCK.BID_WON[1], auctionId }); -} - -describe('rubicon analytics adapter', function () { - let sandbox; - let clock; - let getDataFromLocalStorageStub, setDataInLocalStorageStub, localStorageIsEnabledStub; - beforeEach(function () { - getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); - setDataInLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage'); - localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); - mockGpt.disable(); - sandbox = sinon.sandbox.create(); - - localStorageIsEnabledStub.returns(true); - - sandbox.stub(events, 'getEvents').returns([]); - - sandbox.stub(utils, 'generateUUID').returns(STUBBED_UUID); - - clock = sandbox.useFakeTimers(1519767013781); - - rubiconAnalyticsAdapter.referrerHostname = ''; - - config.setConfig({ - s2sConfig: { - timeout: 1000, - accountId: 10000, - }, - rubicon: { - wrapperName: '10000_fakewrapper_test' - } - }) - }); - - afterEach(function () { - sandbox.restore(); - config.resetConfig(); - mockGpt.enable(); - getDataFromLocalStorageStub.restore(); - setDataInLocalStorageStub.restore(); - localStorageIsEnabledStub.restore(); - }); - - it('should require accountId', function () { - sandbox.stub(utils, 'logError'); - - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event' - } - }); - - expect(utils.logError.called).to.equal(true); - }); - - it('should require endpoint', function () { - sandbox.stub(utils, 'logError'); - - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - accountId: 1001 - } - }); - - expect(utils.logError.called).to.equal(true); - }); - - describe('config subscribe', function () { - it('should update the pvid if user asks', function () { - expect(utils.generateUUID.called).to.equal(false); - config.setConfig({ rubicon: { updatePageView: true } }); - expect(utils.generateUUID.called).to.equal(true); - }); - it('should merge in and preserve older set configs', function () { - resetRubiConf(); - config.setConfig({ - rubicon: { - wrapperName: '1001_general', - int_type: 'dmpbjs', - fpkvs: { - source: 'fb' - } - } - }); - expect(rubiConf).to.deep.equal({ - analyticsEventDelay: 0, - dmBilling: { - enabled: false, - vendors: [], - waitForAuction: true - }, - pvid: '12345678', - wrapperName: '1001_general', - int_type: 'dmpbjs', - fpkvs: { - source: 'fb' - }, - }); - - // update it with stuff - config.setConfig({ - rubicon: { - fpkvs: { - link: 'email' - } - } - }); - expect(rubiConf).to.deep.equal({ - analyticsEventDelay: 0, - dmBilling: { - enabled: false, - vendors: [], - waitForAuction: true - }, - pvid: '12345678', - wrapperName: '1001_general', - int_type: 'dmpbjs', - fpkvs: { - source: 'fb', - link: 'email' - }, - }); - - // overwriting specific edge keys should update them - config.setConfig({ - rubicon: { - fpkvs: { - link: 'iMessage', - source: 'twitter' - } - } - }); - expect(rubiConf).to.deep.equal({ - analyticsEventDelay: 0, - dmBilling: { - enabled: false, - vendors: [], - waitForAuction: true - }, - pvid: '12345678', - wrapperName: '1001_general', - int_type: 'dmpbjs', - fpkvs: { - link: 'iMessage', - source: 'twitter' - }, - }); - }); - }); - - describe('sampling', function () { - beforeEach(function () { - sandbox.stub(Math, 'random').returns(0.08); - sandbox.stub(utils, 'logError'); - }); - - afterEach(function () { - rubiconAnalyticsAdapter.disableAnalytics(); - }); - - describe('with options.samplingFactor', function () { - it('should sample', function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001, - samplingFactor: 10 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(1); - }); - - it('should unsample', function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001, - samplingFactor: 20 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(0); - }); - - it('should throw errors for invalid samplingFactor', function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001, - samplingFactor: 30 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(0); - expect(utils.logError.called).to.equal(true); - }); - }); - describe('with options.sampling', function () { - it('should sample', function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001, - sampling: 0.1 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(1); - }); - - it('should unsample', function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001, - sampling: 0.05 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(0); - }); - - it('should throw errors for invalid samplingFactor', function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001, - sampling: 1 / 30 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(0); - expect(utils.logError.called).to.equal(true); - }); - }); - }); - - describe('when handling events', function () { - beforeEach(function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001 - } - }); - }); - - afterEach(function () { - rubiconAnalyticsAdapter.disableAnalytics(); - }); - - it('should build a batched message from prebid events', function () { - performStandardAuction(); - - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - - expect(request.url).to.equal('//localhost:9999/event'); - - let message = JSON.parse(request.requestBody); - validate(message); - - expect(message).to.deep.equal(ANALYTICS_MESSAGE); - }); - - it('should pass along bidderOrder correctly', function () { - const appnexusBid = utils.deepClone(MOCK.BID_REQUESTED); - appnexusBid.bidderCode = 'appnexus'; - const pubmaticBid = utils.deepClone(MOCK.BID_REQUESTED); - pubmaticBid.bidderCode = 'pubmatic'; - const indexBid = utils.deepClone(MOCK.BID_REQUESTED); - indexBid.bidderCode = 'ix'; - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, pubmaticBid); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_REQUESTED, indexBid); - events.emit(BID_REQUESTED, appnexusBid); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - clock.tick(SEND_TIMEOUT + 1000); - - let message = JSON.parse(server.requests[0].requestBody); - expect(message.auctions[0].bidderOrder).to.deep.equal([ - 'pubmatic', - 'rubicon', - 'ix', - 'appnexus' - ]); - }); - - it('should pass along user ids', function () { - let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); - auctionInit.bidderRequests[0].bids[0].userId = { - criteoId: 'sadfe4334', - lotamePanoramaId: 'asdf3gf4eg', - pubcid: 'dsfa4545-svgdfs5', - sharedId: { id1: 'asdf', id2: 'sadf4344' } - }; - - events.emit(AUCTION_INIT, auctionInit); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - - let message = JSON.parse(request.requestBody); - validate(message); - - expect(message.auctions[0].user).to.deep.equal({ - ids: [ - { provider: 'criteoId', 'hasId': true }, - { provider: 'lotamePanoramaId', 'hasId': true }, - { provider: 'pubcid', 'hasId': true }, - { provider: 'sharedId', 'hasId': true }, - ] - }); - }); - - it('should handle bidResponse dimensions correctly', function () { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - - // mock bid response with playerWidth and playerHeight (NO width and height) - let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); - delete bidResponse1.width; - delete bidResponse1.height; - bidResponse1.playerWidth = 640; - bidResponse1.playerHeight = 480; - - // mock bid response with no width height or playerwidth playerheight - let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); - delete bidResponse2.width; - delete bidResponse2.height; - delete bidResponse2.playerWidth; - delete bidResponse2.playerHeight; - - events.emit(BID_RESPONSE, bidResponse1); - events.emit(BID_RESPONSE, bidResponse2); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.dimensions).to.deep.equal({ - width: 640, - height: 480 - }); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.dimensions).to.equal(undefined); - }); - - it('should pass along adomians correctly', function () { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - - // 1 adomains - let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); - bidResponse1.meta = { - advertiserDomains: ['magnite.com'] - } - - // two adomains - let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); - bidResponse2.meta = { - advertiserDomains: ['prebid.org', 'magnite.com'] - } - - // make sure we only pass max 10 adomains - bidResponse2.meta.advertiserDomains = [...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains, ...bidResponse2.meta.advertiserDomains] - - events.emit(BID_RESPONSE, bidResponse1); - events.emit(BID_RESPONSE, bidResponse2); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.adomains).to.deep.equal(['magnite.com']); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.adomains).to.deep.equal(['prebid.org', 'magnite.com', 'prebid.org', 'magnite.com', 'prebid.org', 'magnite.com', 'prebid.org', 'magnite.com', 'prebid.org', 'magnite.com']); - }); - - it('should NOT pass along adomians correctly when edge cases', function () { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - - // empty => nothing - let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); - bidResponse1.meta = { - advertiserDomains: [] - } - - // not array => nothing - let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); - bidResponse2.meta = { - advertiserDomains: 'prebid.org' - } - - events.emit(BID_RESPONSE, bidResponse1); - events.emit(BID_RESPONSE, bidResponse2); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.adomains).to.be.undefined; - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.adomains).to.be.undefined; - }); - - it('should NOT pass along adomians with other edge cases', function () { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - - // should filter out non string values and pass valid ones - let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); - bidResponse1.meta = { - advertiserDomains: [123, 'prebid.org', false, true, [], 'magnite.com', {}] - } - - // array of arrays (as seen when passed by kargo bid adapter) - let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); - bidResponse2.meta = { - advertiserDomains: [['prebid.org']] - } - - events.emit(BID_RESPONSE, bidResponse1); - events.emit(BID_RESPONSE, bidResponse2); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.adomains).to.deep.equal(['prebid.org', 'magnite.com']); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.adomains).to.be.undefined; - }); - - it('should not pass empty adServerTargeting values', function () { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - - const mockTargeting = utils.deepClone(MOCK.SET_TARGETING); - mockTargeting['/19968336/header-bid-tag-0'].hb_test = ''; - mockTargeting['/19968336/header-bid-tag1'].hb_test = 'NOT_EMPTY'; - events.emit(SET_TARGETING, mockTargeting); - - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].adserverTargeting.hb_test).to.be.undefined; - expect(message.auctions[0].adUnits[1].adserverTargeting.hb_test).to.equal('NOT_EMPTY'); - }); - - function performFloorAuction(provider) { - let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); - auctionInit.bidderRequests[0].bids[0].floorData = { - skipped: false, - modelVersion: 'someModelName', - modelWeight: 10, - modelTimestamp: 1606772895, - location: 'setConfig', - skipRate: 15, - fetchStatus: 'error', - floorProvider: provider - }; - let flooredResponse = { - ...BID, - floorData: { - floorValue: 4, - floorRule: '12345/sports|video', - floorCurrency: 'USD', - cpmAfterAdjustments: 2.1, - enforcements: { - enforceJS: true, - enforcePBS: false, - floorDeals: false, - bidAdjustment: true - }, - matchedFields: { - gptSlot: '12345/sports', - mediaType: 'video' - } - }, - status: 'bidRejected', - cpm: 0, - getStatusCode() { - return 2; - } - }; - - let notFlooredResponse = { - ...BID2, - floorData: { - floorValue: 1, - floorRule: '12345/news|banner', - floorCurrency: 'USD', - cpmAfterAdjustments: 1.55, - enforcements: { - enforceJS: true, - enforcePBS: false, - floorDeals: false, - bidAdjustment: true - }, - matchedFields: { - gptSlot: '12345/news', - mediaType: 'banner' - } - } - }; - - let floorMinResponse = { - ...BID3, - floorData: { - floorValue: 1.5, - floorRuleValue: 1, - floorRule: '12345/entertainment|banner', - floorCurrency: 'USD', - cpmAfterAdjustments: 2.00, - enforcements: { - enforceJS: true, - enforcePBS: false, - floorDeals: false, - bidAdjustment: true - }, - matchedFields: { - gptSlot: '12345/entertainment', - mediaType: 'banner' - } - } - }; - - let bidRequest = utils.deepClone(MOCK.BID_REQUESTED); - bidRequest.bids.push(floorMinRequest) - - // spoof the auction with just our duplicates - events.emit(AUCTION_INIT, auctionInit); - events.emit(BID_REQUESTED, bidRequest); - events.emit(BID_RESPONSE, flooredResponse); - events.emit(BID_RESPONSE, notFlooredResponse); - events.emit(BID_RESPONSE, floorMinResponse); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[1]); - events.emit(BID_WON, MOCK.BID_WON[2]); - clock.tick(SEND_TIMEOUT + 1000); - - expect(server.requests.length).to.equal(1); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - return message; - } - - it('should capture price floor information correctly', function () { - let message = performFloorAuction('rubicon'); - - // verify our floor stuff is passed - // top level floor info - expect(message.auctions[0].floors).to.deep.equal({ - location: 'setConfig', - modelName: 'someModelName', - modelWeight: 10, - modelTimestamp: 1606772895, - skipped: false, - enforcement: true, - dealsEnforced: false, - skipRate: 15, - fetchStatus: 'error', - provider: 'rubicon' - }); - // first adUnit's adSlot - expect(message.auctions[0].adUnits[0].gam.adSlot).to.equal('12345/sports'); - // since no other bids, we set adUnit status to no-bid - expect(message.auctions[0].adUnits[0].status).to.equal('no-bid'); - // first adUnits bid is rejected - expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected-ipf'); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.equal(4); - // if bid rejected should take cpmAfterAdjustments val - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(2.1); - - // second adUnit's adSlot - expect(message.auctions[0].adUnits[1].gam.adSlot).to.equal('12345/news'); - // top level adUnit status is success - expect(message.auctions[0].adUnits[1].status).to.equal('success'); - // second adUnits bid is success - expect(message.auctions[0].adUnits[1].bids[0].status).to.equal('success'); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.equal(1); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); - - // second adUnit's adSlot - expect(message.auctions[0].adUnits[2].gam.adSlot).to.equal('12345/entertainment'); - // top level adUnit status is success - expect(message.auctions[0].adUnits[2].status).to.equal('success'); - // second adUnits bid is success - expect(message.auctions[0].adUnits[2].bids[0].status).to.equal('success'); - expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorValue).to.equal(1.5); - expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorRuleValue).to.equal(1); - expect(message.auctions[0].adUnits[2].bids[0].bidResponse.bidPriceUSD).to.equal(2.01); - }); - - it('should still send floor info if provider is not rubicon', function () { - let message = performFloorAuction('randomProvider'); - - // verify our floor stuff is passed - // top level floor info - expect(message.auctions[0].floors).to.deep.equal({ - location: 'setConfig', - modelName: 'someModelName', - modelWeight: 10, - modelTimestamp: 1606772895, - skipped: false, - enforcement: true, - dealsEnforced: false, - skipRate: 15, - fetchStatus: 'error', - provider: 'randomProvider' - }); - // first adUnit's adSlot - expect(message.auctions[0].adUnits[0].gam.adSlot).to.equal('12345/sports'); - // since no other bids, we set adUnit status to no-bid - expect(message.auctions[0].adUnits[0].status).to.equal('no-bid'); - // first adUnits bid is rejected - expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected-ipf'); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.equal(4); - // if bid rejected should take cpmAfterAdjustments val - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(2.1); - - // second adUnit's adSlot - expect(message.auctions[0].adUnits[1].gam.adSlot).to.equal('12345/news'); - // top level adUnit status is success - expect(message.auctions[0].adUnits[1].status).to.equal('success'); - // second adUnits bid is success - expect(message.auctions[0].adUnits[1].bids[0].status).to.equal('success'); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.equal(1); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); - - // second adUnit's adSlot - expect(message.auctions[0].adUnits[2].gam.adSlot).to.equal('12345/entertainment'); - // top level adUnit status is success - expect(message.auctions[0].adUnits[2].status).to.equal('success'); - // second adUnits bid is success - expect(message.auctions[0].adUnits[2].bids[0].status).to.equal('success'); - expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorValue).to.equal(1.5); - expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorRuleValue).to.equal(1); - expect(message.auctions[0].adUnits[2].bids[0].bidResponse.bidPriceUSD).to.equal(2.01); - }); - - describe('with session handling', function () { - const expectedPvid = STUBBED_UUID.slice(0, 8); - beforeEach(function () { - config.setConfig({ rubicon: { updatePageView: true } }); - }); - - it('should not log any session data if local storage is not enabled', function () { - localStorageIsEnabledStub.returns(false); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - delete expectedMessage.session; - delete expectedMessage.fpkvs; - - performStandardAuction(); - - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - - expect(request.url).to.equal('//localhost:9999/event'); - - let message = JSON.parse(request.requestBody); - validate(message); - - expect(message).to.deep.equal(expectedMessage); - }); - - it('should should pass along custom rubicon kv and pvid when defined', function () { - config.setConfig({ - rubicon: { - fpkvs: { - source: 'fb', - link: 'email' - } - } - }); - performStandardAuction(); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8); - expectedMessage.fpkvs = [ - { key: 'source', value: 'fb' }, - { key: 'link', value: 'email' } - ] - expect(message).to.deep.equal(expectedMessage); - }); - - it('should convert kvs to strings before sending', function () { - config.setConfig({ - rubicon: { - fpkvs: { - number: 24, - boolean: false, - string: 'hello', - array: ['one', 2, 'three'], - object: { one: 'two' } - } - } - }); - performStandardAuction(); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8); - expectedMessage.fpkvs = [ - { key: 'number', value: '24' }, - { key: 'boolean', value: 'false' }, - { key: 'string', value: 'hello' }, - { key: 'array', value: 'one,2,three' }, - { key: 'object', value: '[object Object]' } - ] - expect(message).to.deep.equal(expectedMessage); - }); - - it('should use the query utm param rubicon kv value and pass updated kv and pvid when defined', function () { - sandbox.stub(utils, 'getWindowLocation').returns({ 'search': '?utm_source=other', 'pbjs_debug': 'true' }); - - config.setConfig({ - rubicon: { - fpkvs: { - source: 'fb', - link: 'email' - } - } - }); - performStandardAuction(); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8); - expectedMessage.fpkvs = [ - { key: 'source', value: 'other' }, - { key: 'link', value: 'email' } - ] - - message.fpkvs.sort((left, right) => left.key < right.key); - expectedMessage.fpkvs.sort((left, right) => left.key < right.key); - - expect(message).to.deep.equal(expectedMessage); - }); - - it('should pick up existing localStorage and use its values', function () { - // set some localStorage - let inputlocalStorage = { - id: '987654', - start: 1519766113781, // 15 mins before "now" - expires: 1519787713781, // six hours later - lastSeen: 1519766113781, - fpkvs: { source: 'tw' } - }; - getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); - - config.setConfig({ - rubicon: { - fpkvs: { - link: 'email' // should merge this with what is in the localStorage! - } - } - }); - performStandardAuction(); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.session = { - id: '987654', - start: 1519766113781, - expires: 1519787713781, - pvid: expectedPvid - } - expectedMessage.fpkvs = [ - { key: 'source', value: 'tw' }, - { key: 'link', value: 'email' } - ] - expect(message).to.deep.equal(expectedMessage); - - let calledWith; - try { - calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); - } catch (e) { - calledWith = {}; - } - - expect(calledWith).to.deep.equal({ - id: '987654', // should have stayed same - start: 1519766113781, // should have stayed same - expires: 1519787713781, // should have stayed same - lastSeen: 1519767013781, // lastSeen updated to our "now" - fpkvs: { source: 'tw', link: 'email' }, // link merged in - pvid: expectedPvid // new pvid stored - }); - }); - - it('should overwrite matching localstorge value and use its remaining values', function () { - sandbox.stub(utils, 'getWindowLocation').returns({ 'search': '?utm_source=fb&utm_click=dog' }); - - // set some localStorage - let inputlocalStorage = { - id: '987654', - start: 1519766113781, // 15 mins before "now" - expires: 1519787713781, // six hours later - lastSeen: 1519766113781, - fpkvs: { source: 'tw', link: 'email' } - }; - getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); - - config.setConfig({ - rubicon: { - fpkvs: { - link: 'email' // should merge this with what is in the localStorage! - } - } - }); - performStandardAuction(); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.session = { - id: '987654', - start: 1519766113781, - expires: 1519787713781, - pvid: expectedPvid - } - expectedMessage.fpkvs = [ - { key: 'source', value: 'fb' }, - { key: 'link', value: 'email' }, - { key: 'click', value: 'dog' } - ] - - message.fpkvs.sort((left, right) => left.key < right.key); - expectedMessage.fpkvs.sort((left, right) => left.key < right.key); - - expect(message).to.deep.equal(expectedMessage); - - let calledWith; - try { - calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); - } catch (e) { - calledWith = {}; - } - - expect(calledWith).to.deep.equal({ - id: '987654', // should have stayed same - start: 1519766113781, // should have stayed same - expires: 1519787713781, // should have stayed same - lastSeen: 1519767013781, // lastSeen updated to our "now" - fpkvs: { source: 'fb', link: 'email', click: 'dog' }, // link merged in - pvid: expectedPvid // new pvid stored - }); - }); - - it('should throw out session if lastSeen > 30 mins ago and create new one', function () { - // set some localStorage - let inputlocalStorage = { - id: '987654', - start: 1519764313781, // 45 mins before "now" - expires: 1519785913781, // six hours later - lastSeen: 1519764313781, // 45 mins before "now" - fpkvs: { source: 'tw' } - }; - getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); - - config.setConfig({ - rubicon: { - fpkvs: { - link: 'email' // should merge this with what is in the localStorage! - } - } - }); - - performStandardAuction(); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid - expectedMessage.session.pvid = expectedPvid; - - // the saved fpkvs should have been thrown out since session expired - expectedMessage.fpkvs = [ - { key: 'link', value: 'email' } - ] - expect(message).to.deep.equal(expectedMessage); - - let calledWith; - try { - calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); - } catch (e) { - calledWith = {}; - } - - expect(calledWith).to.deep.equal({ - id: STUBBED_UUID, // should have stayed same - start: 1519767013781, // should have stayed same - expires: 1519788613781, // should have stayed same - lastSeen: 1519767013781, // lastSeen updated to our "now" - fpkvs: { link: 'email' }, // link merged in - pvid: expectedPvid // new pvid stored - }); - }); - - it('should throw out session if past expires time and create new one', function () { - // set some localStorage - let inputlocalStorage = { - id: '987654', - start: 1519745353781, // 6 hours before "expires" - expires: 1519766953781, // little more than six hours ago - lastSeen: 1519767008781, // 5 seconds ago - fpkvs: { source: 'tw' } - }; - getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); - - config.setConfig({ - rubicon: { - fpkvs: { - link: 'email' // should merge this with what is in the localStorage! - } - } - }); - - performStandardAuction(); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid - expectedMessage.session.pvid = expectedPvid; - - // the saved fpkvs should have been thrown out since session expired - expectedMessage.fpkvs = [ - { key: 'link', value: 'email' } - ] - expect(message).to.deep.equal(expectedMessage); - - let calledWith; - try { - calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); - } catch (e) { - calledWith = {}; - } - - expect(calledWith).to.deep.equal({ - id: STUBBED_UUID, // should have stayed same - start: 1519767013781, // should have stayed same - expires: 1519788613781, // should have stayed same - lastSeen: 1519767013781, // lastSeen updated to our "now" - fpkvs: { link: 'email' }, // link merged in - pvid: expectedPvid // new pvid stored - }); - }); - }); - describe('with googletag enabled', function () { - let gptSlot0, gptSlot1; - let gptSlotRenderEnded0, gptSlotRenderEnded1; - beforeEach(function () { - mockGpt.enable(); - gptSlot0 = mockGpt.makeSlot({ code: '/19968336/header-bid-tag-0' }); - gptSlotRenderEnded0 = { - eventName: 'slotRenderEnded', - params: { - slot: gptSlot0, - isEmpty: false, - advertiserId: 1111, - sourceAgnosticCreativeId: 2222, - sourceAgnosticLineItemId: 3333 - } - }; - - gptSlot1 = mockGpt.makeSlot({ code: '/19968336/header-bid-tag1' }); - gptSlotRenderEnded1 = { - eventName: 'slotRenderEnded', - params: { - slot: gptSlot1, - isEmpty: false, - advertiserId: 4444, - sourceAgnosticCreativeId: 5555, - sourceAgnosticLineItemId: 6666 - } - }; - }); - - afterEach(function () { - mockGpt.disable(); - }); - - it('should add necessary gam information if gpt is enabled and slotRender event emmited', function () { - performStandardAuction([gptSlotRenderEnded0, gptSlotRenderEnded1]); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.auctions[0].adUnits[0].gam = { - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }; - expectedMessage.auctions[0].adUnits[1].gam = { - advertiserId: 4444, - creativeId: 5555, - lineItemId: 6666, - adSlot: '/19968336/header-bid-tag1' - }; - expect(message).to.deep.equal(expectedMessage); - }); - - it('should handle empty gam renders', function () { - performStandardAuction([gptSlotRenderEnded0, { - eventName: 'slotRenderEnded', - params: { - slot: gptSlot1, - isEmpty: true - } - }]); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.auctions[0].adUnits[0].gam = { - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }; - expectedMessage.auctions[0].adUnits[1].gam = { - isSlotEmpty: true, - adSlot: '/19968336/header-bid-tag1' - }; - expect(message).to.deep.equal(expectedMessage); - }); - - it('should still add gam ids if falsy', function () { - performStandardAuction([gptSlotRenderEnded0, { - eventName: 'slotRenderEnded', - params: { - slot: gptSlot1, - isEmpty: false, - advertiserId: 0, - sourceAgnosticCreativeId: 0, - sourceAgnosticLineItemId: 0 - } - }]); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.auctions[0].adUnits[0].gam = { - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }; - expectedMessage.auctions[0].adUnits[1].gam = { - advertiserId: 0, - creativeId: 0, - lineItemId: 0, - adSlot: '/19968336/header-bid-tag1' - }; - expect(message).to.deep.equal(expectedMessage); - }); - - it('should pick backup Ids if no sourceAgnostic available first', function () { - performStandardAuction([gptSlotRenderEnded0, { - eventName: 'slotRenderEnded', - params: { - slot: gptSlot1, - isEmpty: false, - advertiserId: 0, - lineItemId: 1234, - creativeId: 5678 - } - }]); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.auctions[0].adUnits[0].gam = { - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }; - expectedMessage.auctions[0].adUnits[1].gam = { - advertiserId: 0, - creativeId: 5678, - lineItemId: 1234, - adSlot: '/19968336/header-bid-tag1' - }; - expect(message).to.deep.equal(expectedMessage); - }); - - it('should correctly set adUnit for associated slots', function () { - performStandardAuction([gptSlotRenderEnded0, gptSlotRenderEnded1]); - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.auctions[0].adUnits[0].gam = { - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }; - expectedMessage.auctions[0].adUnits[1].gam = { - advertiserId: 4444, - creativeId: 5555, - lineItemId: 6666, - adSlot: '/19968336/header-bid-tag1' - }; - expect(message).to.deep.equal(expectedMessage); - }); - - it('should only mark the first gam data not all matches', function () { - config.setConfig({ - rubicon: { - waitForGamSlots: true - } - }); - performStandardAuction(); - performStandardAuction([gptSlotRenderEnded0, gptSlotRenderEnded1], '32d332de-123a-32dg-2345-cefef3423324'); - - // tick the clock and both should fire - clock.tick(3000); - - expect(server.requests.length).to.equal(2); - - // first one should have GAM data - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - - // trigger should be gam since all adunits had associated gam render - expect(message.trigger).to.be.equal('gam'); - expect(message.auctions[0].adUnits[0].gam).to.deep.equal({ - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }); - expect(message.auctions[0].adUnits[1].gam).to.deep.equal({ - advertiserId: 4444, - creativeId: 5555, - lineItemId: 6666, - adSlot: '/19968336/header-bid-tag1' - }); - - // second one should NOT have gam data - request = server.requests[1]; - message = JSON.parse(request.requestBody); - validate(message); - - // trigger should be auctionEnd - expect(message.trigger).to.be.equal('auctionEnd'); - expect(message.auctions[0].adUnits[0].gam).to.be.undefined; - expect(message.auctions[0].adUnits[1].gam).to.be.undefined; - }); - - it('should send request when waitForGamSlots is present but no bidWons are sent', function () { - config.setConfig({ - rubicon: { - waitForGamSlots: true, - } - }); - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - - // should send if just slotRenderEnded is emmitted for both - mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params); - mockGpt.emitEvent(gptSlotRenderEnded1.eventName, gptSlotRenderEnded1.params); - - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - - let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - delete expectedMessage.bidsWon; // should not be any of these - expectedMessage.auctions[0].adUnits[0].gam = { - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }; - expectedMessage.auctions[0].adUnits[1].gam = { - advertiserId: 4444, - creativeId: 5555, - lineItemId: 6666, - adSlot: '/19968336/header-bid-tag1' - }; - expectedMessage.trigger = 'gam'; - expect(message).to.deep.equal(expectedMessage); - }); - - it('should delay the event call depending on analyticsEventDelay config', function () { - config.setConfig({ - rubicon: { - waitForGamSlots: true, - analyticsEventDelay: 2000 - } - }); - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - - // should send if just slotRenderEnded is emmitted for both - mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params); - mockGpt.emitEvent(gptSlotRenderEnded1.eventName, gptSlotRenderEnded1.params); - - // Should not be sent until delay - expect(server.requests.length).to.equal(0); - - // tick the clock and it should fire - clock.tick(2000); - - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - let message = JSON.parse(request.requestBody); - validate(message); - let expectedGam0 = { - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333, - adSlot: '/19968336/header-bid-tag-0' - }; - let expectedGam1 = { - advertiserId: 4444, - creativeId: 5555, - lineItemId: 6666, - adSlot: '/19968336/header-bid-tag1' - }; - expect(expectedGam0).to.deep.equal(message.auctions[0].adUnits[0].gam); - expect(expectedGam1).to.deep.equal(message.auctions[0].adUnits[1].gam); - }); - }); - - it('should correctly overwrite bidId if seatBidId is on the bidResponse', function () { - // Only want one bid request in our mock auction - let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); - bidRequested.bids.shift(); - let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); - auctionInit.adUnits.shift(); - - // clone the mock bidResponse and duplicate - let seatBidResponse = utils.deepClone(BID2); - seatBidResponse.seatBidId = 'abc-123-do-re-me'; - - const setTargeting = { - [seatBidResponse.adUnitCode]: seatBidResponse.adserverTargeting - }; - - const bidWon = Object.assign({}, seatBidResponse, { - 'status': 'rendered' - }); - - // spoof the auction with just our duplicates - events.emit(AUCTION_INIT, auctionInit); - events.emit(BID_REQUESTED, bidRequested); - events.emit(BID_RESPONSE, seatBidResponse); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, setTargeting); - events.emit(BID_WON, bidWon); - - let message = JSON.parse(server.requests[0].requestBody); - - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidId).to.equal('abc-123-do-re-me'); - expect(message.bidsWon[0].bidId).to.equal('abc-123-do-re-me'); - }); - - it('should correctly overwrite bidId if pbsBidId is on the bidResponse', function () { - // Only want one bid request in our mock auction - let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); - bidRequested.bids.shift(); - let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); - auctionInit.adUnits.shift(); - - // clone the mock bidResponse and duplicate - let seatBidResponse = utils.deepClone(BID4); - - const setTargeting = { - [seatBidResponse.adUnitCode]: seatBidResponse.adserverTargeting - }; - - const bidWon = Object.assign({}, seatBidResponse, { - 'status': 'rendered' - }); - - // spoof the auction with just our duplicates - events.emit(AUCTION_INIT, auctionInit); - events.emit(BID_REQUESTED, bidRequested); - events.emit(BID_RESPONSE, seatBidResponse); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, setTargeting); - events.emit(BID_WON, bidWon); - - let message = JSON.parse(server.requests[0].requestBody); - - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidId).to.equal('zzzz-yyyy-xxxx-wwww'); - expect(message.bidsWon[0].bidId).to.equal('zzzz-yyyy-xxxx-wwww'); - }); - - it('should correctly generate new bidId if it is 0', function () { - // Only want one bid request in our mock auction - let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); - bidRequested.bids.shift(); - let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); - auctionInit.adUnits.shift(); - - // clone the mock bidResponse and duplicate - let seatBidResponse = utils.deepClone(BID4); - seatBidResponse.pbsBidId = '0'; - - const setTargeting = { - [seatBidResponse.adUnitCode]: seatBidResponse.adserverTargeting - }; - - const bidWon = Object.assign({}, seatBidResponse, { - 'status': 'rendered' - }); - - // spoof the auction with just our duplicates - events.emit(AUCTION_INIT, auctionInit); - events.emit(BID_REQUESTED, bidRequested); - events.emit(BID_RESPONSE, seatBidResponse); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, setTargeting); - events.emit(BID_WON, bidWon); - - let message = JSON.parse(server.requests[0].requestBody); - - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidId).to.equal(STUBBED_UUID); - expect(message.bidsWon[0].bidId).to.equal(STUBBED_UUID); - }); - - it('should pick the highest cpm bid if more than one bid per bidRequestId', function () { - // Only want one bid request in our mock auction - let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); - bidRequested.bids.shift(); - let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); - auctionInit.adUnits.shift(); - - // clone the mock bidResponse and duplicate - let duplicateResponse1 = utils.deepClone(BID2); - duplicateResponse1.cpm = 1.0; - duplicateResponse1.adserverTargeting.hb_pb = '1.0'; - duplicateResponse1.adserverTargeting.hb_adid = '1111'; - let duplicateResponse2 = utils.deepClone(BID2); - duplicateResponse2.cpm = 5.5; - duplicateResponse2.adserverTargeting.hb_pb = '5.5'; - duplicateResponse2.adserverTargeting.hb_adid = '5555'; - let duplicateResponse3 = utils.deepClone(BID2); - duplicateResponse3.cpm = 0.1; - duplicateResponse3.adserverTargeting.hb_pb = '0.1'; - duplicateResponse3.adserverTargeting.hb_adid = '3333'; - - const setTargeting = { - [duplicateResponse2.adUnitCode]: duplicateResponse2.adserverTargeting - }; - - const bidWon = Object.assign({}, duplicateResponse2, { - 'status': 'rendered' - }); - - // spoof the auction with just our duplicates - events.emit(AUCTION_INIT, auctionInit); - events.emit(BID_REQUESTED, bidRequested); - events.emit(BID_RESPONSE, duplicateResponse1); - events.emit(BID_RESPONSE, duplicateResponse2); - events.emit(BID_RESPONSE, duplicateResponse3); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, setTargeting); - events.emit(BID_WON, bidWon); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(5.5); - expect(message.auctions[0].adUnits[0].adserverTargeting.hb_pb).to.equal('5.5'); - expect(message.auctions[0].adUnits[0].adserverTargeting.hb_adid).to.equal('5555'); - expect(message.bidsWon.length).to.equal(1); - expect(message.bidsWon[0].bidResponse.bidPriceUSD).to.equal(5.5); - expect(message.bidsWon[0].adserverTargeting.hb_pb).to.equal('5.5'); - expect(message.bidsWon[0].adserverTargeting.hb_adid).to.equal('5555'); - }); - - it('should send batched message without BID_WON if necessary and further BID_WON events individually', function () { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - - clock.tick(SEND_TIMEOUT + 1000); - - events.emit(BID_WON, MOCK.BID_WON[1]); - - expect(server.requests.length).to.equal(2); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.bidsWon.length).to.equal(1); - expect(message.auctions).to.deep.equal(ANALYTICS_MESSAGE.auctions); - expect(message.bidsWon[0]).to.deep.equal(ANALYTICS_MESSAGE.bidsWon[0]); - - message = JSON.parse(server.requests[1].requestBody); - validate(message); - expect(message.bidsWon.length).to.equal(1); - expect(message).to.not.have.property('auctions'); - expect(message.bidsWon[0]).to.deep.equal(ANALYTICS_MESSAGE.bidsWon[1]); - }); - - it('should properly mark bids as timed out', function () { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); - events.emit(AUCTION_END, MOCK.AUCTION_END); - - clock.tick(SEND_TIMEOUT + 1000); - - expect(server.requests.length).to.equal(1); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - let timedOutBid = message.auctions[0].adUnits[0].bids[0]; - expect(timedOutBid.status).to.equal('error'); - expect(timedOutBid.error.code).to.equal('timeout-error'); - expect(timedOutBid.error.description).to.equal('prebid.js timeout'); - expect(timedOutBid).to.not.have.property('bidResponse'); - }); - - it('should pass aupName as pattern', function () { - let bidRequest = utils.deepClone(MOCK.BID_REQUESTED); - bidRequest.bids[0].ortb2Imp = { - ext: { - data: { - aupname: '1234/mycoolsite/*&gpt_leaderboard&deviceType=mobile' - } - } - }; - bidRequest.bids[1].ortb2Imp = { - ext: { - data: { - aupname: '1234/mycoolsite/*&gpt_skyscraper&deviceType=mobile' - } - } - }; - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, bidRequest); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - - clock.tick(SEND_TIMEOUT + 1000); - - expect(server.requests.length).to.equal(1); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].pattern).to.equal('1234/mycoolsite/*&gpt_leaderboard&deviceType=mobile'); - expect(message.auctions[0].adUnits[1].pattern).to.equal('1234/mycoolsite/*&gpt_skyscraper&deviceType=mobile'); - }); - - it('should pass gpid if defined', function () { - let bidRequest = utils.deepClone(MOCK.BID_REQUESTED); - bidRequest.bids[0].ortb2Imp = { - ext: { - gpid: '1234/mycoolsite/lowerbox' - } - }; - bidRequest.bids[1].ortb2Imp = { - ext: { - gpid: '1234/mycoolsite/leaderboard' - } - }; - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, bidRequest); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - - clock.tick(SEND_TIMEOUT + 1000); - - expect(server.requests.length).to.equal(1); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - expect(message.auctions[0].adUnits[0].gpid).to.equal('1234/mycoolsite/lowerbox'); - expect(message.auctions[0].adUnits[1].gpid).to.equal('1234/mycoolsite/leaderboard'); - }); - - it('should pass bidderDetail for multibid auctions', function () { - let bidResponse = utils.deepClone(MOCK.BID_RESPONSE[1]); - bidResponse.targetingBidder = 'rubi2'; - bidResponse.originalRequestId = bidResponse.requestId; - bidResponse.requestId = '1a2b3c4d5e6f7g8h9'; - - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, bidResponse); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - - clock.tick(SEND_TIMEOUT + 1000); - - expect(server.requests.length).to.equal(1); - - let message = JSON.parse(server.requests[0].requestBody); - validate(message); - - expect(message.auctions[0].adUnits[1].bids[1].bidder).to.equal('rubicon'); - expect(message.auctions[0].adUnits[1].bids[1].bidderDetail).to.equal('rubi2'); - }); - - it('should successfully convert bid price to USD in parseBidResponse', function () { - // Set the rates - setConfig({ - adServerCurrency: 'JPY', - rates: { - USD: { - JPY: 100 - } - } - }); - - // set our bid response to JPY - const bidCopy = utils.deepClone(BID2); - bidCopy.currency = 'JPY'; - bidCopy.cpm = 100; - - // Now add the bidResponse hook which hooks on the currenct conversion function onto the bid response - let innerBid; - addBidResponseHook(function (adCodeId, bid) { - innerBid = bid; - }, 'elementId', bidCopy); - - // Use the rubi analytics parseBidResponse Function to get the resulting cpm from the bid response! - const bidResponseObj = parseBidResponse(innerBid); - expect(bidResponseObj).to.have.property('bidPriceUSD'); - expect(bidResponseObj.bidPriceUSD).to.equal(1.0); - }); - }); - - describe('config with integration type', () => { - it('should use the integration type provided in the config instead of the default', () => { - config.setConfig({ - rubicon: { - int_type: 'testType' - } - }) - - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message.integration).to.equal('testType'); - - rubiconAnalyticsAdapter.disableAnalytics(); - }); - }); - - describe('billing events integration', () => { - beforeEach(function () { - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001 - } - }); - // default dmBilling - config.setConfig({ - rubicon: { - dmBilling: { - enabled: false, - vendors: [], - waitForAuction: true - } - } - }) - }); - afterEach(function () { - rubiconAnalyticsAdapter.disableAnalytics(); - }); - const basicBillingAuction = (billingEvents = []) => { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - - // emit billing events - billingEvents.forEach(ev => events.emit(BILLABLE_EVENT, ev)); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - } - it('should ignore billing events when not enabled', () => { - basicBillingAuction([{ - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }]); - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message.billableEvents).to.be.undefined; - }); - it('should ignore billing events when enabled but vendor is not whitelisted', () => { - // off by default - config.setConfig({ - rubicon: { - dmBilling: { - enabled: true - } - } - }); - basicBillingAuction([{ - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }]); - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message.billableEvents).to.be.undefined; - }); - it('should ignore billing events if billingId is not defined or billingId is not a string', () => { - // off by default - config.setConfig({ - rubicon: { - dmBilling: { - enabled: true, - vendors: ['vendorName'] - } - } - }); - basicBillingAuction([ - { - vendor: 'vendorName', - type: 'auction', - }, - { - vendor: 'vendorName', - type: 'auction', - billingId: true - }, - { - vendor: 'vendorName', - type: 'auction', - billingId: 1233434 - }, - { - vendor: 'vendorName', - type: 'auction', - billingId: null - } - ]); - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message.billableEvents).to.be.undefined; - }); - it('should pass along billing event in same payload', () => { - // off by default - config.setConfig({ - rubicon: { - dmBilling: { - enabled: true, - vendors: ['vendorName'] - } - } - }); - basicBillingAuction([{ - vendor: 'vendorName', - type: 'pageView', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }]); - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message).to.haveOwnProperty('auctions'); - expect(message.billableEvents).to.deep.equal([{ - accountId: 1001, - vendor: 'vendorName', - type: 'pageView', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }]); - }); - it('should pass along multiple billing events but filter out duplicates', () => { - // off by default - config.setConfig({ - rubicon: { - dmBilling: { - enabled: true, - vendors: ['vendorName'] - } - } - }); - basicBillingAuction([ - { - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }, - { - vendor: 'vendorName', - type: 'impression', - billingId: '743db6e3-21f2-44d4-917f-cb3488c6076f' - }, - { - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - } - ]); - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message).to.haveOwnProperty('auctions'); - expect(message.billableEvents).to.deep.equal([ - { - accountId: 1001, - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }, - { - accountId: 1001, - vendor: 'vendorName', - type: 'impression', - billingId: '743db6e3-21f2-44d4-917f-cb3488c6076f' - } - ]); - }); - it('should pass along event right away if no pending auction', () => { - // off by default - config.setConfig({ - rubicon: { - dmBilling: { - enabled: true, - vendors: ['vendorName'] - } - } - }); - - events.emit(BILLABLE_EVENT, { - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }); - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message).to.not.haveOwnProperty('auctions'); - expect(message.billableEvents).to.deep.equal([ - { - accountId: 1001, - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - } - ]); - }); - it('should pass along event right away if pending auction but not waiting', () => { - // off by default - config.setConfig({ - rubicon: { - dmBilling: { - enabled: true, - vendors: ['vendorName'], - waitForAuction: false - } - } - }); - // should fire right away, and then auction later - basicBillingAuction([{ - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - }]); - expect(server.requests.length).to.equal(2); - const billingRequest = server.requests[0]; - const billingMessage = JSON.parse(billingRequest.requestBody); - expect(billingMessage).to.not.haveOwnProperty('auctions'); - expect(billingMessage.billableEvents).to.deep.equal([ - { - accountId: 1001, - vendor: 'vendorName', - type: 'auction', - billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965' - } - ]); - // auction event after - const auctionRequest = server.requests[1]; - const auctionMessage = JSON.parse(auctionRequest.requestBody); - // should not double pass events! - expect(auctionMessage).to.not.haveOwnProperty('billableEvents'); - }); - }); - - describe('wrapper details passed in', () => { - it('should correctly pass in the wrapper details if provided', () => { - config.setConfig({ - rubicon: { - wrapperName: '1001_wrapperName_exp.4', - wrapperFamily: '1001_wrapperName', - rule_name: 'na-mobile' - } - }); - - rubiconAnalyticsAdapter.enableAnalytics({ - options: { - endpoint: '//localhost:9999/event', - accountId: 1001 - } - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(1); - const request = server.requests[0]; - const message = JSON.parse(request.requestBody); - expect(message.wrapper).to.deep.equal({ - name: '1001_wrapperName_exp.4', - family: '1001_wrapperName', - rule: 'na-mobile' - }); - - rubiconAnalyticsAdapter.disableAnalytics(); - }); - }); - - it('getHostNameFromReferer correctly grabs hostname from an input URL', function () { - let inputUrl = 'https://www.prebid.org/some/path?pbjs_debug=true'; - expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.org'); - inputUrl = 'https://www.prebid.com/some/path?pbjs_debug=true'; - expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.com'); - inputUrl = 'https://prebid.org/some/path?pbjs_debug=true'; - expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org'); - inputUrl = 'http://xn--p8j9a0d9c9a.xn--q9jyb4c/'; - expect(typeof getHostNameFromReferer(inputUrl)).to.equal('string'); - - // not non-UTF char's in query / path which break if noDecodeWholeURL not set - inputUrl = 'https://prebid.org/search_results/%95x%8Em%92%CA/?category=000'; - expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org'); - }); -}); diff --git a/test/spec/modules/rubiconAnalyticsSchema.json b/test/spec/modules/rubiconAnalyticsSchema.json deleted file mode 100644 index 2d0dca42d23..00000000000 --- a/test/spec/modules/rubiconAnalyticsSchema.json +++ /dev/null @@ -1,494 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Prebid Auctions", - "description": "A batched data object describing the lifecycle of an auction or multiple auction across a single page view.", - "type": "object", - "required": [ - "integration", - "version" - ], - "anyOf": [ - { - "required": [ - "auctions" - ] - }, - { - "required": [ - "bidsWon" - ] - }, - { - "required": [ - "billableEvents" - ] - } - ], - "properties": { - "integration": { - "type": "string", - "description": "Integration type that generated this event.", - "default": "pbjs" - }, - "version": { - "type": "string", - "description": "Version of Prebid.js responsible for the auctions contained within." - }, - "fpkvs": { - "type": "array", - "description": "List of any dynamic key value pairs set by publisher.", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "key", - "value" - ], - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - } - }, - "session": { - "type": "object", - "description": "The session information for a given event", - "required": [ - "id", - "start", - "expires" - ], - "properties": { - "id": { - "type": "string", - "description": "UUID of session." - }, - "start": { - "type": "integer", - "description": "Unix timestamp of time of creation for this session in milliseconds." - }, - "expires": { - "type": "integer", - "description": "Unix timestamp of the maximum allowed time in milliseconds of the session." - }, - "pvid": { - "type": "string", - "description": "id to track page view." - } - } - }, - "auctions": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "adUnits", - "samplingFactor" - ], - "properties": { - "clientTimeoutMillis": { - "type": "integer", - "description": "Timeout given in client for given auction in milliseconds (if applicable)." - }, - "serverTimeoutMillis": { - "type": "integer", - "description": "Timeout configured for server adapter request in milliseconds (if applicable)." - }, - "accountId": { - "type": "number", - "description": "The account id for prebid server (if applicable)." - }, - "samplingFactor": { - "$ref": "#/definitions/samplingFactor" - }, - "adUnits": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "description": "An array of adUnits involved in this auction.", - "required": [ - "status", - "adUnitCode", - "transactionId", - "mediaTypes", - "dimensions", - "bids" - ], - "properties": { - "status": { - "type": "string", - "description": "The status of the adUnit" - }, - "adUnitCode": { - "type": "string", - "description": "The adUnit.code identifier" - }, - "transactionId": { - "type": "string", - "description": "The UUID generated id to represent this adunit in this auction." - }, - "adSlot": { - "type": "string" - }, - "mediaTypes": { - "$ref": "#/definitions/mediaTypes" - }, - "videoAdFormat": { - "$ref": "#/definitions/videoAdFormat" - }, - "dimensions": { - "type": "array", - "description": "All valid sizes included in this auction (note: may be sizeConfig filtered).", - "minItems": 1, - "items": { - "$ref": "#/definitions/dimensions" - } - }, - "adserverTargeting": { - "$ref": "#/definitions/adserverTargeting" - }, - "bids": { - "type": "array", - "description": "An array that contains a combination of the bids from the adUnit combined with their responses.", - "minItems": 1, - "items": { - "$ref": "#/definitions/bid" - } - }, - "accountId": { - "type": "number", - "description": "The Rubicon AccountId associated with this adUnit - Removed if null" - }, - "siteId": { - "type": "number", - "description": "The Rubicon siteId associated with this adUnit - Removed if null" - }, - "zoneId": { - "type": "number", - "description": "The Rubicon zoneId associated with this adUnit - Removed if null" - }, - "gam": { - "$ref": "#/definitions/gam" - } - } - } - } - } - } - }, - "bidsWon": { - "type": "array", - "minItems": 1, - "items": { - "allOf": [ - { - "$ref": "#/definitions/bid" - }, - { - "required": [ - "transactionId", - "accountId", - "samplingFactor", - "mediaTypes", - "adUnitCode", - "bidwonStatus" - ], - "properties": { - "transactionId": { - "type": "string" - }, - "accountId": { - "type": "number" - }, - "samplingFactor": { - "$ref": "#/definitions/samplingFactor" - }, - "adUnitCode": { - "type": "string" - }, - "videoAdFormat": { - "$ref": "#/definitions/videoAdFormat" - }, - "mediaTypes": { - "$ref": "#/definitions/mediaTypes" - }, - "adserverTargeting": { - "$ref": "#/definitions/adserverTargeting" - }, - "bidwonStatus": { - "description": "Whether the bid was successfully rendered or not", - "type": "string", - "enum": [ - "success", - "error" - ] - }, - "siteId": { - "type": "number", - "description": "The Rubicon siteId associated with this adUnit - Removed if null" - }, - "zoneId": { - "type": "number", - "description": "The Rubicon zoneId associated with this adUnit - Removed if null" - } - } - } - ] - } - }, - "billableEvents":{ - "type":"array", - "minItems":1, - "items":{ - "type":"object", - "required":[ - "accountId", - "vendor", - "type", - "billingId" - ], - "properties":{ - "vendor":{ - "type":"string", - "description":"The name of the vendor who emitted the billable event" - }, - "type":{ - "type":"string", - "description":"The type of billable event", - "enum":[ - "impression", - "pageLoad", - "auction", - "request", - "general" - ] - }, - "billingId":{ - "type":"string", - "description":"A UUID which is responsible more mapping this event to" - }, - "accountId": { - "type": "number", - "description": "The account id for the rubicon publisher" - } - } - } - } - }, - "definitions": { - "gam": { - "type": "object", - "description": "The gam information for a given ad unit", - "required": [ - "adSlot" - ], - "properties": { - "adSlot": { - "type": "string" - }, - "advertiserId": { - "type": "integer" - }, - "creativeId": { - "type": "integer" - }, - "LineItemId": { - "type": "integer" - }, - "isSlotEmpty": { - "type": "boolean", - "enum": [ - true - ] - } - } - }, - "adserverTargeting": { - "type": "object", - "description": "The adserverTargeting key/value pairs", - "patternProperties": { - ".+": { - "type": "string" - } - } - }, - "samplingFactor": { - "type": "integer", - "description": "An integer value representing the factor to multiply event count by to receive unsampled count.", - "enum": [ - 1, - 10, - 20, - 40, - 100 - ] - }, - "videoAdFormat": { - "type": "string", - "description": "This value only provided for video specifies the ad format", - "enum": [ - "pre-roll", - "interstitial", - "outstream", - "mid-roll", - "post-roll", - "vertical" - ] - }, - "mediaTypes": { - "type": "array", - "uniqueItems": true, - "minItems": 1, - "items": { - "type": "string", - "enum": [ - "native", - "video", - "banner" - ] - } - }, - "dimensions": { - "type": "object", - "description": "Size object representing the dimensions of creative in pixels.", - "required": [ - "width", - "height" - ], - "properties": { - "width": { - "type": "integer", - "minimum": 1 - }, - "height": { - "type": "integer", - "minimum": 1 - } - } - }, - "bid": { - "type": "object", - "required": [ - "bidder", - "bidId", - "status", - "source" - ], - "properties": { - "bidder": { - "type": "string" - }, - "bidId": { - "type": "string", - "description": "UUID representing this individual bid request in this auction." - }, - "params": { - "description": "A copy of the bid.params from the adUnit.bids", - "anyOf": [ - { - "type": "object" - }, - { - "$ref": "#/definitions/params/rubicon" - } - ] - }, - "status": { - "type": "string", - "enum": [ - "success", - "no-bid", - "error", - "rejected-gdpr", - "rejected-ipf" - ] - }, - "error": { - "type": "object", - "additionalProperties": false, - "required": [ - "code" - ], - "properties": { - "code": { - "type": "string", - "enum": [ - "request-error", - "connect-error", - "timeout-error" - ] - }, - "description": { - "type": "string" - } - } - }, - "source": { - "type": "string", - "enum": [ - "client", - "server" - ] - }, - "clientLatencyMillis": { - "type": "integer", - "description": "Latency from auction start to bid response recieved in milliseconds." - }, - "serverLatencyMillis": { - "type": "integer", - "description": "Latency returned by prebid server (response_time_ms)." - }, - "bidResponse": { - "type": "object", - "required": [ - "mediaType", - "bidPriceUSD" - ], - "properties": { - "dimensions": { - "$ref": "#/definitions/dimensions" - }, - "mediaType": { - "type": "string", - "enum": [ - "native", - "video", - "banner" - ] - }, - "bidPriceUSD": { - "type": "number", - "description": "The bid value denoted in USD" - }, - "dealId": { - "type": "integer", - "description": "The id associated with any potential deals" - } - } - } - } - }, - "params": { - "rubicon": { - "type": "object", - "properties": { - "accountId": { - "type": "number" - }, - "siteId": { - "type": "number" - }, - "zoneId": { - "type": "number" - } - } - } - } - } -} diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 0c8477a0fcb..d9c3555bf03 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -87,6 +87,11 @@ describe('the rubicon adapter', function () { function getBidderRequest() { return { bidderCode: 'rubicon', + ortb2: { + source: { + tid: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + } + }, auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', bidderRequestId: '178e34bad3658f', bids: [ @@ -120,7 +125,11 @@ describe('the rubicon adapter', function () { bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', - transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + ortb2Imp: { + ext: { + tid: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + } + }, } ], start: 1472239426002, @@ -319,6 +328,11 @@ describe('the rubicon adapter', function () { bidderRequest = { bidderCode: 'rubicon', auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + ortb2: { + source: { + tid: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + } + }, bidderRequestId: '178e34bad3658f', bids: [ { @@ -351,7 +365,11 @@ describe('the rubicon adapter', function () { bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', - transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + ortb2Imp: { + ext: { + tid: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + } + }, } ], start: 1472239426002, @@ -436,7 +454,7 @@ describe('the rubicon adapter', function () { 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', 'kw': 'a,b,c', @@ -626,7 +644,7 @@ describe('the rubicon adapter', function () { 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'x_imp.ext.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', @@ -978,7 +996,7 @@ describe('the rubicon adapter', function () { 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', 'kw': 'a,b,c', @@ -2285,6 +2303,7 @@ describe('the rubicon adapter', function () { describe('createSlotParams', function () { it('should return a valid slot params object', function () { + const localBidderRequest = Object.assign({}, bidderRequest); let expectedQuery = { 'account_id': '14062', 'site_id': '70608', @@ -2294,7 +2313,7 @@ describe('the rubicon adapter', function () { 'p_pos': 'atf', 'rp_secure': /[01]/, 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', 'kw': 'a,b,c', @@ -2307,7 +2326,7 @@ describe('the rubicon adapter', function () { 'rf': 'localhost' }; - const slotParams = spec.createSlotParams(bidderRequest.bids[0], bidderRequest); + const slotParams = spec.createSlotParams(bidderRequest.bids[0], localBidderRequest); // test that all values above are both present and correct Object.keys(expectedQuery).forEach(key => { diff --git a/test/spec/modules/scatteredBidAdapter_spec.js b/test/spec/modules/scatteredBidAdapter_spec.js index 7f0b13bc07b..1db2d98d326 100644 --- a/test/spec/modules/scatteredBidAdapter_spec.js +++ b/test/spec/modules/scatteredBidAdapter_spec.js @@ -97,9 +97,7 @@ describe('Scattered adapter', function () { it('has the right fields filled', function () { let request = spec.buildRequests(arrayOfValidBidRequests, validBidderRequest); const bidderRequest = request.data; - assert.equal(bidderRequest.id, validBidderRequest.auctionId); assert.ok(bidderRequest.site); - assert.ok(bidderRequest.source); assert.lengthOf(bidderRequest.imp, 1); }); diff --git a/test/spec/modules/schain_spec.js b/test/spec/modules/schain_spec.js index 34d0cff9a60..eb8e35749db 100644 --- a/test/spec/modules/schain_spec.js +++ b/test/spec/modules/schain_spec.js @@ -469,6 +469,15 @@ describe('#makeBidRequestsHook', function() { makeBidRequestsHook(testCallback, testBidderRequests); }); + it('should not share the same schain object between different bid requests', (done) => { + config.setBidderConfig(goodStrictBidderConfig); + makeBidRequestsHook((requests) => { + requests[0].bids[0].schain.field = 'value'; + expect(requests[1].bids[0].schain.field).to.not.exist; + done(); + }, deepClone(bidderRequests)) + }); + it('should reject bad strict config but allow a bad relaxed config for bidders trying to override it', function () { function testCallback(bidderRequests) { expect(bidderRequests[0].bids[0].schain).to.exist; diff --git a/test/spec/modules/seedingAllianceAdapter_spec.js b/test/spec/modules/seedingAllianceAdapter_spec.js index 6086db01de4..03548cf923a 100755 --- a/test/spec/modules/seedingAllianceAdapter_spec.js +++ b/test/spec/modules/seedingAllianceAdapter_spec.js @@ -49,17 +49,6 @@ describe('SeedingAlliance adapter', function () { assert.deepEqual(keys, data); }); - it('Verify the auction ID', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {}, - auctionId: 'auctionId' - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' }, auctionId: validBidRequests[0].auctionId }).data); - - assert.equal(request.id, validBidRequests[0].auctionId); - }); - it('Verify the site url', function () { let siteUrl = 'https://www.yourdomain.tld/your-directory/'; let validBidRequests = [{ @@ -189,7 +178,6 @@ describe('SeedingAlliance adapter', function () { const resultNative = spec.interpretResponse(goodNativeResponse, bidNativeRequest); const bidNative = goodNativeResponse.body.seatbid[0].bid[0]; - assert.deepEqual(resultNative[0].bidderCode, 'seedingAlliance'); assert.deepEqual(resultNative[0].currency, goodNativeResponse.body.cur); assert.deepEqual(resultNative[0].requestId, bidNativeRequest.bidRequests[0].bidId); assert.deepEqual(resultNative[0].cpm, bidNative.price); @@ -198,7 +186,6 @@ describe('SeedingAlliance adapter', function () { const resultBanner = spec.interpretResponse(goodBannerResponse, bidBannerRequest); - assert.deepEqual(resultBanner[0].bidderCode, 'seedingAlliance'); assert.deepEqual(resultBanner[0].mediaType, 'banner'); assert.deepEqual(resultBanner[0].width, bidBannerRequest.bidRequests[0].sizes[0]); assert.deepEqual(resultBanner[0].height, bidBannerRequest.bidRequests[0].sizes[1]); diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 3627296975b..fb666e89f73 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -20,7 +20,11 @@ function getSlotConfigs(mediaTypes, params) { bidder: 'seedtag', mediaTypes: mediaTypes, src: 'client', - transactionId: 'd704d006-0d6e-4a09-ad6c-179e7e758096', + ortb2Imp: { + ext: { + tid: 'd704d006-0d6e-4a09-ad6c-179e7e758096', + } + }, adUnitCode: 'adunit-code', }; } diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index fc4fbc86018..5b7e5b6b956 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -230,7 +230,7 @@ describe('sharethrough adapter spec', function () { api: [3], mimes: ['video/3gpp'], protocols: [2, 3], - playerSize: [640, 480], + playerSize: [[640, 480]], startdelay: 42, skipmin: 10, skipafter: 20, @@ -248,7 +248,11 @@ describe('sharethrough adapter spec', function () { refererInfo: { ref: 'https://referer.com', }, - auctionId: 'auction-id', + ortb2: { + source: { + tid: 'auction-id' + } + }, timeout: 242 }; }); @@ -311,7 +315,7 @@ describe('sharethrough adapter spec', function () { expect(openRtbReq.device.ua).to.equal(navigator.userAgent); expect(openRtbReq.regs.coppa).to.equal(1); - expect(openRtbReq.source.tid).to.equal(bidderRequest.auctionId); + expect(openRtbReq.source.tid).to.equal(bidderRequest.ortb2.source.tid); expect(openRtbReq.source.ext.version).not.to.be.undefined; expect(openRtbReq.source.ext.str).not.to.be.undefined; expect(openRtbReq.source.ext.schain).to.deep.equal(bidRequests[0].schain); diff --git a/test/spec/modules/sirdataRtdProvider_spec.js b/test/spec/modules/sirdataRtdProvider_spec.js index eccc4777906..fbb5967bc20 100644 --- a/test/spec/modules/sirdataRtdProvider_spec.js +++ b/test/spec/modules/sirdataRtdProvider_spec.js @@ -1,26 +1,31 @@ -import {addSegmentData, getSegmentsAndCategories, sirdataSubmodule} from 'modules/sirdataRtdProvider.js'; +import {addSegmentData, getSegmentsAndCategories, sirdataSubmodule, setOrtb2} from 'modules/sirdataRtdProvider.js'; import {server} from 'test/mocks/xhr.js'; const responseHeader = {'Content-Type': 'application/json'}; describe('sirdataRtdProvider', function () { describe('sirdataSubmodule', function () { + it('exists', function () { + expect(sirdataSubmodule.init).to.be.a('function'); + }); it('successfully instantiates', function () { expect(sirdataSubmodule.init()).to.equal(true); }); + it('has the correct module name', function () { + expect(sirdataSubmodule.name).to.equal('SirdataRTDModule'); + }); }); describe('Add Segment Data', function () { it('adds segment data', function () { - const config = { + const firstConfig = { params: { - setGptKeyValues: false, + partnerId: 1, + key: 1, + setGptKeyValues: true, + gptCurationId: 27449, contextualMinRelevancyScore: 50, - bidders: [{ - bidder: 'appnexus' - }, { - bidder: 'other' - }] + bidders: [] } }; @@ -37,21 +42,47 @@ describe('sirdataRtdProvider', function () { } ]; - let data = { + let firstReqBidsConfigObj = { + adUnits: adUnits, + ortb2Fragments: { + global: {} + } + }; + + let firstData = { segments: [111111, 222222], - contextual_categories: {'333333': 100} + contextual_categories: {'333333': 100}, + 'segtaxid': null, + 'cattaxid': null, + 'shared_taxonomy': { + '27449': { + 'segments': [444444, 555555], + 'segtaxid': null, + 'cattaxid': null, + 'contextual_categories': {'666666': 100} + } + }, + 'global_taxonomy': { + '9998': { + 'segments': [123, 234], + 'segtaxid': 4, + 'cattaxid': 7, + 'contextual_categories': {'345': 100, '456': 100} + } + } }; - addSegmentData({adUnits}, data, config, () => { + addSegmentData(firstReqBidsConfigObj, firstData, firstConfig, () => { }); - expect(adUnits[0].bids[0].params.keywords).to.have.deep.property('sd_rtd', ['111111', '222222', '333333']); + + expect(firstReqBidsConfigObj.ortb2Fragments.global.user.data[0].ext.segtax).to.equal(4); }); }); describe('Get Segments And Categories', function () { it('gets data from async request and adds segment data', function () { const overrideAppnexus = function (adUnit, list, data, bid) { - deepSetValue(bid, 'params.keywords.custom', list); + deepSetValue(bid, 'params.keywords.custom', list.segments.concat(list.categories)); } const config = { @@ -60,16 +91,21 @@ describe('sirdataRtdProvider', function () { contextualMinRelevancyScore: 50, bidders: [{ bidder: 'appnexus', - customFunction: overrideAppnexus + customFunction: overrideAppnexus, + curationId: 27446 }, { - bidder: 'smartadserver' + bidder: 'smartadserver', + curationId: 27440 }, { bidder: 'ix', sizeLimit: 1200, + curationId: 27248 }, { bidder: 'rubicon', + curationId: 27452 }, { bidder: 'proxistore', + curationId: 27484 }] } }; @@ -146,6 +182,12 @@ describe('sirdataRtdProvider', function () { 'segtaxid': 552, 'cattaxid': 553, 'contextual_categories': {'666666': 100} + }, + '27446': { + 'segments': [777777, 888888], + 'segtaxid': 552, + 'cattaxid': 553, + 'contextual_categories': {'999999': 100} } }, 'global_taxonomy': { @@ -164,8 +206,6 @@ describe('sirdataRtdProvider', function () { let request = server.requests[0]; request.respond(200, responseHeader, JSON.stringify(data)); - expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.have.deep.property('target', 'sd_rtd=111111;sd_rtd=222222;sd_rtd=333333;sd_rtd=444444;sd_rtd=555555;sd_rtd=666666'); - expect(reqBidsConfigObj.ortb2Fragments.global.site.content.data[0].name).to.equal( 'sirdata.com' ); @@ -185,4 +225,57 @@ describe('sirdataRtdProvider', function () { expect(reqBidsConfigObj.ortb2Fragments.global.user.data[0].ext.segtax).to.equal(4); }); }); + + describe('Set ortb2 for bidder', function () { + it('set ortb2 for a givent bidder', function () { + const config = { + params: { + setGptKeyValues: false, + contextualMinRelevancyScore: 50, + bidders: [{ + bidder: 'appnexus', + }] + } + }; + + let reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }] + }], + ortb2Fragments: { + global: {} + } + }; + + let data = { + 'segments': [111111, 222222], + 'segtaxid': null, + 'cattaxid': null, + 'contextual_categories': {'333333': 100}, + 'shared_taxonomy': { + '27440': { + 'segments': [444444, 555555], + 'segtaxid': null, + 'cattaxid': null, + 'contextual_categories': {'666666': 100} + } + }, + 'global_taxonomy': {} + }; + + window.googletag = window.googletag || {}; + window.googletag.cmd = window.googletag.cmd || []; + + let test = setOrtb2(reqBidsConfigObj.ortb2Fragments, 'appnexus', 'user', []); + expect(test).to.be.false; + + test = setOrtb2(reqBidsConfigObj.ortb2Fragments, 'appnexus', 'user', ['1']); + expect(test).to.be.true; + }); + }); }); diff --git a/test/spec/modules/sizeMapping_spec.js b/test/spec/modules/sizeMapping_spec.js new file mode 100644 index 00000000000..55b536868e6 --- /dev/null +++ b/test/spec/modules/sizeMapping_spec.js @@ -0,0 +1,342 @@ +import {expect} from 'chai'; +import {resolveStatus, setSizeConfig, sizeSupported} from 'modules/sizeMapping.js'; +import {includes} from 'src/polyfill.js'; + +let utils = require('src/utils.js'); +let deepClone = utils.deepClone; + +describe('sizeMapping', function () { + var sizeConfig = [{ + 'mediaQuery': '(min-width: 1200px)', + 'sizesSupported': [ + [970, 90], + [728, 90], + [300, 250] + ] + }, { + 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', + 'sizesSupported': [ + [728, 90], + [300, 250], + [300, 100] + ] + }, { + 'mediaQuery': '(min-width: 0px) and (max-width: 767px)', + 'sizesSupported': [] + }]; + + var sizeConfigWithLabels = [{ + 'mediaQuery': '(min-width: 1200px)', + 'labels': ['desktop'] + }, { + 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', + 'sizesSupported': [ + [728, 90], + [300, 250] + ], + 'labels': ['tablet', 'phone'] + }, { + 'mediaQuery': '(min-width: 0px) and (max-width: 767px)', + 'sizesSupported': [ + [300, 250], + [300, 100] + ], + 'labels': ['phone'] + }]; + + let sandbox, + matchMediaOverride; + + beforeEach(function () { + setSizeConfig(sizeConfig); + + sandbox = sinon.sandbox.create(); + + matchMediaOverride = {matches: false}; + + sandbox.stub(utils.getWindowTop(), 'matchMedia').callsFake((...args) => { + if (typeof matchMediaOverride === 'function') { + return matchMediaOverride.apply(utils.getWindowTop(), args); + } + return matchMediaOverride; + }); + }); + + afterEach(function () { + setSizeConfig([]); + + sandbox.restore(); + }); + + describe('sizeConfig', () => { + it('should allow us to validate a single size', function () { + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + + expect(sizeSupported([300, 250])).to.equal(true); + expect(sizeSupported([80, 80])).to.equal(false); + }); + + it('should log a warning when mediaQuery property missing from sizeConfig', function () { + let errorConfig = deepClone(sizeConfig); + + delete errorConfig[0].mediaQuery; + + sandbox.stub(utils, 'logWarn'); + + resolveStatus(undefined, {}, errorConfig); + expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); + }); + + it('should log a warning message when mediaQuery property is declared as an empty string', function () { + const errorConfig = deepClone(sizeConfig); + errorConfig[0].mediaQuery = ''; + + sandbox.stub(utils, 'logWarn'); + resolveStatus(undefined, {}, errorConfig); + expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); + }); + }); + + const TEST_SIZES = [[970, 90], [728, 90], [300, 250], [300, 100], [80, 80]]; + + const suites = { + banner: { + mediaTypes: { + banner: { + sizes: TEST_SIZES + } + }, + getSizes(mediaTypes) { + return mediaTypes.banner.sizes; + }, + } + } + if (FEATURES.VIDEO) { + suites.video = { + mediaTypes: { + video: { + playerSize: TEST_SIZES + } + }, + getSizes(mediaTypes) { + return mediaTypes.video.playerSize; + } + } + } + Object.entries(suites).forEach(([mediaType, {mediaTypes, getSizes}]) => { + describe(`for ${mediaType}`, () => { + describe('when handling sizes', function () { + it('when one mediaQuery block matches, it should filter the adUnit.sizes passed in', function () { + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + + let status = resolveStatus(undefined, mediaTypes, sizeConfig); + + expect(status.active).to.equal(true); + expect(getSizes(status.mediaTypes)).to.deep.equal( + [[970, 90], [728, 90], [300, 250]] + ); + }); + + it('when multiple mediaQuery block matches, it should filter a union of the matched sizesSupported', function () { + matchMediaOverride = (str) => includes([ + '(min-width: 1200px)', + '(min-width: 768px) and (max-width: 1199px)' + ], str) ? {matches: true} : {matches: false}; + + let status = resolveStatus(undefined, mediaTypes, sizeConfig); + expect(status.active).to.equal(true); + expect(getSizes(status.mediaTypes)).to.deep.equal( + [[970, 90], [728, 90], [300, 250], [300, 100]] + ); + }); + + it('if no mediaQueries match, it should allow all sizes specified', function () { + matchMediaOverride = () => ({matches: false}); + + let status = resolveStatus(undefined, mediaTypes, sizeConfig); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(mediaTypes); + }); + + it('if a mediaQuery matches and has sizesSupported: [], it should filter all sizes', function () { + matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; + + let status = resolveStatus(undefined, mediaTypes, sizeConfig); + expect(status.active).to.equal(false); + expect(getSizes(status.mediaTypes)).to.deep.equal([]); + }); + + it('should filter all banner sizes and should disable the adUnit even if other mediaTypes are present', function () { + matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; + let status = resolveStatus(undefined, Object.assign({}, mediaTypes, { + native: { + type: 'image' + } + }), sizeConfig); + expect(status.active).to.equal(false); + expect(getSizes(status.mediaTypes)).to.deep.equal([]); + expect(status.mediaTypes.native).to.deep.equal({ + type: 'image' + }); + }); + + it('if a mediaQuery matches and no sizesSupported specified, it should not affect adUnit.sizes', function () { + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + + let status = resolveStatus(undefined, mediaTypes, sizeConfigWithLabels); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(mediaTypes); + }); + }); + + describe('when handling labels', function () { + it('should activate/deactivate adUnits/bidders based on sizeConfig.labels', function () { + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + + let status = resolveStatus({ + labels: ['desktop'] + }, mediaTypes, sizeConfigWithLabels); + + expect(status).to.deep.equal({ + active: true, + mediaTypes: mediaTypes + }); + + status = resolveStatus({ + labels: ['tablet'] + }, mediaTypes, sizeConfigWithLabels); + + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(mediaTypes); + }); + + it('should active/deactivate adUnits/bidders based on requestBids labels', function () { + let activeLabels = ['us-visitor', 'desktop', 'smart']; + + let status = resolveStatus({ + labels: ['uk-visitor'], // from adunit + activeLabels // from requestBids.labels + }, mediaTypes, sizeConfigWithLabels); + + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(mediaTypes); + + status = resolveStatus({ + labels: ['us-visitor'], + activeLabels + }, mediaTypes, sizeConfigWithLabels); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(mediaTypes); + + status = resolveStatus({ + labels: ['us-visitor', 'tablet'], + labelAll: true, + activeLabels + }, mediaTypes, sizeConfigWithLabels); + + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(mediaTypes); + + status = resolveStatus({ + labels: ['us-visitor', 'desktop'], + labelAll: true, + activeLabels + }, mediaTypes, undefined, sizeConfigWithLabels); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(mediaTypes); + }); + }); + }); + }); + + if (FEATURES.VIDEO) { + it('should activate/decactivate adUnits/bidders based on labels with multiformat ads', function () { + matchMediaOverride = (str) => str === '(min-width: 768px) and (max-width: 1199px)' ? {matches: true} : {matches: false}; + + let multiFormatSizes = { + banner: { + sizes: [[728, 90], [300, 300]] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [[728, 90], [300, 300]] + } + }; + + let status = resolveStatus({ + labels: ['tablet', 'test'], + labelAll: true + }, multiFormatSizes, sizeConfigWithLabels); + + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[728, 90]] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [[728, 90]] + } + }); + + status = resolveStatus({ + labels: ['tablet'] + }, multiFormatSizes, sizeConfigWithLabels); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[728, 90]] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [[728, 90]] + } + }); + + [multiFormatSizes.banner.sizes, multiFormatSizes.video.playerSize].forEach(sz => sz.splice(0, 1, [728, 80])) + status = resolveStatus({ + labels: ['tablet'] + }, multiFormatSizes, sizeConfigWithLabels); + + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [] + } + }); + + delete multiFormatSizes.banner; + delete multiFormatSizes.video; + + status = resolveStatus({ + labels: ['tablet'] + }, multiFormatSizes, sizeConfigWithLabels); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + native: { + type: 'image' + } + }); + }); + } +}); diff --git a/test/spec/modules/slimcutBidAdapter_spec.js b/test/spec/modules/slimcutBidAdapter_spec.js index d821627c24b..da0fee48936 100644 --- a/test/spec/modules/slimcutBidAdapter_spec.js +++ b/test/spec/modules/slimcutBidAdapter_spec.js @@ -178,7 +178,6 @@ describe('slimcutBidAdapter', function() { 'ad': AD_SCRIPT, 'requestId': '3ede2a3fa0db94', 'creativeId': 'er2ee', - 'transactionId': 'deadb33f', 'winUrl': 'https://sb.freeskreen.com/win', 'meta': { 'advertiserDomains': [] diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 7f727d5d9e3..61a877f329d 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -13,6 +13,7 @@ const CONSENT_STRING = 'HFIDUYFIUYIUYWIPOI87392DSU' const AUCTION_ID = '6653'; const defaultBidderRequest = { + bidderRequestId: 'mock-uuid', gdprConsent: { consentString: CONSENT_STRING, gdprApplies: true @@ -554,7 +555,7 @@ describe('smaatoBidAdapterTest', () => { const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.id).to.be.equal(AUCTION_ID); + expect(req.id).to.exist; expect(req.imp.length).to.be.equal(ADPOD_DURATION / DURATION_RANGE[0]); expect(req.imp[0].id).to.be.equal(BID_ID); expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID); @@ -658,7 +659,7 @@ describe('smaatoBidAdapterTest', () => { const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.id).to.be.equal(AUCTION_ID); + expect(req.id).to.exist; expect(req.imp.length).to.be.equal(DURATION_RANGE.length); expect(req.imp[0].id).to.be.equal(BID_ID); expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID); diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 504ff978e9e..9daa6a87826 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; import { spec } from 'modules/smartadserverBidAdapter.js'; // Default params with optional ones @@ -29,7 +30,11 @@ describe('Smart bid adapter tests', function () { ckId: 42 }, requestId: 'efgh5678', - transactionId: 'zsfgzzg' + ortb2Imp: { + ext: { + tid: 'zsfgzzg' + } + }, }]; var DEFAULT_PARAMS_WITH_EIDS = [{ @@ -586,7 +591,11 @@ describe('Smart bid adapter tests', function () { } }, requestId: 'efgh5678', - transactionId: 'zsfgzzg' + ortb2Imp: { + ext: { + tid: 'zsfgzzg', + } + }, }]; var INSTREAM_BID_RESPONSE = { @@ -855,7 +864,11 @@ describe('Smart bid adapter tests', function () { protocol: 7 } }, - requestId: 'efgh5679', + ortb2Imp: { + ext: { + tid: 'efgh5679', + } + }, transactionId: 'zsfgzzga' }]; @@ -1339,4 +1352,45 @@ describe('Smart bid adapter tests', function () { expect(bannerRequest).to.have.property('formatid').and.to.equal('90'); }); }); + + describe('Global Placement ID (GPID)', function () { + it('should not include gpid by default', () => { + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.not.have.property('gdid'); + }); + + it('should include gpid if pbadslot in ortb2Imp', () => { + const gpid = '/19968336/header-bid-tag-1'; + const bidRequests = deepClone(DEFAULT_PARAMS_WO_OPTIONAL); + + bidRequests[0].ortb2Imp = { + ext: { + data: { + pbadslot: gpid + } + } + }; + + const request = spec.buildRequests(bidRequests); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.have.property('gpid').and.to.equal(gpid); + }); + + it('should include gpid if imp[].ext.gpid exists', () => { + const gpid = '/1111/homepage#div-leftnav'; + const bidRequests = deepClone(DEFAULT_PARAMS_WO_OPTIONAL); + + bidRequests[0].ortb2Imp = { + ext: { gpid } + }; + + const request = spec.buildRequests(bidRequests); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.have.property('gpid').and.to.equal(gpid); + }); + }); }); diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index 44d2c7c6507..22221dbe1ef 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -14,7 +14,11 @@ const DISPLAY_REQUEST = [{ zoneId: 1 }, requestId: 'request_abcd1234', - transactionId: 'trans_abcd1234' + ortb2Imp: { + ext: { + tid: 'trans_abcd1234', + } + }, }]; const DISPLAY_REQUEST_WITH_EIDS = [{ @@ -29,7 +33,11 @@ const DISPLAY_REQUEST_WITH_EIDS = [{ zoneId: 1 }, requestId: 'request_abcd1234', - transactionId: 'trans_abcd1234', + ortb2Imp: { + ext: { + tid: 'trans_abcd1234', + } + }, userIdAsEids: [{ source: 'pubcid.org', uids: [{ @@ -61,7 +69,11 @@ const DISPLAY_REQUEST_WITH_POSITION_TYPE = [{ positionType: 'infeed' }, requestId: 'request_abcd1234', - transactionId: 'trans_abcd1234' + ortb2Imp: { + ext: { + tid: 'trans_abcd1234', + } + }, }]; const BID_RESPONSE_DISPLAY = { @@ -92,7 +104,11 @@ const VIDEO_INSTREAM_REQUEST = [{ bidfloor: 2.50 }, requestId: 'request_abcd1234', - transactionId: 'trans_abcd1234' + ortb2Imp: { + ext: { + tid: 'trans_abcd1234', + } + } }]; const BID_RESPONSE_VIDEO_INSTREAM = { @@ -124,7 +140,11 @@ const VIDEO_OUTSTREAM_REQUEST = [{ bidfloor: 2.50 }, requestId: 'request_abcd1234', - transactionId: 'trans_abcd1234' + ortb2Imp: { + ext: { + tid: 'trans_abcd1234', + } + } }]; const BID_RESPONSE_VIDEO_OUTSTREAM = { @@ -224,7 +244,6 @@ describe('smilewantedBidAdapterTests', function () { describe('gdpr tests', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); }); it('SmileWanted - Verify build request with GDPR', function () { diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 831d3ae0315..c84013d1963 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -170,6 +170,19 @@ describe('sovrnBidAdapter', function() { expect(payload.site.domain).to.equal('example.com') }) + it('sets correct timeout', function() { + const bidderRequest = { + ...baseBidderRequest, + bidderCode: 'sovrn', + auctionId: '1d1a030790a475', + bidderRequestId: '22edbae2733bf6', + timeout: 3000, + bids: [baseBidRequest] + } + const payload = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) + expect(payload.tmax).to.equal(3000) + }) + it('includes the ad unit code in the request', function() { const impression = payload.imp[0] expect(impression.adunitcode).to.equal('adunit-code') diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index 8c9bbe3b336..a95f08314b5 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -611,14 +611,14 @@ describe('SSPBC adapter', function () { expect(result.length).to.equal(bids.length); expect(resultSingle.length).to.equal(1); - expect(resultSingle[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'bidderCode', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls'); + expect(resultSingle[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls'); }); it('should create bid from OneCode (parameter-less) request, if response contains siteId', function () { let resultOneCode = spec.interpretResponse(serverResponseOneCode, requestOneCode); expect(resultOneCode.length).to.equal(1); - expect(resultOneCode[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'bidderCode', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls'); + expect(resultOneCode[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls'); }); it('should not create bid from OneCode (parameter-less) request, if response does not contain siteId', function () { @@ -649,7 +649,7 @@ describe('SSPBC adapter', function () { expect(resultVideo.length).to.equal(1); let videoBid = resultVideo[0]; - expect(videoBid).to.have.keys('adType', 'bidderCode', 'cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'vastContent', 'vastXml', 'vastUrl', 'vurls'); + expect(videoBid).to.have.keys('adType', 'cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'vastContent', 'vastXml', 'vastUrl', 'vurls'); expect(videoBid.adType).to.equal('instream'); expect(videoBid.mediaType).to.equal('video'); expect(videoBid.vastXml).to.match(/^<\?xml.*<\/VAST>$/); @@ -663,7 +663,7 @@ describe('SSPBC adapter', function () { expect(resultNative.length).to.equal(1); let nativeBid = resultNative[0]; - expect(nativeBid).to.have.keys('bidderCode', 'cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'native', 'vurls'); + expect(nativeBid).to.have.keys('cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'native', 'vurls'); expect(nativeBid.native).to.have.keys('image', 'icon', 'title', 'sponsoredBy', 'body', 'clickUrl', 'impressionTrackers', 'javascriptTrackers', 'clickTrackers'); }); }); diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index c2806317ee6..55d79804a38 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -527,14 +527,16 @@ describe('stroeerCore bid adapter', function () { 'bid': 'bid8', 'viz': true, 'ban': { - 'siz': [[300, 600], [160, 60]] - } + 'siz': [[300, 600], [160, 60]], + 'fp': undefined + }, }, { 'sid': 'ABC=', 'bid': 'bid12', 'ban': { - 'siz': [[100, 200], [300, 500]] + 'siz': [[100, 200], [300, 500]], + 'fp': undefined }, 'viz': undefined } @@ -548,7 +550,8 @@ describe('stroeerCore bid adapter', function () { 'vid': { 'ctx': 'instream', 'siz': [640, 480], - 'mim': ['video/mp4', 'video/quicktime'] + 'mim': ['video/mp4', 'video/quicktime'], + 'fp': undefined } } ]; @@ -589,7 +592,8 @@ describe('stroeerCore bid adapter', function () { 'bid': 'bid3', 'viz': true, 'ban': { - 'siz': [[100, 200], [300, 500]] + 'siz': [[100, 200], [300, 500]], + 'fp': undefined } } ]; @@ -602,7 +606,8 @@ describe('stroeerCore bid adapter', function () { 'vid': { 'ctx': 'instream', 'siz': [640, 480], - 'mim': ['video/mp4', 'video/quicktime'] + 'mim': ['video/mp4', 'video/quicktime'], + 'fp': undefined } } ]; @@ -696,6 +701,146 @@ describe('stroeerCore bid adapter', function () { const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); assert.deepEqual(serverRequestInfo.data.schain, schain); }); + + it('should add floor info to banner bid request if floor is available', () => { + const bidReq = buildBidderRequest(); + + const getFloorStub1 = sinon.stub(); + const getFloorStub2 = sinon.stub(); + + getFloorStub1 + .returns({}) + .withArgs({currency: 'EUR', mediaType: BANNER, size: '*'}) + .returns({currency: 'TRY', floor: 0.7}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: [300, 600]}) + .returns({currency: 'TRY', floor: 1.3}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: [160, 60]}) + .returns({currency: 'TRY', floor: 2.5}) + + getFloorStub2 + .returns({}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: '*'}) + .returns({currency: 'USD', floor: 1.2}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: [728, 90]}) + .returns({currency: 'USD', floor: 1.85}) + + bidReq.bids[0].getFloor = getFloorStub1; + bidReq.bids[1].getFloor = getFloorStub2; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const firstBid = serverRequestBids[0]; + const secondBid = serverRequestBids[1]; + + assert.nestedPropertyVal(firstBid, 'ban.fp.def', 0.7); + assert.nestedPropertyVal(firstBid, 'ban.fp.cur', 'TRY'); + assert.deepNestedPropertyVal(firstBid, 'ban.fp.siz', [{w: 300, h: 600, p: 1.3}, {w: 160, h: 60, p: 2.5}]); + + assert.isTrue(getFloorStub1.calledThrice); + + assert.nestedPropertyVal(secondBid, 'ban.fp.def', 1.2); + assert.nestedPropertyVal(secondBid, 'ban.fp.cur', 'USD'); + assert.deepNestedPropertyVal(secondBid, 'ban.fp.siz', [{w: 728, h: 90, p: 1.85}]); + + assert.isTrue(getFloorStub2.calledTwice); + }); + + it('should add floor info to video bid request if floor is available', () => { + const bidReq = buildBidderRequest(); + + const getFloorStub1 = sinon.stub(); + const getFloorStub2 = sinon.stub(); + + getFloorStub1 + .returns({}) + .withArgs({currency: 'EUR', mediaType: 'video', size: '*'}) + .returns({currency: 'NZD', floor: 3.25}) + .withArgs({currency: 'EUR', mediaType: 'video', size: [640, 480]}) + .returns({currency: 'NZD', floor: 4.10}); + + getFloorStub2 + .returns({}) + .withArgs({currency: 'EUR', mediaType: 'video', size: '*'}) + .returns({currency: 'GBP', floor: 4.75}) + .withArgs({currency: 'EUR', mediaType: 'video', size: [1280, 720]}) + .returns({currency: 'GBP', floor: 6.50}) + + delete bidReq.bids[0].mediaTypes.banner; + bidReq.bids[0].mediaTypes.video = { + playerSize: [640, 480], + context: 'instream' + }; + + delete bidReq.bids[1].mediaTypes.banner; + bidReq.bids[1].mediaTypes.video = { + playerSize: [1280, 720], + context: 'outstream' + }; + + bidReq.bids[0].getFloor = getFloorStub1; + bidReq.bids[1].getFloor = getFloorStub2; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const firstBid = serverRequestBids[0]; + const secondBid = serverRequestBids[1]; + + assert.nestedPropertyVal(firstBid, 'vid.fp.def', 3.25); + assert.nestedPropertyVal(firstBid, 'vid.fp.cur', 'NZD'); + assert.deepNestedPropertyVal(firstBid, 'vid.fp.siz', [{w: 640, h: 480, p: 4.10}]); + + assert.isTrue(getFloorStub1.calledTwice); + + assert.nestedPropertyVal(secondBid, 'vid.fp.def', 4.75); + assert.nestedPropertyVal(secondBid, 'vid.fp.cur', 'GBP'); + assert.deepNestedPropertyVal(secondBid, 'vid.fp.siz', [{w: 1280, h: 720, p: 6.50}]); + + assert.isTrue(getFloorStub2.calledTwice); + }); + + it('should not add floor info to bid request if floor is unavailable', () => { + const bidReq = buildBidderRequest(); + const getFloorSpy = sinon.spy(() => ({})); + + delete bidReq.bids[0].getFloor; + bidReq.bids[1].getFloor = getFloorSpy; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const firstBid = serverRequestBids[0]; + const secondBid = serverRequestBids[1]; + + assert.nestedPropertyVal(firstBid, 'ban.fp', undefined); + assert.nestedPropertyVal(secondBid, 'ban.fp', undefined); + + assert.isTrue(getFloorSpy.calledWith({currency: 'EUR', mediaType: 'banner', size: '*'})); + assert.isTrue(getFloorSpy.calledWith({currency: 'EUR', mediaType: 'banner', size: [728, 90]})); + assert.isTrue(getFloorSpy.calledTwice); + }); + + it('should not add floor info for a size when it is the same as the default', () => { + const bidReq = buildBidderRequest(); + const getFloorStub = sinon.stub(); + + getFloorStub + .returns({currency: 'EUR', floor: 1.9}) + .withArgs({currency: 'EUR', mediaType: BANNER, size: [160, 60]}) + .returns({currency: 'EUR', floor: 2.7}); + + bidReq.bids[0].getFloor = getFloorStub; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const bid = serverRequestBids[0]; + + assert.nestedPropertyVal(bid, 'ban.fp.def', 1.9); + assert.nestedPropertyVal(bid, 'ban.fp.cur', 'EUR'); + assert.deepNestedPropertyVal(bid, 'ban.fp.siz', [{w: 160, h: 60, p: 2.7}]); + }); }); }); }); diff --git a/test/spec/modules/stvBidAdapter_spec.js b/test/spec/modules/stvBidAdapter_spec.js index d095fd3cf55..41f29cced34 100644 --- a/test/spec/modules/stvBidAdapter_spec.js +++ b/test/spec/modules/stvBidAdapter_spec.js @@ -60,6 +60,18 @@ describe('stvAdapter', function() { 'bidderRequestId': '22edbae2733bf61', 'auctionId': '1d1a030790a475', 'adUnitCode': 'testDiv1', + 'schain': { + 'ver': '1.0', + 'complete': 0, + 'nodes': [ + { + 'asi': 'reseller.com', + 'sid': 'aaaaa', + 'rid': 'BidRequest4', + 'hp': 1 + } + ] + } }, { 'bidder': 'stv', @@ -169,7 +181,7 @@ describe('stvAdapter', function() { expect(request1.method).to.equal('GET'); expect(request1.url).to.equal(ENDPOINT_URL); let data = request1.data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid').replace(/pbver=.*?&/g, 'pbver=test&'); - expect(data).to.equal('_f=html&alternative=prebid_js&_ps=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pbver=test&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&bcat=IAB2%2CIAB4&dvt=desktop&pbcode=testDiv1&media_types%5Bbanner%5D=300x250'); + expect(data).to.equal('_f=html&alternative=prebid_js&_ps=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pbver=test&schain=1.0,0!reseller.com,aaaaa,1,BidRequest4,,,&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&bcat=IAB2%2CIAB4&dvt=desktop&pbcode=testDiv1&media_types%5Bbanner%5D=300x250'); }); var request2 = spec.buildRequests([bidRequests[1]], bidderRequest)[0]; diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 206a0142043..7d31e291667 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -5,14 +5,15 @@ import * as utils from '../../../src/utils' import {server} from '../../mocks/xhr' describe('Taboola Adapter', function () { - let hasLocalStorage, cookiesAreEnabled, getDataFromLocalStorage, localStorageIsEnabled, getCookie, commonBidRequest; + let sandbox, hasLocalStorage, cookiesAreEnabled, getDataFromLocalStorage, localStorageIsEnabled, getCookie, commonBidRequest; beforeEach(() => { - hasLocalStorage = sinon.stub(userData.storageManager, 'hasLocalStorage'); - cookiesAreEnabled = sinon.stub(userData.storageManager, 'cookiesAreEnabled'); - getCookie = sinon.stub(userData.storageManager, 'getCookie'); - getDataFromLocalStorage = sinon.stub(userData.storageManager, 'getDataFromLocalStorage'); - localStorageIsEnabled = sinon.stub(userData.storageManager, 'localStorageIsEnabled'); + sandbox = sinon.sandbox.create(); + hasLocalStorage = sandbox.stub(userData.storageManager, 'hasLocalStorage'); + cookiesAreEnabled = sandbox.stub(userData.storageManager, 'cookiesAreEnabled'); + getCookie = sandbox.stub(userData.storageManager, 'getCookie'); + getDataFromLocalStorage = sandbox.stub(userData.storageManager, 'getDataFromLocalStorage'); + localStorageIsEnabled = sandbox.stub(userData.storageManager, 'localStorageIsEnabled'); commonBidRequest = createBidRequest(); $$PREBID_GLOBAL$$.bidderSettings = { taboola: { @@ -22,12 +23,7 @@ describe('Taboola Adapter', function () { }); afterEach(() => { - hasLocalStorage.restore(); - cookiesAreEnabled.restore(); - getCookie.restore(); - getDataFromLocalStorage.restore(); - localStorageIsEnabled.restore(); - + sandbox.restore(); $$PREBID_GLOBAL$$.bidderSettings = {}; }) @@ -124,6 +120,7 @@ describe('Taboola Adapter', function () { } const commonBidderRequest = { + bidderRequestId: 'mock-uuid', refererInfo: { page: 'https://example.com/ref', ref: 'https://ref', @@ -133,6 +130,7 @@ describe('Taboola Adapter', function () { it('should build display request', function () { const expectedData = { + id: 'mock-uuid', 'imp': [{ 'id': 1, 'banner': { diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index e981d80c8ae..a4b94135bad 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -121,6 +121,13 @@ describe('teadsBidAdapter', () => { expect(request.method).to.equal('POST'); }); + it('should not send auctionId in bid request ', function() { + const request = spec.buildRequests(bidRequests, bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload.data[0].auctionId).to.not.exist + }); + it('should send US Privacy to endpoint', function() { let usPrivacy = 'OHHHFCP1' let bidderRequest = { diff --git a/test/spec/modules/terceptAnalyticsAdapter_spec.js b/test/spec/modules/terceptAnalyticsAdapter_spec.js index 594e1e5f5b4..a1384bfd919 100644 --- a/test/spec/modules/terceptAnalyticsAdapter_spec.js +++ b/test/spec/modules/terceptAnalyticsAdapter_spec.js @@ -709,7 +709,6 @@ describe('tercept analytics adapter', function () { 'bidsReceived': [], 'winningBids': [], 'timeout': 1000, - 'config': initOptions }, 'initOptions': initOptions }; diff --git a/test/spec/modules/trustpidSystem_spec.js b/test/spec/modules/trustpidSystem_spec.js deleted file mode 100644 index 38e861d977f..00000000000 --- a/test/spec/modules/trustpidSystem_spec.js +++ /dev/null @@ -1,237 +0,0 @@ -import { expect } from 'chai'; -import { trustpidSubmodule } from 'modules/trustpidSystem.js'; -import { storage } from 'modules/trustpidSystem.js'; - -describe('trustpid System', () => { - const connectDataKey = 'fcIdConnectData'; - const connectDomainKey = 'fcIdConnectDomain'; - - const getStorageData = (idGraph) => { - if (!idGraph) { - idGraph = {id: 501, domain: ''}; - } - return { - 'connectId': { - 'idGraph': [idGraph], - } - } - }; - - it('should have the correct module name declared', () => { - expect(trustpidSubmodule.name).to.equal('trustpid'); - }); - - describe('trustpid getId()', () => { - afterEach(() => { - storage.removeDataFromLocalStorage(connectDataKey); - storage.removeDataFromLocalStorage(connectDomainKey); - }); - - after(() => { - window.FC_CONF = {}; - }) - - it('it should return object with key callback', () => { - expect(trustpidSubmodule.getId()).to.have.property('callback'); - }); - - it('should return object with key callback with value type - function', () => { - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData())); - expect(trustpidSubmodule.getId()).to.have.property('callback'); - expect(typeof trustpidSubmodule.getId().callback).to.be.equal('function'); - }); - - it('tests if localstorage & JSON works properly ', () => { - const idGraph = { - 'domain': 'domainValue', - 'atid': 'atidValue', - }; - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - expect(JSON.parse(storage.getDataFromLocalStorage(connectDataKey))).to.have.property('connectId'); - }); - - it('returns {callback: func} if domains don\'t match', () => { - const idGraph = { - 'domain': 'domainValue', - 'atid': 'atidValue', - }; - storage.setDataInLocalStorage(connectDomainKey, JSON.stringify('differentDomainValue')); - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - expect(trustpidSubmodule.getId()).to.have.property('callback'); - }); - - it('returns {id: {trustpid: data.trustpid}} if we have the right data stored in the localstorage ', () => { - const idGraph = { - 'domain': 'test.domain', - 'atid': 'atidValue', - }; - storage.setDataInLocalStorage(connectDomainKey, JSON.stringify('test.domain')); - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - const response = trustpidSubmodule.getId(); - expect(response).to.have.property('id'); - expect(response.id).to.have.property('trustpid'); - expect(response.id.trustpid).to.be.equal('atidValue'); - }); - - it('returns {trustpid: data.trustpid} if we have the right data stored in the localstorage right after the callback is called', (done) => { - const idGraph = { - 'domain': 'test.domain', - 'atid': 'atidValue', - }; - const response = trustpidSubmodule.getId(); - expect(response).to.have.property('callback'); - expect(response.callback.toString()).contain('result(callback)'); - - if (typeof response.callback === 'function') { - storage.setDataInLocalStorage(connectDomainKey, JSON.stringify('test.domain')); - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - response.callback(function (result) { - expect(result).to.not.be.null; - expect(result).to.have.property('trustpid'); - expect(result.trustpid).to.be.equal('atidValue'); - done() - }) - } - }); - - it('returns null if domains don\'t match', (done) => { - const idGraph = { - 'domain': 'test.domain', - 'atid': 'atidValue', - }; - storage.setDataInLocalStorage(connectDomainKey, JSON.stringify('differentDomainValue')); - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - - const response = trustpidSubmodule.getId(); - expect(response).to.have.property('callback'); - expect(response.callback.toString()).contain('result(callback)'); - - if (typeof response.callback === 'function') { - setTimeout(() => { - expect(JSON.parse(storage.getDataFromLocalStorage(connectDomainKey))).to.be.equal('differentDomainValue'); - }, 100) - response.callback(function (result) { - expect(result).to.be.null; - done() - }) - } - }); - - it('returns {trustpid: data.trustpid} if we have the right data stored in the localstorage right after 500ms delay', (done) => { - const idGraph = { - 'domain': 'test.domain', - 'atid': 'atidValue', - }; - - const response = trustpidSubmodule.getId(); - expect(response).to.have.property('callback'); - expect(response.callback.toString()).contain('result(callback)'); - - if (typeof response.callback === 'function') { - setTimeout(() => { - storage.setDataInLocalStorage(connectDomainKey, JSON.stringify('test.domain')); - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - }, 500); - response.callback(function (result) { - expect(result).to.not.be.null; - expect(result).to.have.property('trustpid'); - expect(result.trustpid).to.be.equal('atidValue'); - done() - }) - } - }); - - it('returns null if we have the data stored in the localstorage after 500ms delay and the max (waiting) delay is only 200ms ', (done) => { - const idGraph = { - 'domain': 'test.domain', - 'atid': 'atidValue', - }; - - const response = trustpidSubmodule.getId({params: {maxDelayTime: 200}}); - expect(response).to.have.property('callback'); - expect(response.callback.toString()).contain('result(callback)'); - - if (typeof response.callback === 'function') { - setTimeout(() => { - storage.setDataInLocalStorage(connectDomainKey, JSON.stringify('test.domain')); - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - }, 500); - response.callback(function (result) { - expect(result).to.be.null; - done() - }) - } - }); - }); - - describe('trustpid decode()', () => { - const VALID_API_RESPONSES = [ - { - expected: '32a97f612', - payload: { - trustpid: '32a97f612' - } - }, - { - expected: '32a97f61', - payload: { - trustpid: '32a97f61', - } - }, - ]; - VALID_API_RESPONSES.forEach(responseData => { - it('should return a newly constructed object with the trustpid for a payload with {trustpid: value}', () => { - expect(trustpidSubmodule.decode(responseData.payload)).to.deep.equal( - {trustpid: responseData.expected} - ); - }); - }); - - [{}, '', {foo: 'bar'}].forEach((response) => { - it(`should return null for an invalid response "${JSON.stringify(response)}"`, () => { - expect(trustpidSubmodule.decode(response)).to.be.null; - }); - }); - }); - - describe('trustpid messageHandler', () => { - afterEach(() => { - storage.removeDataFromLocalStorage(connectDataKey); - storage.removeDataFromLocalStorage(connectDomainKey); - }); - - after(() => { - window.FC_CONF = {}; - }) - - const domains = [ - 'domain1', - 'domain2', - 'domain3', - ]; - - domains.forEach(domain => { - it(`correctly sets trustpid value for domain name ${domain}`, (done) => { - const idGraph = { - 'domain': domain, - 'atid': 'atidValue', - }; - - storage.setDataInLocalStorage(connectDomainKey, JSON.stringify(domain)); - storage.setDataInLocalStorage(connectDataKey, JSON.stringify(getStorageData(idGraph))); - - const eventData = { - data: `{\"msgType\":\"MNOSELECTOR\",\"body\":{\"url\":\"https://${domain}/some/path\"}}` - }; - - window.dispatchEvent(new MessageEvent('message', eventData)); - - const response = trustpidSubmodule.getId(); - expect(response).to.have.property('id'); - expect(response.id).to.have.property('trustpid'); - expect(response.id.trustpid).to.be.equal('atidValue'); - done(); - }); - }); - }); -}); diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index 5e472543a01..56c506dea6b 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -223,7 +223,11 @@ describe('ttdBidAdapter', function () { const baseBidderRequestReferer = detectReferer(testWindow)(); const baseBidderRequest = { 'bidderCode': 'ttd', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + ortb2: { + source: { + tid: 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + } + }, 'bidderRequestId': '18084284054531', 'auctionStart': 1540945362095, 'timeout': 3000, @@ -320,10 +324,10 @@ describe('ttdBidAdapter', function () { expect(requestBody.imp[0].rwdd).to.equal(1); }); - it('sends auction id in source.tid', function () { + it('sends source.tid', function () { const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.source).to.be.not.null; - expect(requestBody.source.tid).to.equal(baseBidderRequest.auctionId); + expect(requestBody.source.tid).to.equal(baseBidderRequest.ortb2.source.tid); }); it('includes the ad size in the bid request', function () { @@ -756,7 +760,11 @@ describe('ttdBidAdapter', function () { const baseBidderRequest = { 'bidderCode': 'ttd', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + ortb2: { + source: { + tid: 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + } + }, 'bidderRequestId': '18084284054531', 'auctionStart': 1540945362095, 'timeout': 3000, @@ -776,7 +784,7 @@ describe('ttdBidAdapter', function () { const requestBody = testBuildRequests(baseBannerMultipleBidRequests, baseBidderRequest).data; expect(requestBody.imp.length).to.equal(2); expect(requestBody.source).to.be.not.null; - expect(requestBody.source.tid).to.equal(baseBidderRequest.auctionId); + expect(requestBody.source.tid).to.equal(baseBidderRequest.ortb2.source.tid); expect(requestBody.imp[0].ext).to.be.not.null; expect(requestBody.imp[0].ext.tid).to.equal('8651474f-58b1-4368-b812-84f8c937a099'); expect(requestBody.imp[1].ext).to.be.not.null; diff --git a/test/spec/modules/uid2IdSystem_helpers.js b/test/spec/modules/uid2IdSystem_helpers.js new file mode 100644 index 00000000000..65d52c1d7c3 --- /dev/null +++ b/test/spec/modules/uid2IdSystem_helpers.js @@ -0,0 +1,70 @@ +import { setConsentConfig } from 'modules/consentManagement.js'; +import { server } from 'test/mocks/xhr.js'; +import {coreStorage, init, setSubmoduleRegistry, requestBidsHook} from 'modules/userId/index.js'; + +const msIn12Hours = 60 * 60 * 12 * 1000; +const expireCookieDate = 'Thu, 01 Jan 1970 00:00:01 GMT'; + +export const cookieHelpers = { + getFutureCookieExpiry: () => new Date(Date.now() + msIn12Hours).toUTCString(), + setPublisherCookie: (cookieName, token) => coreStorage.setCookie(cookieName, JSON.stringify(token), cookieHelpers.getFutureCookieExpiry()), + clearCookies: (...cookieNames) => cookieNames.forEach(cookieName => coreStorage.setCookie(cookieName, '', expireCookieDate)), +} + +export const runAuction = async () => { + const adUnits = [{ + code: 'adUnit-code', + mediaTypes: {banner: {}, native: {}}, + sizes: [[300, 200], [300, 600]], + bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] + }]; + return new Promise(function(resolve) { + requestBidsHook(function() { + resolve(adUnits[0].bids[0]); + }, {adUnits}); + }); +} + +export const apiHelpers = { + makeTokenResponse: (token, shouldRefresh = false, expired = false) => ({ + advertising_token: token, + refresh_token: 'fake-refresh-token', + identity_expires: expired ? Date.now() - 1000 : Date.now() + 60 * 60 * 1000, + refresh_from: shouldRefresh ? Date.now() - 1000 : Date.now() + 60 * 1000, + refresh_expires: Date.now() + 24 * 60 * 60 * 1000, // 24 hours + refresh_response_key: 'wR5t6HKMfJ2r4J7fEGX9Gw==', // Fake data + }), + respondAfterDelay: (delay) => new Promise((resolve) => setTimeout(() => { + server.respond(); + setTimeout(() => resolve()); + }, delay)), +} + +export const setGdprApplies = (consent = false) => { + const consentDetails = consent ? { + tcString: 'CPhJRpMPhJRpMABAMBFRACBoALAAAEJAAIYgAKwAQAKgArABAAqAAA', + purpose: { + consents: { + '1': true, + }, + }, + vendor: { + consents: { + '21': true, + }, + } + + } : { + tcString: 'CPhJRpMPhJRpMABAMBFRACBoALAAAEJAAIYgAKwAQAKgArABAAqAAA' + }; + const staticConfig = { + cmpApi: 'static', + timeout: 7500, + consentData: { + gdprApplies: true, + ...consentDetails + } + } + setConsentConfig(staticConfig); + return staticConfig; +} diff --git a/test/spec/modules/uid2IdSystem_spec.js b/test/spec/modules/uid2IdSystem_spec.js index b33e8cda501..20a38a292bb 100644 --- a/test/spec/modules/uid2IdSystem_spec.js +++ b/test/spec/modules/uid2IdSystem_spec.js @@ -1,11 +1,15 @@ +/* eslint-disable no-console */ + import {coreStorage, init, setSubmoduleRegistry, requestBidsHook} from 'modules/userId/index.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import { uid2IdSubmodule } from 'modules/uid2IdSystem.js'; import 'src/prebid.js'; +import 'modules/consentManagement.js'; import { getGlobal } from 'src/prebidGlobal.js'; import { server } from 'test/mocks/xhr.js'; import { configureTimerInterceptors } from 'test/mocks/timers.js'; +import { cookieHelpers, runAuction, apiHelpers, setGdprApplies } from './uid2IdSystem_helpers.js'; import {hook} from 'src/hook.js'; import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; @@ -14,72 +18,48 @@ let expect = require('chai').expect; const clearTimersAfterEachTest = true; const debugOutput = () => {}; -const expireCookieDate = 'Thu, 01 Jan 1970 00:00:01 GMT'; -const msIn12Hours = 60 * 60 * 12 * 1000; const moduleCookieName = '__uid2_advertising_token'; const publisherCookieName = '__UID2_SERVER_COOKIE'; const auctionDelayMs = 10; -const legacyConfigParams = null; -const serverCookieConfigParams = { uid2ServerCookie: publisherCookieName } -const getFutureCookieExpiry = () => new Date(Date.now() + msIn12Hours).toUTCString(); -const setPublisherCookie = (token) => coreStorage.setCookie(publisherCookieName, JSON.stringify(token), getFutureCookieExpiry()); - -const makePrebidIdentityContainer = (token) => ({uid2: {id: token}}); -const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ - userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'uid2', params}] }, debug, ...extraSettings -}); - const initialToken = `initial-advertising-token`; const legacyToken = 'legacy-advertising-token'; const refreshedToken = 'refreshed-advertising-token'; -const makeUid2Token = (token = initialToken, shouldRefresh = false, expired = false) => ({ - advertising_token: token, - refresh_token: 'fake-refresh-token', - identity_expires: expired ? Date.now() - 1000 : Date.now() + 60 * 60 * 1000, - refresh_from: shouldRefresh ? Date.now() - 1000 : Date.now() + 60 * 1000, - refresh_expires: Date.now() + 24 * 60 * 60 * 1000, // 24 hours - refresh_response_key: 'wR5t6HKMfJ2r4J7fEGX9Gw==', + +const legacyConfigParams = {storage: null}; +const serverCookieConfigParams = { uid2ServerCookie: publisherCookieName }; +const newServerCookieConfigParams = { uid2Cookie: publisherCookieName }; + +const makeUid2IdentityContainer = (token) => ({uid2: {id: token}}); +let useLocalStorage = false; +const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ + userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'uid2', params: {storage: useLocalStorage ? 'localStorage' : 'cookie', ...params}}] }, debug, ...extraSettings }); -const expectInitialToken = (bid) => expect(bid?.userId ?? {}).to.deep.include(makePrebidIdentityContainer(initialToken)); -const expectRefreshedToken = (bid) => expect(bid?.userId ?? {}).to.deep.include(makePrebidIdentityContainer(refreshedToken)); + +const getFromAppropriateStorage = () => { + if (useLocalStorage) return coreStorage.getDataFromLocalStorage(moduleCookieName); + else return coreStorage.getCookie(moduleCookieName); +} + +const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeUid2IdentityContainer(token)); +const expectLegacyToken = (bid) => expect(bid.userId).to.deep.include(makeUid2IdentityContainer(legacyToken)); const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId'); -const expectGlobalToHaveRefreshedIdentity = () => expect(getGlobal().getUserIds()).to.deep.include(makePrebidIdentityContainer(refreshedToken)); +const expectGlobalToHaveToken = (token) => expect(getGlobal().getUserIds()).to.deep.include(makeUid2IdentityContainer(token)); const expectGlobalToHaveNoUid2 = () => expect(getGlobal().getUserIds()).to.not.haveOwnProperty('uid2'); -const expectLegacyToken = (bid) => expect(bid.userId).to.deep.include(makePrebidIdentityContainer(legacyToken)); -const expectNoLegacyToken = (bid) => expect(bid.userId).to.not.deep.include(makePrebidIdentityContainer(legacyToken)); -const expectModuleCookieEmptyOrMissing = () => expect(coreStorage.getCookie(moduleCookieName)).to.be.null; -const expectModuleCookieToContain = (initialIdentity, latestIdentity) => { - const cookie = JSON.parse(coreStorage.getCookie(moduleCookieName)); +const expectNoLegacyToken = (bid) => expect(bid.userId).to.not.deep.include(makeUid2IdentityContainer(legacyToken)); +const expectModuleStorageEmptyOrMissing = () => expect(getFromAppropriateStorage()).to.be.null; +const expectModuleStorageToContain = (initialIdentity, latestIdentity) => { + const cookie = JSON.parse(getFromAppropriateStorage()); if (initialIdentity) expect(cookie.originalToken.advertising_token).to.equal(initialIdentity); if (latestIdentity) expect(cookie.latestToken.advertising_token).to.equal(latestIdentity); } const apiUrl = 'https://prod.uidapi.com/v2/token/refresh'; const headers = { 'Content-Type': 'application/json' }; -const makeSuccessResponseBody = () => btoa(JSON.stringify({ status: 'success', body: { ...makeUid2Token(), advertising_token: refreshedToken } })); +const makeSuccessResponseBody = () => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: refreshedToken } })); const configureUid2Response = (httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); const configureUid2ApiSuccessResponse = () => configureUid2Response(200, makeSuccessResponseBody()); const configureUid2ApiFailResponse = () => configureUid2Response(500, 'Error'); -const respondAfterDelay = (delay) => new Promise((resolve) => setTimeout(() => { - server.respond(); - setTimeout(() => resolve()); -}, delay)); - -const runAuction = async () => { - const adUnits = [{ - code: 'adUnit-code', - mediaTypes: {banner: {}, native: {}}, - sizes: [[300, 200], [300, 600]], - bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] - }]; - return new Promise(function(resolve) { - requestBidsHook(function() { - resolve(adUnits[0].bids[0]); - }, {adUnits}); - }); -} - // Runs the provided test twice - once with a successful API mock, once with one which returns a server error const testApiSuccessAndFailure = (act, testDescription, failTestDescription, only = false) => { const testFn = only ? it.only : it; @@ -92,6 +72,26 @@ const testApiSuccessAndFailure = (act, testDescription, failTestDescription, onl await act(false); }); } + +const testCookieAndLocalStorage = (description, test, only = false) => { + const describeFn = only ? describe.only : describe; + describeFn(`Using cookies: ${description}`, async function() { + before(function() { + useLocalStorage = false; + }); + await test(); + }); + describeFn(`Using local storage: ${description}`, async function() { + before(function() { + useLocalStorage = true; + }); + after(function() { + useLocalStorage = false; + }); + await test(); + }); +}; + describe(`UID2 module`, function () { let suiteSandbox, testSandbox, timerSpy, fullTestTitle, restoreSubtleToUndefined = false; before(function () { @@ -141,21 +141,21 @@ describe(`UID2 module`, function () { await timerSpy.waitAllActiveTimers(); } } - coreStorage.setCookie(moduleCookieName, '', expireCookieDate); - coreStorage.setCookie(publisherCookieName, '', expireCookieDate); + cookieHelpers.clearCookies(moduleCookieName, publisherCookieName); + coreStorage.removeDataFromLocalStorage(moduleCookieName); debugOutput('----------------- END TEST ------------------'); }); describe('Configuration', function() { it('When no baseUrl is provided in config, the module calls the production endpoint', function() { - const uid2Token = makeUid2Token(initialToken, true, true); + const uid2Token = apiHelpers.makeTokenResponse(initialToken, true, true); config.setConfig(makePrebidConfig({uid2Token})); expect(server.requests[0]?.url).to.have.string('https://prod.uidapi.com/'); }); it('When a baseUrl is provided in config, the module calls the provided endpoint', function() { - const uid2Token = makeUid2Token(initialToken, true, true); + const uid2Token = apiHelpers.makeTokenResponse(initialToken, true, true); config.setConfig(makePrebidConfig({uid2Token, uid2ApiBase: 'https://operator-integ.uidapi.com'})); expect(server.requests[0]?.url).to.have.string('https://operator-integ.uidapi.com/'); }); @@ -175,9 +175,10 @@ describe(`UID2 module`, function () { // This should cover older integrations where the server is setting this legacy cookie and expecting the module to pass it on. describe('When a legacy cookie exists', function () { // Creates a test which sets the legacy cookie, configures the UID2 module with provided params, runs an - const createLegacyTest = function(params, bidAssertions) { + const createLegacyTest = function(params, bidAssertions, addConsent = false) { return async function() { - coreStorage.setCookie(moduleCookieName, legacyToken, getFutureCookieExpiry()); + coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); + if (addConsent) setGdprApplies(); config.setConfig(makePrebidConfig(params)); const bid = await runAuction(); @@ -189,120 +190,154 @@ describe(`UID2 module`, function () { createLegacyTest(legacyConfigParams, [expectLegacyToken])); it('and a server cookie config is used without a valid server cookie, it should provide the legacy cookie', createLegacyTest(serverCookieConfigParams, [expectLegacyToken])); + it('and a server cookie is used with a valid server cookie configured using the new param name, it should provide the server cookie', + async function() { cookieHelpers.setPublisherCookie(publisherCookieName, apiHelpers.makeTokenResponse(initialToken)); await createLegacyTest(serverCookieConfigParams, [(bid) => expectToken(bid, initialToken), expectNoLegacyToken])(); }); it('and a server cookie is used with a valid server cookie, it should provide the server cookie', - async function() { setPublisherCookie(makeUid2Token()); await createLegacyTest(serverCookieConfigParams, [expectInitialToken, expectNoLegacyToken])(); }); + async function() { cookieHelpers.setPublisherCookie(publisherCookieName, apiHelpers.makeTokenResponse(initialToken)); await createLegacyTest(newServerCookieConfigParams, [(bid) => expectToken(bid, initialToken), expectNoLegacyToken])(); }); it('and a token is provided in config, it should provide the config token', - createLegacyTest({uid2Token: makeUid2Token()}, [expectInitialToken, expectNoLegacyToken])); + createLegacyTest({uid2Token: apiHelpers.makeTokenResponse(initialToken)}, [(bid) => expectToken(bid, initialToken), expectNoLegacyToken])); + it('and GDPR applies, no identity should be provided to the auction', + createLegacyTest(legacyConfigParams, [expectNoIdentity], true)); + it('and GDPR applies, when getId is called directly it provides no identity', () => { + coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); + const consentConfig = setGdprApplies(); + let configObj = makePrebidConfig(legacyConfigParams); + const result = uid2IdSubmodule.getId(configObj.userSync.userIds[0], consentConfig.consentData); + expect(result?.id).to.not.exist; + }); + + it('multiple runs do not change the value', async function() { + coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); + config.setConfig(makePrebidConfig(legacyConfigParams)); + + const bid = await runAuction(); + + console.log('Storage', coreStorage.getDataFromLocalStorage(moduleCookieName)); + init(config); + setSubmoduleRegistry([uid2IdSubmodule]); + config.setConfig(makePrebidConfig(legacyConfigParams)); + const bid2 = await runAuction(); + + expect(bid.userId.uid2.id).to.equal(bid2.userId.uid2.id); + }); }); // This setup runs all of the functional tests with both types of config - the full token response in params, or a server cookie with the cookie name provided let scenarios = [ { name: 'Token provided in config call', - setConfig: (token, extraConfig = {}) => config.setConfig(makePrebidConfig({uid2Token: token}, extraConfig)), + setConfig: (token, extraConfig = {}) => { + const gen = makePrebidConfig({uid2Token: token}, extraConfig); + console.log('GENERATED CONFIG', gen.userSync.userIds[0].params); + return config.setConfig(gen); + }, }, { name: 'Token provided in server-set cookie', setConfig: (token, extraConfig) => { - setPublisherCookie(token); + cookieHelpers.setPublisherCookie(publisherCookieName, token); config.setConfig(makePrebidConfig(serverCookieConfigParams, extraConfig)); }, } ] scenarios.forEach(function(scenario) { - describe(scenario.name, function() { + testCookieAndLocalStorage(scenario.name, function() { describe(`When an expired token which can be refreshed is provided`, function() { describe('When the refresh is available in time', function() { testApiSuccessAndFailure(async function(apiSucceeds) { - scenario.setConfig(makeUid2Token(initialToken, true, true)); - respondAfterDelay(auctionDelayMs / 10); + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); + apiHelpers.respondAfterDelay(auctionDelayMs / 10); const bid = await runAuction(); - if (apiSucceeds) expectRefreshedToken(bid); + if (apiSucceeds) expectToken(bid, refreshedToken); else expectNoIdentity(bid); }, 'it should be used in the auction', 'the auction should have no uid2'); testApiSuccessAndFailure(async function(apiSucceeds) { - scenario.setConfig(makeUid2Token(initialToken, true, true)); - respondAfterDelay(auctionDelayMs / 10); + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); + apiHelpers.respondAfterDelay(auctionDelayMs / 10); await runAuction(); if (apiSucceeds) { - expectModuleCookieToContain(initialToken, refreshedToken); + expectModuleStorageToContain(initialToken, refreshedToken); } else { - expectModuleCookieEmptyOrMissing(); + expectModuleStorageEmptyOrMissing(); } - }, 'the refreshed token should be stored in the module cookie', 'the module cookie should not be set'); + }, 'the refreshed token should be stored in the module storage', 'the module storage should not be set'); }); describe(`when the response doesn't arrive before the auction timer`, function() { testApiSuccessAndFailure(async function() { - scenario.setConfig(makeUid2Token(initialToken, true, true)); + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); const bid = await runAuction(); expectNoIdentity(bid); }, 'it should run the auction'); testApiSuccessAndFailure(async function(apiSucceeds) { - scenario.setConfig(makeUid2Token(initialToken, true, true)); - const promise = respondAfterDelay(auctionDelayMs * 2); + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); + const promise = apiHelpers.respondAfterDelay(auctionDelayMs * 2); const bid = await runAuction(); expectNoIdentity(bid); expectGlobalToHaveNoUid2(); await promise; - if (apiSucceeds) expectGlobalToHaveRefreshedIdentity(); + if (apiSucceeds) expectGlobalToHaveToken(refreshedToken); else expectGlobalToHaveNoUid2(); }, 'it should update the userId after the auction', 'there should be no global identity'); }) describe('and there is a refreshed token in the module cookie', function() { it('the refreshed value from the cookie is used', async function() { - const initialIdentity = makeUid2Token(initialToken, true, true); - const refreshedIdentity = makeUid2Token(refreshedToken); + const initialIdentity = apiHelpers.makeTokenResponse(initialToken, true, true); + const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken); const moduleCookie = {originalToken: initialIdentity, latestToken: refreshedIdentity}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), getFutureCookieExpiry()); + coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); scenario.setConfig(initialIdentity); const bid = await runAuction(); - expectRefreshedToken(bid); + expectToken(bid, refreshedToken); }); }) }); describe(`When a current token is provided`, function() { - beforeEach(function() { - scenario.setConfig(makeUid2Token()); - }); - it('it should use the token in the auction', async function() { + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken)); const bid = await runAuction(); - expectInitialToken(bid); + expectToken(bid, initialToken); }); + + it('and GDPR applies, the token should not be used', async function() { + setGdprApplies(); + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken)); + const bid = await runAuction(); + expectNoIdentity(bid); + }) }); describe(`When a current token which should be refreshed is provided, and the auction is set to run immediately`, function() { beforeEach(function() { - scenario.setConfig(makeUid2Token(initialToken, true), {auctionDelay: 0, syncDelay: 1}); + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true), {auctionDelay: 0, syncDelay: 1}); }); testApiSuccessAndFailure(async function() { - respondAfterDelay(10); + apiHelpers.respondAfterDelay(10); const bid = await runAuction(); - expectInitialToken(bid); + expectToken(bid, initialToken); }, 'it should not be refreshed before the auction runs'); testApiSuccessAndFailure(async function(success) { - const promise = respondAfterDelay(1); + const promise = apiHelpers.respondAfterDelay(1); await runAuction(); await promise; if (success) { - expectModuleCookieToContain(initialToken, refreshedToken); + expectModuleStorageToContain(initialToken, refreshedToken); } else { - expectModuleCookieToContain(initialToken, initialToken); + expectModuleStorageToContain(initialToken, initialToken); } }, 'the refreshed token should be stored in the module cookie after the auction runs', 'the module cookie should only have the original token'); it('it should use the current token in the auction', async function() { const bid = await runAuction(); - expectInitialToken(bid); + expectToken(bid, initialToken); }); }); }); diff --git a/test/spec/modules/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index 040d85f9179..2d7c1f11178 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -967,7 +967,6 @@ describe('UnderdogMedia adapter', function () { expect(bids).to.be.lengthOf(2); expect(bids[0].meta.advertiserDomains).to.deep.equal(['domain1']) - expect(bids[0].bidderCode).to.equal('underdogmedia'); expect(bids[0].cpm).to.equal(2.5); expect(bids[0].width).to.equal('160'); expect(bids[0].height).to.equal('600'); diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index 8766307b3bd..7f9c2e7b3d5 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -41,7 +41,9 @@ const videoBidReq = [{ }, mediaTypes: {video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + placement: 1, + plcmt: 1 }}, sizes: [[300, 250], [300, 600]], bidId: '263be71e91dd9d', @@ -184,6 +186,31 @@ const bidderReqCcpaAndGdpr = { uspConsent: 'NY12' }; +const bidderReqGpp = { + refererInfo: { + topmostLocation: 'http://prebid.org/dev-docs/bidder-adaptor.html' + }, + gppConsent: { + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + applicableSections: [7] + } +}; + +const bidderReqFullGppCcpaGdpr = { + refererInfo: { + topmostLocation: 'http://prebid.org/dev-docs/bidder-adaptor.html' + }, + gppConsent: { + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + applicableSections: [7] + }, + gdprConsent: { + gdprApplies: true, + consentString: 'gdprConsent' + }, + uspConsent: '1YNN' +}; + const validBidRes = { ad: '
Hello
', publisherId: 12345, @@ -381,6 +408,31 @@ describe('Undertone Adapter', () => { expect(request.url).to.equal(REQ_URL); expect(request.method).to.equal('POST'); }); + it(`should have gppConsent fields`, function () { + const request = spec.buildRequests(bidReq, bidderReqGpp); + const domainStart = bidderReq.refererInfo.topmostLocation.indexOf('//'); + const domainEnd = bidderReq.refererInfo.topmostLocation.indexOf('/', domainStart + 2); + const domain = bidderReq.refererInfo.topmostLocation.substring(domainStart + 2, domainEnd); + const gppStr = bidderReqGpp.gppConsent.gppString; + const gppSid = bidderReqGpp.gppConsent.applicableSections; + const REQ_URL = `${URL}?pid=${bidReq[0].params.publisherId}&domain=${domain}&gpp=${gppStr}&gpp_sid=${gppSid}`; + expect(request.url).to.equal(REQ_URL); + expect(request.method).to.equal('POST'); + }); + it(`should have gpp, ccpa and gdpr fields`, function () { + const request = spec.buildRequests(bidReq, bidderReqFullGppCcpaGdpr); + const domainStart = bidderReq.refererInfo.topmostLocation.indexOf('//'); + const domainEnd = bidderReq.refererInfo.topmostLocation.indexOf('/', domainStart + 2); + const domain = bidderReq.refererInfo.topmostLocation.substring(domainStart + 2, domainEnd); + const gppStr = bidderReqFullGppCcpaGdpr.gppConsent.gppString; + const gppSid = bidderReqFullGppCcpaGdpr.gppConsent.applicableSections; + const ccpa = bidderReqFullGppCcpaGdpr.uspConsent; + const gdpr = bidderReqFullGppCcpaGdpr.gdprConsent.gdprApplies ? 1 : 0; + const gdprStr = bidderReqFullGppCcpaGdpr.gdprConsent.consentString; + const REQ_URL = `${URL}?pid=${bidReq[0].params.publisherId}&domain=${domain}&gdpr=${gdpr}&gdprstr=${gdprStr}&ccpa=${ccpa}&gpp=${gppStr}&gpp_sid=${gppSid}`; + expect(request.url).to.equal(REQ_URL); + expect(request.method).to.equal('POST'); + }); it('should have all relevant fields', function () { const request = spec.buildRequests(bidReq, bidderReq); const bid1 = JSON.parse(request.data)['x-ut-hb-params'][0]; @@ -409,10 +461,14 @@ describe('Undertone Adapter', () => { expect(bidVideo.video.playbackMethod).to.equal(2); expect(bidVideo.video.maxDuration).to.equal(30); expect(bidVideo.video.skippable).to.equal(true); + expect(bidVideo.video.placement).to.equal(1); + expect(bidVideo.video.plcmt).to.equal(1); expect(bidVideo2.video.skippable).to.equal(null); expect(bidVideo2.video.maxDuration).to.equal(null); expect(bidVideo2.video.playbackMethod).to.equal(null); + expect(bidVideo2.video.placement).to.equal(null); + expect(bidVideo2.video.plcmt).to.equal(null); }); it('should send all userIds data to server', function () { const request = spec.buildRequests(bidReqUserIds, bidderReq); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index a2f2bfd8713..e2d4062399b 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1,25 +1,25 @@ import { attachIdSystem, auctionDelay, - coreStorage, + coreStorage, dep, + findRootDomain, init, + PBJS_USER_ID_OPTOUT_NAME, requestBidsHook, + requestDataDeletion, setStoredConsentData, setStoredValue, setSubmoduleRegistry, syncDelay, - PBJS_USER_ID_OPTOUT_NAME, - findRootDomain, requestDataDeletion, } from 'modules/userId/index.js'; import {createEidsArray} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; +import {getPrebidInternal} from 'src/utils.js'; import * as events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import {getGlobal} from 'src/prebidGlobal.js'; -import { - resetConsentData, -} from 'modules/consentManagement.js'; +import {resetConsentData, } from 'modules/consentManagement.js'; import {server} from 'test/mocks/xhr.js'; import {find} from 'src/polyfill.js'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem.js'; @@ -27,7 +27,10 @@ import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; import {id5IdSubmodule} from 'modules/id5IdSystem.js'; import {identityLinkSubmodule} from 'modules/identityLinkIdSystem.js'; import {dmdIdSubmodule} from 'modules/dmdIdSystem.js'; -import {liveIntentIdSubmodule, setEventFiredFlag as liveIntentIdSubmoduleDoNotFireEvent} from 'modules/liveIntentIdSystem.js'; +import { + liveIntentIdSubmodule, + setEventFiredFlag as liveIntentIdSubmoduleDoNotFireEvent +} from 'modules/liveIntentIdSystem.js'; import {merkleIdSubmodule} from 'modules/merkleIdSystem.js'; import {netIdSubmodule} from 'modules/netIdSystem.js'; import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; @@ -39,8 +42,8 @@ import {criteoIdSubmodule} from 'modules/criteoIdSystem.js'; import {mwOpenLinkIdSubModule} from 'modules/mwOpenLinkIdSystem.js'; import {tapadIdSubmodule} from 'modules/tapadIdSystem.js'; import {tncidSubModule} from 'modules/tncIdSystem.js'; -import {getPrebidInternal} from 'src/utils.js'; import {uid2IdSubmodule} from 'modules/uid2IdSystem.js'; +import {euidIdSubmodule} from 'modules/euidIdSystem.js'; import {admixerIdSubmodule} from 'modules/admixerIdSystem.js'; import {deepintentDpesSubmodule} from 'modules/deepintentDpesIdSystem.js'; import {amxIdSubmodule} from '../../../modules/amxIdSystem.js'; @@ -55,6 +58,8 @@ import {getPPID} from '../../../src/adserver.js'; import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; import {GDPR_GVLIDS} from '../../../src/consentHandler.js'; import {MODULE_TYPE_UID} from '../../../src/activities/modules.js'; +import {ACTIVITY_ENRICH_EIDS} from '../../../src/activities/activities.js'; +import {ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE} from '../../../src/activities/params.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -188,8 +193,7 @@ describe('User ID', function () { mockGpt.disable(); mockGpt.enable(); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('pubcid_alt', 'altpubcid200000', (new Date(Date.now() + 5000).toUTCString())); - let origSK = coreStorage.setCookie.bind(coreStorage); + coreStorage.setCookie('pubcid_alt', 'altpubcid200000', (new Date(Date.now() + 20000).toUTCString())); sinon.spy(coreStorage, 'setCookie'); sinon.stub(utils, 'logWarn'); }); @@ -320,10 +324,6 @@ describe('User ID', function () { }); }); }); - // Because the consent cookie doesn't exist yet, we'll have 2 setCookie calls: - // 1) for the consent cookie - // 2) from the getId() call that results in a new call to store the results - expect(coreStorage.setCookie.callCount).to.equal(2); }); }); @@ -350,7 +350,6 @@ describe('User ID', function () { }); }); }); - expect(coreStorage.setCookie.callCount).to.equal(2); }); }); @@ -870,7 +869,7 @@ describe('User ID', function () { it('handles config with no usersync object', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' expect(typeof utils.logInfo.args[0]).to.equal('undefined'); @@ -878,14 +877,14 @@ describe('User ID', function () { it('handles config with empty usersync object', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({ userSync: { userIds: [{}] @@ -896,7 +895,7 @@ describe('User ID', function () { it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({ userSync: { userIds: [{ @@ -913,7 +912,7 @@ describe('User ID', function () { it('config with 1 configurations should create 1 submodules', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); @@ -933,9 +932,9 @@ describe('User ID', function () { expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); - it('config with 22 configurations should result in 22 submodules add', function () { + it('config with 23 configurations should result in 23 submodules add', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule, tncidSubModule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule, tncidSubModule]); config.setConfig({ userSync: { syncDelay: 0, @@ -978,6 +977,8 @@ describe('User ID', function () { storage: {name: 'tapad_id', type: 'cookie'} }, { name: 'uid2' + }, { + name: 'euid' }, { name: 'admixerId', storage: {name: 'admixerId', type: 'cookie'} @@ -1001,12 +1002,12 @@ describe('User ID', function () { }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 22 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 23 submodules'); }); it('config syncDelay updates module correctly', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({ userSync: { syncDelay: 99, @@ -1021,7 +1022,7 @@ describe('User ID', function () { it('config auctionDelay updates module correctly', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({ userSync: { auctionDelay: 100, @@ -1036,7 +1037,7 @@ describe('User ID', function () { it('config auctionDelay defaults to 0 if not a number', function () { init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({ userSync: { auctionDelay: '', @@ -2369,7 +2370,7 @@ describe('User ID', function () { localStorage.setItem('qid_exp', new Date(Date.now() + 5000).toUTCString()) init(config); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, uid2IdSubmodule, euidIdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); config.setConfig({ userSync: { @@ -2503,9 +2504,57 @@ describe('User ID', function () { localStorage.removeItem('amxId'); localStorage.removeItem('amxId_exp'); coreStorage.setCookie('kpuid', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('__uid2_advertising_token', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); + + describe('activity controls', () => { + let isAllowed; + const MOCK_IDS = ['mockId1', 'mockId2'] + beforeEach(() => { + isAllowed = sinon.stub(dep, 'isAllowed'); + init(config); + setSubmoduleRegistry([]); + const mods = MOCK_IDS.map((name) => ({ + name, + decode: function (value) { + return { + [name]: value + }; + }, + getId: function () { + return {id: `${name}Value`}; + } + })); + mods.forEach(attachIdSystem); + }); + afterEach(() => { + isAllowed.restore(); + }); + + it('should check for enrichEids activity permissions', (done) => { + isAllowed.callsFake((activity, params) => { + return !(activity === ACTIVITY_ENRICH_EIDS && + params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_UID && + params[ACTIVITY_PARAM_COMPONENT_NAME] === MOCK_IDS[0]) + }) + + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: MOCK_IDS.map(name => ({ + name, storage: {name, type: 'cookie'} + })) + } + }); + requestBidsHook((req) => { + const activeIds = req.adUnits.flatMap(au => au.bids).flatMap(bid => Object.keys(bid.userId)); + expect(Array.from(new Set(activeIds))).to.have.members([MOCK_IDS[1]]); + done(); + }, {adUnits}) + }); + }) }); describe('callbacks at the end of auction', function () { @@ -2591,15 +2640,21 @@ describe('User ID', function () { }); describe('Set cookie behavior', function () { - let coreStorageSpy; + let cookie, cookieStub; + beforeEach(function () { - coreStorageSpy = sinon.spy(coreStorage, 'setCookie'); setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); + cookie = document.cookie; + cookieStub = sinon.stub(document, 'cookie'); + cookieStub.get(() => cookie); + cookieStub.set((val) => cookie = val); }); + afterEach(function () { - coreStorageSpy.restore(); + cookieStub.restore(); }); + it('should allow submodules to override the domain', function () { const submodule = { submodule: { @@ -2608,26 +2663,34 @@ describe('User ID', function () { } }, config: { + name: 'mockId', storage: { type: 'cookie' } + }, + storageMgr: { + setCookie: sinon.stub() } } setStoredValue(submodule, 'bar'); - expect(coreStorage.setCookie.getCall(0).args[4]).to.equal('foo.com'); + expect(submodule.storageMgr.setCookie.getCall(0).args[4]).to.equal('foo.com'); }); - it('should pass null for domain if submodule does not override the domain', function () { + it('should pass no domain if submodule does not override the domain', function () { const submodule = { submodule: {}, config: { + name: 'mockId', storage: { type: 'cookie' } + }, + storageMgr: { + setCookie: sinon.stub() } } setStoredValue(submodule, 'bar'); - expect(coreStorage.setCookie.getCall(0).args[4]).to.equal(null); + expect(submodule.storageMgr.setCookie.getCall(0).args[4]).to.equal(null); }); }); diff --git a/test/spec/modules/utiqSystem_spec.js b/test/spec/modules/utiqSystem_spec.js new file mode 100644 index 00000000000..afeeea7c3ea --- /dev/null +++ b/test/spec/modules/utiqSystem_spec.js @@ -0,0 +1,188 @@ +import { expect } from 'chai'; +import { utiqSubmodule } from 'modules/utiqSystem.js'; +import { storage } from 'modules/utiqSystem.js'; + +describe('utiqSystem', () => { + const utiqPassKey = 'utiqPass'; + + const getStorageData = (idGraph) => { + if (!idGraph) { + idGraph = {id: 501, domain: ''}; + } + return { + 'connectId': { + 'idGraph': [idGraph], + } + } + }; + + it('should have the correct module name declared', () => { + expect(utiqSubmodule.name).to.equal('utiq'); + }); + + describe('utiq getId()', () => { + afterEach(() => { + storage.removeDataFromLocalStorage(utiqPassKey); + }); + + it('it should return object with key callback', () => { + expect(utiqSubmodule.getId()).to.have.property('callback'); + }); + + it('should return object with key callback with value type - function', () => { + storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData())); + expect(utiqSubmodule.getId()).to.have.property('callback'); + expect(typeof utiqSubmodule.getId().callback).to.be.equal('function'); + }); + + it('tests if localstorage & JSON works properly ', () => { + const idGraph = { + 'domain': 'domainValue', + 'atid': 'atidValue', + }; + storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph))); + expect(JSON.parse(storage.getDataFromLocalStorage(utiqPassKey))).to.have.property('connectId'); + }); + + it('returns {id: {utiq: data.utiq}} if we have the right data stored in the localstorage ', () => { + const idGraph = { + 'domain': 'test.domain', + 'atid': 'atidValue', + }; + storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph))); + const response = utiqSubmodule.getId(); + expect(response).to.have.property('id'); + expect(response.id).to.have.property('utiq'); + expect(response.id.utiq).to.be.equal('atidValue'); + }); + + it('returns {utiq: data.utiq} if we have the right data stored in the localstorage right after the callback is called', (done) => { + const idGraph = { + 'domain': 'test.domain', + 'atid': 'atidValue', + }; + const response = utiqSubmodule.getId(); + expect(response).to.have.property('callback'); + expect(response.callback.toString()).contain('result(callback)'); + + if (typeof response.callback === 'function') { + storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph))); + response.callback(function (result) { + expect(result).to.not.be.null; + expect(result).to.have.property('utiq'); + expect(result.utiq).to.be.equal('atidValue'); + done() + }) + } + }); + + it('returns {utiq: data.utiq} if we have the right data stored in the localstorage right after 500ms delay', (done) => { + const idGraph = { + 'domain': 'test.domain', + 'atid': 'atidValue', + }; + + const response = utiqSubmodule.getId(); + expect(response).to.have.property('callback'); + expect(response.callback.toString()).contain('result(callback)'); + + if (typeof response.callback === 'function') { + setTimeout(() => { + storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph))); + }, 500); + response.callback(function (result) { + expect(result).to.not.be.null; + expect(result).to.have.property('utiq'); + expect(result.utiq).to.be.equal('atidValue'); + done() + }) + } + }); + + it('returns null if we have the data stored in the localstorage after 500ms delay and the max (waiting) delay is only 200ms ', (done) => { + const idGraph = { + 'domain': 'test.domain', + 'atid': 'atidValue', + }; + + const response = utiqSubmodule.getId({params: {maxDelayTime: 200}}); + expect(response).to.have.property('callback'); + expect(response.callback.toString()).contain('result(callback)'); + + if (typeof response.callback === 'function') { + setTimeout(() => { + storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph))); + }, 500); + response.callback(function (result) { + expect(result).to.be.null; + done() + }) + } + }); + }); + + describe('utiq decode()', () => { + const VALID_API_RESPONSES = [ + { + expected: '32a97f612', + payload: { + utiq: '32a97f612' + } + }, + { + expected: '32a97f61', + payload: { + utiq: '32a97f61', + } + }, + ]; + VALID_API_RESPONSES.forEach(responseData => { + it('should return a newly constructed object with the utiq for a payload with {utiq: value}', () => { + expect(utiqSubmodule.decode(responseData.payload)).to.deep.equal( + {utiq: responseData.expected} + ); + }); + }); + + [{}, '', {foo: 'bar'}].forEach((response) => { + it(`should return null for an invalid response "${JSON.stringify(response)}"`, () => { + expect(utiqSubmodule.decode(response)).to.be.null; + }); + }); + }); + + describe('utiq messageHandler', () => { + afterEach(() => { + storage.removeDataFromLocalStorage(utiqPassKey); + }); + + const domains = [ + 'domain1', + 'domain2', + 'domain3', + ]; + + domains.forEach(domain => { + it(`correctly sets utiq value for domain name ${domain}`, (done) => { + const idGraph = { + 'domain': domain, + 'atid': 'atidValue', + }; + + storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph))); + + const eventData = { + data: `{\"msgType\":\"MNOSELECTOR\",\"body\":{\"url\":\"https://${domain}/some/path\"}}` + }; + + window.dispatchEvent(new MessageEvent('message', eventData)); + + const response = utiqSubmodule.getId(); + expect(response).to.have.property('id'); + expect(response.id).to.have.property('utiq'); + expect(response.id.utiq).to.be.equal('atidValue'); + done(); + }); + }); + }); +}); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 1ddc3058a03..6e92bae8d59 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -38,7 +38,6 @@ const BID = { } }, 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', 'sizes': [[300, 250], [300, 600]], 'bidderRequestId': '1fdb5ff1b6eaa7', 'auctionId': 'auction_id', @@ -50,6 +49,7 @@ const BID = { 'mediaTypes': [BANNER], 'ortb2Imp': { 'ext': { + tid: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', 'gpid': '1234567890' } } @@ -59,7 +59,11 @@ const VIDEO_BID = { 'bidId': '2d52001cabd527', 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', 'bidderRequestId': '12a8ae9ada9c13', - 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', + ortb2Imp: { + ext: { + tid: '56e184c6-bde9-497b-b9b9-cf47a61381ee', + } + }, 'auctionId': 'auction_id', 'bidRequestsCount': 4, 'bidderRequestsCount': 3, diff --git a/test/spec/modules/videobyteBidAdapter_spec.js b/test/spec/modules/videobyteBidAdapter_spec.js index f7ea0698956..7844e2bd1be 100644 --- a/test/spec/modules/videobyteBidAdapter_spec.js +++ b/test/spec/modules/videobyteBidAdapter_spec.js @@ -463,7 +463,6 @@ describe('VideoByteBidAdapter', function () { }); let o = { requestId: serverResponse.id, - bidderCode: spec.code, cpm: serverResponse.seatbid[0].bid[0].price, creativeId: serverResponse.seatbid[0].bid[0].crid, vastXml: serverResponse.seatbid[0].bid[0].adm, diff --git a/test/spec/modules/vrtcalBidAdapter_spec.js b/test/spec/modules/vrtcalBidAdapter_spec.js index 491c96df5e2..764db3fa4f0 100644 --- a/test/spec/modules/vrtcalBidAdapter_spec.js +++ b/test/spec/modules/vrtcalBidAdapter_spec.js @@ -55,14 +55,21 @@ describe('vrtcalBidAdapter', function () { expect(request[0].data).to.match(/"bidfloor":0.55/); }); - it('pass GDPR,CCPA, and COPPA indicators/consent strings with the request when present', function () { + it('pass GDPR,CCPA,COPPA, and GPP indicators/consent strings with the request when present', function () { bidRequests[0].gdprConsent = {consentString: 'gdpr-consent-string', gdprApplies: true}; bidRequests[0].uspConsent = 'ccpa-consent-string'; config.setConfig({ coppa: false }); + bidRequests[0].ortb2 = { + regs: { + gpp: 'testGpp', + gpp_sid: [1, 2, 3] + } + } + request = spec.buildRequests(bidRequests); expect(request[0].data).to.match(/"user":{"ext":{"consent":"gdpr-consent-string"/); - expect(request[0].data).to.match(/"regs":{"coppa":0,"ext":{"gdpr":1,"us_privacy":"ccpa-consent-string"}}/); + expect(request[0].data).to.match(/"regs":{"coppa":0,"ext":{"gdpr":1,"us_privacy":"ccpa-consent-string","gpp":"testGpp","gpp_sid":\[1,2,3\]}}/); }); it('pass bidder timeout/tmax with the request', function () { diff --git a/test/spec/modules/weboramaRtdProvider_spec.js b/test/spec/modules/weboramaRtdProvider_spec.js index bbc1c6d6f02..7de8474d7c9 100644 --- a/test/spec/modules/weboramaRtdProvider_spec.js +++ b/test/spec/modules/weboramaRtdProvider_spec.js @@ -130,19 +130,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_ctx=foo;webo_ctx=bar;webo_ds=baz'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('webo_ctx=foo,bar|webo_ds=baz'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(data); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: data - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: data - }, - } - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -217,19 +217,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_vctx=foo;webo_vctx=bar'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('webo_vctx=foo,bar'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(data); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: data - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: data - }, - } - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -304,19 +304,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_vctx=foo;webo_vctx=bar'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('webo_vctx=foo,bar'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(data); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: data - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: data - }, - } - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -543,9 +543,22 @@ describe('weboramaRtdProvider', function() { expect(adUnit.bids[1].params).to.be.undefined; expect(adUnit.bids[2].params.keywords).to.deep.equal(data); expect(adUnit.bids[3].params).to.be.undefined; - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + + return; + } + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -809,9 +822,10 @@ describe('weboramaRtdProvider', function() { baz: 'bam', } }); - - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) }); }); }); @@ -951,9 +965,10 @@ describe('weboramaRtdProvider', function() { baz: 'bam', } }); - - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) }); }); }); @@ -1105,8 +1120,8 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar;webo_ctx=foo;webo_ctx=bar;webo_ds=baz'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('foo=bar|webo_ctx=foo,bar|webo_ds=baz'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar'); + expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('foo=bar'); expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal({ foo: ['bar'], webo_ctx: ['foo', 'bar'], @@ -1115,20 +1130,20 @@ describe('weboramaRtdProvider', function() { expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ inventory: { foo: 'bar', - webo_ctx: ['foo', 'bar'], - webo_ds: ['baz'], }, visitor: { baz: 'bam', } }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: data - }, - } - }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + }) }); it('should use default profile in case of api error', function() { @@ -1196,19 +1211,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_ctx=baz'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('webo_ctx=baz'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(defaultProfile); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: defaultProfile - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: defaultProfile - }, - } - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: defaultProfile + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: defaultProfile, meta: { @@ -1316,20 +1331,35 @@ describe('weboramaRtdProvider', function() { reqBidsConfigObj.adUnits.forEach(adUnit => { expect(adUnit.bids.length).to.equal(5); - expect(adUnit.bids[0].params.target).to.equal('webo_ctx=foo;webo_ctx=bar;webo_ds=baz'); - expect(adUnit.bids[1].params.dctr).to.equal('webo_ctx=foo,bar|webo_ds=baz'); - expect(adUnit.bids[3].params).to.deep.equal({ - inventory: data - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ + expect(adUnit.bids[0].params).to.be.undefined; + expect(adUnit.bids[1].params).to.be.undefined; + expect(adUnit.bids[3].params).to.be.undefined; + }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: { + webo_ctx: ['foo', 'bar'], + webo_ds: ['baz'], + webo_bar: ['baz'], + } + }, + } + }); + + return + } + + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ site: { ext: { data: data }, } }); - }); - + }) expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal({ webo_ctx: ['foo', 'bar'], webo_ds: ['baz'], @@ -1414,19 +1444,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_cs=foo;webo_cs=bar;webo_audiences=baz'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('webo_cs=foo,bar|webo_audiences=baz'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(data); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - visitor: data - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - user: { - ext: { - data: data - }, - } - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + user: { + ext: { + data: data + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -1545,8 +1575,21 @@ describe('weboramaRtdProvider', function() { expect(adUnit.bids[2].params.keywords).to.deep.equal(data); expect(adUnit.bids[3].params).to.be.undefined; }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + user: { + ext: { + data: data + }, + } + }); + + return + } + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -1657,7 +1700,21 @@ describe('weboramaRtdProvider', function() { expect(adUnit.bids[1].params).to.be.undefined; expect(adUnit.bids[3].params).to.be.undefined; }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + user: { + ext: { + data: data + }, + } + }); + + return + } + + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(data); expect(reqBidsConfigObj.adUnits[1].bids[2].params).to.be.undefined; @@ -1813,7 +1870,9 @@ describe('weboramaRtdProvider', function() { } }); }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) }); }); }); @@ -1956,7 +2015,9 @@ describe('weboramaRtdProvider', function() { } }); }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) }); }); }); @@ -2109,8 +2170,8 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar;webo_cs=foo;webo_cs=bar;webo_audiences=baz'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('foo=bar|webo_cs=foo,bar|webo_audiences=baz'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar'); + expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('foo=bar'); expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal({ foo: ['bar'], webo_cs: ['foo', 'bar'], @@ -2122,17 +2183,17 @@ describe('weboramaRtdProvider', function() { }, visitor: { baz: 'bam', - webo_cs: ['foo', 'bar'], - webo_audiences: ['baz'], - } - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - user: { - ext: { - data: data - }, } }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + user: { + ext: { + data: data + }, + } + }); + }) }); it('should use default profile in case of nothing on local storage', function() { @@ -2187,19 +2248,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_audiences=baz'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('webo_audiences=baz'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(defaultProfile); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - visitor: defaultProfile - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - user: { - ext: { - data: defaultProfile - }, - } - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + user: { + ext: { + data: defaultProfile + }, + } + }); + }) }); it('should use default profile if cant read from local storage', function() { @@ -2261,19 +2322,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_audiences=baz'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('webo_audiences=baz'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(defaultProfile); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - visitor: defaultProfile - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - user: { - ext: { - data: defaultProfile - }, - } - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + user: { + ext: { + data: defaultProfile + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: defaultProfile, meta: { @@ -2383,26 +2444,35 @@ describe('weboramaRtdProvider', function() { reqBidsConfigObj.adUnits.forEach(adUnit => { expect(adUnit.bids.length).to.equal(5); - expect(adUnit.bids[0].params.target).to.equal('webo_cs=foo;webo_cs=bar;webo_audiences=baz'); - expect(adUnit.bids[1].params.dctr).to.equal('webo_cs=foo,bar|webo_audiences=baz'); - expect(adUnit.bids[3].params).to.deep.equal({ - visitor: data - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ + expect(adUnit.bids[0].params).to.be.undefined; + expect(adUnit.bids[1].params).to.be.undefined; + expect(adUnit.bids[3].params).to.be.undefined; + }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + user: { + ext: { + data: { + webo_cs: ['foo', 'bar'], + webo_audiences: ['baz'], + webo_bar: ['baz'], + } + }, + } + }); + + return + } + + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ user: { ext: { data: data }, } }); - }); - - expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal({ - webo_cs: ['foo', 'bar'], - webo_audiences: ['baz'], - webo_bar: ['baz'], - }); - expect(reqBidsConfigObj.adUnits[1].bids[2].params.keywords).to.deep.equal(data); + }) expect(onDataResponse).to.deep.equal({ data: data, @@ -2480,19 +2550,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('lite_occupation=gérant;lite_occupation=bénévole;lite_hobbies=sport;lite_hobbies=cinéma'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('lite_occupation=gérant,bénévole|lite_hobbies=sport,cinéma'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(data); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: data, - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: data, - }, - }, - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -2610,7 +2680,21 @@ describe('weboramaRtdProvider', function() { expect(adUnit.bids[2].params.keywords).to.deep.equal(data); expect(adUnit.bids[3].params).to.be.undefined; }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + + return + } + + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) expect(onDataResponse).to.deep.equal({ data: data, @@ -2721,11 +2805,26 @@ describe('weboramaRtdProvider', function() { expect(adUnit.bids[1].params).to.be.undefined; expect(adUnit.bids[3].params).to.be.undefined; }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(data); expect(reqBidsConfigObj.adUnits[1].bids[2].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + + return + } + + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) + expect(onDataResponse).to.deep.equal({ data: data, meta: { @@ -2876,7 +2975,9 @@ describe('weboramaRtdProvider', function() { } }); }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) }); }); }); @@ -3018,7 +3119,9 @@ describe('weboramaRtdProvider', function() { } }); }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.be.undefined; + }) }); }); }); @@ -3173,8 +3276,8 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar;lite_occupation=gérant;lite_occupation=bénévole;lite_hobbies=sport;lite_hobbies=cinéma'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('foo=bar|lite_occupation=gérant,bénévole|lite_hobbies=sport,cinéma'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar'); + expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('foo=bar'); expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal({ foo: ['bar'], lite_occupation: ['gérant', 'bénévole'], @@ -3183,20 +3286,20 @@ describe('weboramaRtdProvider', function() { expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ inventory: { foo: 'bar', - lite_occupation: ['gérant', 'bénévole'], - lite_hobbies: ['sport', 'cinéma'], }, visitor: { baz: 'bam', } }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: data, - }, - }, - }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data + }, + } + }); + }) }); it('should use default profile in case of nothing on local storage', function() { @@ -3250,19 +3353,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('lite_hobbies=sport;lite_hobbies=cinéma'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('lite_hobbies=sport,cinéma'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(defaultProfile); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: defaultProfile, - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: defaultProfile, - }, - }, - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: defaultProfile + }, + } + }); + }) }); it('should use default profile if cant read from local storage', function() { @@ -3323,12 +3426,10 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('lite_hobbies=sport;lite_hobbies=cinéma'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('lite_hobbies=sport,cinéma'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(defaultProfile); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: defaultProfile, - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ site: { ext: { @@ -3336,6 +3437,15 @@ describe('weboramaRtdProvider', function() { }, }, }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: defaultProfile, + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: defaultProfile, meta: { @@ -3403,19 +3513,19 @@ describe('weboramaRtdProvider', function() { }); expect(reqBidsConfigObj.adUnits[0].bids.length).to.equal(5); - expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('lite_hobbies=sport;lite_hobbies=cinéma'); - expect(reqBidsConfigObj.adUnits[0].bids[1].params.dctr).to.equal('lite_hobbies=sport,cinéma'); + expect(reqBidsConfigObj.adUnits[0].bids[0].params).to.be.undefined; + expect(reqBidsConfigObj.adUnits[0].bids[1].params).to.be.undefined; expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal(defaultProfile); - expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.deep.equal({ - inventory: defaultProfile, - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: defaultProfile, - }, - }, - }); + expect(reqBidsConfigObj.adUnits[0].bids[3].params).to.be.undefined; + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: defaultProfile + }, + } + }); + }) expect(onDataResponse).to.deep.equal({ data: defaultProfile, meta: { @@ -3523,19 +3633,35 @@ describe('weboramaRtdProvider', function() { reqBidsConfigObj.adUnits.forEach(adUnit => { expect(adUnit.bids.length).to.equal(5); - expect(adUnit.bids[0].params.target).to.equal('lite_occupation=gérant;lite_occupation=bénévole;lite_hobbies=sport;lite_hobbies=cinéma'); - expect(adUnit.bids[1].params.dctr).to.equal('lite_occupation=gérant,bénévole|lite_hobbies=sport,cinéma'); - expect(adUnit.bids[3].params).to.deep.equal({ - inventory: data, + expect(adUnit.bids[0].params).to.be.undefined; + expect(adUnit.bids[1].params).to.be.undefined; + expect(adUnit.bids[3].params).to.be.undefined; + }); + ['smartadserver', 'pubmatic', 'appnexus', 'rubicon', 'other'].forEach((v) => { + if (v == 'appnexus') { + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: { + lite_occupation: ['gérant', 'bénévole'], + lite_hobbies: ['sport', 'cinéma'], + lito_bar: ['baz'], + }, + }, + } + }); + + return + } + + expect(reqBidsConfigObj.ortb2Fragments.bidder[v]).to.deep.equal({ + site: { + ext: { + data: data, + }, + } }); - }); - expect(reqBidsConfigObj.ortb2Fragments.bidder.other).to.deep.equal({ - site: { - ext: { - data: data, - }, - }, - }); + }) expect(reqBidsConfigObj.adUnits[0].bids[2].params.keywords).to.deep.equal({ lite_occupation: ['gérant', 'bénévole'], diff --git a/test/spec/modules/winrBidAdapter_spec.js b/test/spec/modules/winrBidAdapter_spec.js index 174f600fa06..95d1473d1cb 100644 --- a/test/spec/modules/winrBidAdapter_spec.js +++ b/test/spec/modules/winrBidAdapter_spec.js @@ -599,15 +599,6 @@ describe('WinrAdapter', function () { }); describe('interpretResponse', function () { - let bfStub; - before(function() { - bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); - }); - - after(function() { - bfStub.restore(); - }); - let response = { 'version': '3.0.0', 'tags': [ diff --git a/test/spec/modules/xeBidAdapter_spec.js b/test/spec/modules/xeBidAdapter_spec.js index dcd82683221..38b334c32c5 100644 --- a/test/spec/modules/xeBidAdapter_spec.js +++ b/test/spec/modules/xeBidAdapter_spec.js @@ -11,7 +11,11 @@ const defaultRequest = { bidId: '1', requestId: 'qwerty', auctionId: 'auctionId', - transactionId: 'tr1', + ortb2Imp: { + ext: { + tid: 'tr1', + } + }, mediaTypes: { banner: { sizes: [ @@ -85,7 +89,7 @@ describe('buildRequests', function () { const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; expect(request).to.have.property('bidId').and.to.equal(defaultRequest.bidId); expect(request).to.have.property('auctionId').and.to.equal(defaultRequest.auctionId); - expect(request).to.have.property('transactionId').and.to.equal(defaultRequest.transactionId); + expect(request).to.have.property('transactionId').and.to.equal(defaultRequest.ortb2Imp.ext.tid); expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); expect(request).to.have.property('bc').and.to.equal(1); expect(request).to.have.property('floor').and.to.equal(null); diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index f558089b7b0..b0030c89371 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -89,11 +89,16 @@ let generateBidderRequest = (bidRequestArray, adUnitCode, ortb2 = {}) => { numIframes: 0, stack: ['https://publisher-test.com'], }, + uspConsent: '1-Y-', gdprConsent: { consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA', vendorData: {}, gdprApplies: true }, + gppConsent: { + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + applicableSections: [1, 2, 3] + }, start: new Date().getTime(), timeout: 1000, ortb2 @@ -177,11 +182,6 @@ const generateResponseMock = (admPayloadType, vastVersion, videoContext) => { // Unit tests describe('YahooSSP Bid Adapter:', () => { - it('PLACEHOLDER TO PASS GULP', () => { - const obj = {}; - expect(obj).to.be.an('object'); - }); - describe('Validate basic properties', () => { it('should define the correct bidder code', () => { expect(spec.code).to.equal('yahoossp') @@ -193,40 +193,38 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('getUserSyncs()', () => { - const IMAGE_PIXEL_URL = 'http://image-pixel.com/foo/bar?1234&baz=true'; - const IFRAME_ONE_URL = 'http://image-iframe.com/foo/bar?1234&baz=true'; + const IMAGE_PIXEL_URL = 'http://image-pixel.com/foo/bar?1234&baz=true&gdpr=foo&gdpr_consent=bar'; + const IFRAME_ONE_URL = 'http://image-iframe.com/foo/bar?1234&baz=true&us_privacy=hello&gpp=goodbye'; const IFRAME_TWO_URL = 'http://image-iframe-two.com/foo/bar?1234&baz=true'; - - let serverResponses = []; - beforeEach(() => { - serverResponses[0] = { - body: { - ext: { - pixels: `` - } + const SERVER_RESPONSES = [{ + body: { + ext: { + pixels: `` } } - }); - - after(() => { - serverResponses = undefined; - }); + }]; + const bidderRequest = generateBuildRequestMock({}).bidderRequest; it('for only iframe enabled syncs', () => { let syncOptions = { iframeEnabled: true, pixelEnabled: false }; - let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); - expect(pixelsObjects.length).to.equal(2); - expect(pixelsObjects).to.deep.equal( - [ - {type: 'iframe', 'url': IFRAME_ONE_URL}, - {type: 'iframe', 'url': IFRAME_TWO_URL} - ] - ) + let pixelObjects = spec.getUserSyncs( + syncOptions, + SERVER_RESPONSES, + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + expect(pixelObjects.length).to.equal(2); + + pixelObjects.forEach(pixelObject => { + expect(pixelObject).to.have.all.keys('type', 'url'); + expect(pixelObject.type).to.equal('iframe'); + }); }); it('for only pixel enabled syncs', () => { @@ -234,13 +232,16 @@ describe('YahooSSP Bid Adapter:', () => { iframeEnabled: false, pixelEnabled: true }; - let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); - expect(pixelsObjects.length).to.equal(1); - expect(pixelsObjects).to.deep.equal( - [ - {type: 'image', 'url': IMAGE_PIXEL_URL} - ] - ) + let pixelObjects = spec.getUserSyncs( + syncOptions, + SERVER_RESPONSES, + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + expect(pixelObjects.length).to.equal(1); + expect(pixelObjects[0]).to.have.all.keys('type', 'url'); + expect(pixelObjects[0].type).to.equal('image'); }); it('for both pixel and iframe enabled syncs', () => { @@ -248,15 +249,56 @@ describe('YahooSSP Bid Adapter:', () => { iframeEnabled: true, pixelEnabled: true }; - let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); - expect(pixelsObjects.length).to.equal(3); - expect(pixelsObjects).to.deep.equal( - [ - {type: 'iframe', 'url': IFRAME_ONE_URL}, - {type: 'image', 'url': IMAGE_PIXEL_URL}, - {type: 'iframe', 'url': IFRAME_TWO_URL} - ] - ) + let pixelObjects = spec.getUserSyncs( + syncOptions, + SERVER_RESPONSES, + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + expect(pixelObjects.length).to.equal(3); + let iframeCount = 0; + let imageCount = 0; + pixelObjects.forEach(pixelObject => { + if (pixelObject.type == 'iframe') { + iframeCount++; + } else if (pixelObject.type == 'image') { + imageCount++; + } + }); + expect(iframeCount).to.equal(2); + expect(imageCount).to.equal(1); + }); + + describe('user consent parameters are updated', () => { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + let pixelObjects = spec.getUserSyncs( + syncOptions, + SERVER_RESPONSES, + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + pixelObjects.forEach(pixelObject => { + let url = pixelObject.url; + let urlParams = new URL(url).searchParams; + const expectedParams = { + 'baz': 'true', + 'gdpr_consent': bidderRequest.gdprConsent.consentString, + 'gdpr': bidderRequest.gdprConsent.gdprApplies ? '1' : '0', + 'us_privacy': bidderRequest.uspConsent, + 'gpp': bidderRequest.gppConsent.gppString, + 'gpp_sid': Array.isArray(bidderRequest.gppConsent.applicableSections) ? bidderRequest.gppConsent.applicableSections.join(',') : '' + } + for (const [key, value] of Object.entries(expectedParams)) { + it(`Updates the ${key} consent param in user sync URL ${url}`, () => { + expect(urlParams.get(key)).to.equal(value); + }); + }; + }); }); }); @@ -749,7 +791,15 @@ describe('YahooSSP Bid Adapter:', () => { expect(options.withCredentials).to.be.false; }); - it('adds the ortb2 gpp consent info to the request', function () { + it('set the GPP consent data from the data within the bid request', function () { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + let clonedBidderRequest = {...bidderRequest}; + const data = spec.buildRequests(validBidRequests, clonedBidderRequest)[0].data; + expect(data.regs.ext.gpp).to.equal(bidderRequest.gppConsent.gppString); + expect(data.regs.ext.gpp_sid).to.eql(bidderRequest.gppConsent.applicableSections); + }); + + it('overrides the GPP consent data using data from the ortb2 config object', function () { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); const ortb2 = { regs: { @@ -759,8 +809,8 @@ describe('YahooSSP Bid Adapter:', () => { }; let clonedBidderRequest = {...bidderRequest, ortb2}; const data = spec.buildRequests(validBidRequests, clonedBidderRequest)[0].data; - expect(data.regs.ext.gpp).to.equal('somegppstring'); - expect(data.regs.ext.gpp_sid).to.eql([6, 7]); + expect(data.regs.ext.gpp).to.equal(ortb2.regs.gpp); + expect(data.regs.ext.gpp_sid).to.eql(ortb2.regs.gpp_sid); }); }); @@ -930,8 +980,10 @@ describe('YahooSSP Bid Adapter:', () => { expect(data.regs).to.deep.equal({ ext: { - 'us_privacy': '', - gdpr: 1 + 'us_privacy': bidderRequest.uspConsent, + gdpr: 1, + gpp: bidderRequest.gppConsent.gppString, + gpp_sid: bidderRequest.gppConsent.applicableSections } }); diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 80537facd41..93c231c816b 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -200,6 +200,15 @@ const NATIVE_RESPONSE = Object.assign({}, RESPONSE, { value: 'Native body value', }, }, + { + id: 4, + img: { + url: 'https://localhost:8080/assets/favicon/favicon-16x16.png', + w: 16, + h: 16, + type: 1, + }, + }, ], imptrackers: [ 'http://localhost:8080/ve?d=ODE9ZSY2MTI1MjAzNjMzMzYxPXN0JjA0NWUwZDk0NTY5Yi05M2FiLWUwZTQtOWFjNy1hYWY0MzFiZj1kaXQmMj12', @@ -567,15 +576,24 @@ describe('yieldlabBidAdapter', () => { expect(result[0].native.image.url).to.equal('https://localhost:8080/yl-logo100x100.jpg'); expect(result[0].native.image.width).to.equal(100); expect(result[0].native.image.height).to.equal(100); + expect(result[0].native.icon.url).to.equal('https://localhost:8080/assets/favicon/favicon-16x16.png'); + expect(result[0].native.icon.width).to.equal(16); + expect(result[0].native.icon.height).to.equal(16); expect(result[0].native.clickUrl).to.equal('https://www.yieldlab.de'); expect(result[0].native.impressionTrackers.length).to.equal(3); - expect(result[0].native.assets.length).to.equal(3); + expect(result[0].native.assets.length).to.equal(4); const titleAsset = result[0].native.assets.find(asset => 'title' in asset); - const imageAsset = result[0].native.assets.find(asset => 'img' in asset); + const imageAsset = result[0].native.assets.find((asset) => { + return asset?.img?.type === 3; + }); + const iconAsset = result[0].native.assets.find((asset) => { + return asset?.img?.type === 1; + }); const bodyAsset = result[0].native.assets.find(asset => 'data' in asset); expect(titleAsset).to.exist.and.to.have.nested.property('id', 1) expect(imageAsset).to.exist.and.to.have.nested.property('id', 2) expect(bodyAsset).to.exist.and.to.have.nested.property('id', 3) + expect(iconAsset).to.exist.and.to.have.nested.property('id', 4) }); it('should add adUrl and default native assets when type is Native', () => { @@ -601,6 +619,28 @@ describe('yieldlabBidAdapter', () => { expect(result[0].native.image.height).to.equal(0); }); + it('should not add icon if not present in the native response', () => { + const NATIVE_RESPONSE_WITHOUT_ICON = Object.assign({}, NATIVE_RESPONSE, { + native: { + link: { + url: 'https://www.yieldlab.de', + }, + assets: [ + { + id: 1, + title: { + text: 'This is a great headline', + } + } + ], + imptrackers: [], + }, + }); + const result = spec.interpretResponse({body: [NATIVE_RESPONSE_WITHOUT_ICON]}, {validBidRequests: [NATIVE_REQUEST()], queryParams: REQPARAMS}); + expect(result[0].native.hasOwnProperty('icon')).to.be.false; + expect(result[0].native.title).to.equal('This is a great headline'); + }); + it('should append gdpr parameters to vastUrl', () => { const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST()], queryParams: REQPARAMS_GDPR}); diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index c3678427a9a..601f4546a29 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -127,6 +127,29 @@ describe('Zeta Ssp Bid Adapter', function () { timeout: 500 }]; + const bannerWithFewSizesRequest = [{ + bidId: 12345, + auctionId: 67890, + mediaTypes: { + banner: { + sizes: [[300, 250], [200, 240], [100, 150]], + } + }, + refererInfo: { + page: 'http://www.zetaglobal.com/page?param=value', + domain: 'www.zetaglobal.com', + }, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentString' + }, + schain: schain, + uspConsent: 'someCCPAString', + params: params, + userIdAsEids: eids, + timeout: 500 + }]; + const videoRequest = [{ bidId: 112233, auctionId: 667788, @@ -137,6 +160,8 @@ describe('Zeta Ssp Bid Adapter', function () { mimes: ['video/mp4'], minduration: 5, maxduration: 30, + placement: 2, + plcmt: 1, protocols: [2, 3] } }, @@ -146,6 +171,87 @@ describe('Zeta Ssp Bid Adapter', function () { params: params }]; + const zetaResponse = { + body: { + id: '12345', + seatbid: [ + { + bid: [ + { + id: 'auctionId', + impid: 'impId', + price: 0.0, + adm: 'adMarkup', + crid: 'creativeId', + adomain: [ + 'https://example.com' + ], + h: 250, + w: 300 + } + ] + } + ], + cur: 'USD' + } + } + + const responseBannerPayload = { + data: { + id: '123', + site: { + id: 'SITE_ID', + page: 'page.com', + domain: 'domain.com' + }, + user: { + id: '45asdf9tydhrty789adfad4678rew656789', + buyeruid: '1234567890' + }, + cur: [ + 'USD' + ], + imp: [ + { + id: '1', + banner: { + h: 600, + w: 160 + } + } + ], + at: 1 + } + }; + + const responseVideoPayload = { + data: { + id: '123', + site: { + id: 'SITE_ID', + page: 'page.com', + domain: 'domain.com' + }, + user: { + id: '45asdf9tydhrty789adfad4678rew656789', + buyeruid: '1234567890' + }, + cur: [ + 'USD' + ], + imp: [ + { + id: '1', + video: { + h: 600, + w: 160 + } + } + ], + at: 1 + } + }; + it('Test the bid validation function', function () { const validBid = spec.isBidRequestValid(bannerRequest[0]); const invalidBid = spec.isBidRequestValid(null); @@ -199,7 +305,12 @@ describe('Zeta Ssp Bid Adapter', function () { 'https://example.com' ], h: 250, - w: 300 + w: 300, + ext: { + prebid: { + type: 'banner' + } + } }, { id: 'auctionId2', @@ -213,7 +324,9 @@ describe('Zeta Ssp Bid Adapter', function () { h: 150, w: 200, ext: { - bidtype: 'video' + prebid: { + type: 'video' + } } }, { @@ -226,7 +339,12 @@ describe('Zeta Ssp Bid Adapter', function () { 'https://example3.com' ], h: 400, - w: 300 + w: 300, + ext: { + prebid: { + type: 'video' + } + } } ] } @@ -235,7 +353,7 @@ describe('Zeta Ssp Bid Adapter', function () { } }; - const bidResponse = spec.interpretResponse(response, null); + const bidResponse = spec.interpretResponse(response, responseBannerPayload); expect(bidResponse).to.not.be.empty; const bid1 = bidResponse[0]; @@ -326,6 +444,8 @@ describe('Zeta Ssp Bid Adapter', function () { expect(payload.imp[0].video.mimes).to.eql(videoRequest[0].mediaTypes.video.mimes); expect(payload.imp[0].video.w).to.eql(720); expect(payload.imp[0].video.h).to.eql(340); + expect(payload.imp[0].video.placement).to.eql(videoRequest[0].mediaTypes.video.placement); + expect(payload.imp[0].video.plcmt).to.eql(videoRequest[0].mediaTypes.video.plcmt); expect(payload.imp[0].banner).to.be.undefined; }); @@ -404,4 +524,84 @@ describe('Zeta Ssp Bid Adapter', function () { expect(payload.imp[0].tagid).to.eql(params.tagid); }); + + it('Test if only one size', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + + // banner + expect(payload.imp[0].banner.w).to.eql(300); + expect(payload.imp[0].banner.h).to.eql(250); + + expect(payload.imp[0].banner.format).to.be.undefined; + }); + + it('Test few sizes provided in format', function () { + const request = spec.buildRequests(bannerWithFewSizesRequest, bannerWithFewSizesRequest[0]); + const payload = JSON.parse(request.data); + + // banner + expect(payload.imp[0].banner.w).to.eql(300); + expect(payload.imp[0].banner.h).to.eql(250); + + expect(payload.imp[0].banner.format.length).to.eql(3); + + // format[0] + expect(payload.imp[0].banner.format[0].w).to.eql(300); + expect(payload.imp[0].banner.format[0].h).to.eql(250); + + // format[1] + expect(payload.imp[0].banner.format[1].w).to.eql(200); + expect(payload.imp[0].banner.format[1].h).to.eql(240); + + // format[2] + expect(payload.imp[0].banner.format[2].w).to.eql(100); + expect(payload.imp[0].banner.format[2].h).to.eql(150); + }); + + it('Test the response default mediaType:banner', function () { + const bidResponse = spec.interpretResponse(zetaResponse, responseBannerPayload); + expect(bidResponse).to.not.be.empty; + expect(bidResponse.length).to.eql(1); + expect(bidResponse[0].mediaType).to.eql(BANNER); + expect(bidResponse[0].ad).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); + expect(bidResponse[0].vastXml).to.be.undefined; + }); + + it('Test the response default mediaType:video', function () { + const bidResponse = spec.interpretResponse(zetaResponse, responseVideoPayload); + expect(bidResponse).to.not.be.empty; + expect(bidResponse.length).to.eql(1); + expect(bidResponse[0].mediaType).to.eql(VIDEO); + expect(bidResponse[0].ad).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); + expect(bidResponse[0].vastXml).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); + }); + + it('Test the response mediaType:video from ext param', function () { + zetaResponse.body.seatbid[0].bid[0].ext = { + prebid: { + type: 'video' + } + } + const bidResponse = spec.interpretResponse(zetaResponse, responseBannerPayload); + expect(bidResponse).to.not.be.empty; + expect(bidResponse.length).to.eql(1); + expect(bidResponse[0].mediaType).to.eql(VIDEO); + expect(bidResponse[0].ad).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); + expect(bidResponse[0].vastXml).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); + }); + + it('Test the response mediaType:banner from ext param', function () { + zetaResponse.body.seatbid[0].bid[0].ext = { + prebid: { + type: 'banner' + } + } + const bidResponse = spec.interpretResponse(zetaResponse, responseVideoPayload); + expect(bidResponse).to.not.be.empty; + expect(bidResponse.length).to.eql(1); + expect(bidResponse[0].mediaType).to.eql(BANNER); + expect(bidResponse[0].ad).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); + expect(bidResponse[0].vastXml).to.be.undefined; + }); }); diff --git a/test/spec/sizeMapping_spec.js b/test/spec/sizeMapping_spec.js deleted file mode 100644 index c4efbddad6d..00000000000 --- a/test/spec/sizeMapping_spec.js +++ /dev/null @@ -1,341 +0,0 @@ -import { expect } from 'chai'; -import { resolveStatus, setSizeConfig, sizeSupported } from 'src/sizeMapping.js'; -import {includes} from 'src/polyfill.js' - -let utils = require('src/utils'); -let deepClone = utils.deepClone; - -describe('sizeMapping', function () { - var testSizes = { - banner: { - sizes: [[970, 90], [728, 90], [300, 250], [300, 100], [80, 80]] - } - }; - - var sizeConfig = [{ - 'mediaQuery': '(min-width: 1200px)', - 'sizesSupported': [ - [970, 90], - [728, 90], - [300, 250] - ] - }, { - 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', - 'sizesSupported': [ - [728, 90], - [300, 250], - [300, 100] - ] - }, { - 'mediaQuery': '(min-width: 0px) and (max-width: 767px)', - 'sizesSupported': [] - }]; - - var sizeConfigWithLabels = [{ - 'mediaQuery': '(min-width: 1200px)', - 'labels': ['desktop'] - }, { - 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', - 'sizesSupported': [ - [728, 90], - [300, 250] - ], - 'labels': ['tablet', 'phone'] - }, { - 'mediaQuery': '(min-width: 0px) and (max-width: 767px)', - 'sizesSupported': [ - [300, 250], - [300, 100] - ], - 'labels': ['phone'] - }]; - - let sandbox, - matchMediaOverride; - - beforeEach(function () { - setSizeConfig(sizeConfig); - - sandbox = sinon.sandbox.create(); - - matchMediaOverride = {matches: false}; - - sandbox.stub(utils.getWindowTop(), 'matchMedia').callsFake((...args) => { - if (typeof matchMediaOverride === 'function') { - return matchMediaOverride.apply(utils.getWindowTop(), args); - } - return matchMediaOverride; - }); - }); - - afterEach(function () { - setSizeConfig([]); - - sandbox.restore(); - }); - - describe('when handling sizes', function () { - it('should allow us to validate a single size', function() { - matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - - expect(sizeSupported([300, 250])).to.equal(true); - expect(sizeSupported([80, 80])).to.equal(false); - }); - - it('should log a warning when mediaQuery property missing from sizeConfig', function () { - let errorConfig = deepClone(sizeConfig); - - delete errorConfig[0].mediaQuery; - - sandbox.stub(utils, 'logWarn'); - - resolveStatus(undefined, testSizes, undefined, errorConfig); - expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); - }); - - it('should log a warning message when mediaQuery property is declared as an empty string', function() { - const errorConfig = deepClone(sizeConfig); - errorConfig[0].mediaQuery = ''; - - sandbox.stub(utils, 'logWarn'); - resolveStatus(undefined, testSizes, undefined, errorConfig); - expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); - }); - - it('should allow deprecated adUnit.sizes', function() { - matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - - let status = resolveStatus(undefined, undefined, testSizes.banner.sizes, sizeConfig); - - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [[970, 90], [728, 90], [300, 250]] - } - }); - }); - - it('when one mediaQuery block matches, it should filter the adUnit.sizes passed in', function () { - matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - - let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); - - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [[970, 90], [728, 90], [300, 250]] - } - }); - }); - - it('when multiple mediaQuery block matches, it should filter a union of the matched sizesSupported', function () { - matchMediaOverride = (str) => includes([ - '(min-width: 1200px)', - '(min-width: 768px) and (max-width: 1199px)' - ], str) ? {matches: true} : {matches: false}; - - let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [[970, 90], [728, 90], [300, 250], [300, 100]] - } - }); - }); - - it('if no mediaQueries match, it should allow all sizes specified', function () { - matchMediaOverride = () => ({matches: false}); - - let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal(testSizes); - }); - - it('if a mediaQuery matches and has sizesSupported: [], it should filter all sizes', function () { - matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; - - let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); - expect(status.active).to.equal(false); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [] - } - }); - }); - - it('should filter all banner sizes and should disable the adUnit even if other mediaTypes are present', function () { - matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, Object.assign({}, testSizes, { - native: { - type: 'image' - } - }), undefined, sizeConfig); - expect(status.active).to.equal(false); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [] - }, - native: { - type: 'image' - } - }); - }); - - it('if a mediaQuery matches and no sizesSupported specified, it should not affect adUnit.sizes', function () { - matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - - let status = resolveStatus(undefined, testSizes, undefined, sizeConfigWithLabels); - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal(testSizes); - }); - }); - - describe('when handling labels', function () { - it('should activate/deactivate adUnits/bidders based on sizeConfig.labels', function () { - matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - - let status = resolveStatus({ - labels: ['desktop'] - }, testSizes, undefined, sizeConfigWithLabels); - - expect(status).to.deep.equal({ - active: true, - mediaTypes: testSizes - }); - - status = resolveStatus({ - labels: ['tablet'] - }, testSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(false); - expect(status.mediaTypes).to.deep.equal(testSizes); - }); - - it('should activate/decactivate adUnits/bidders based on labels with multiformat ads', function () { - matchMediaOverride = (str) => str === '(min-width: 768px) and (max-width: 1199px)' ? {matches: true} : {matches: false}; - - let multiFormatSizes = { - banner: { - sizes: [[728, 90], [300, 300]] - }, - native: { - type: 'image' - }, - video: { - context: 'outstream', - playerSize: [300, 300] - } - }; - - let status = resolveStatus({ - labels: ['tablet', 'test'], - labelAll: true - }, multiFormatSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(false); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [[728, 90]] - }, - native: { - type: 'image' - }, - video: { - context: 'outstream', - playerSize: [300, 300] - } - }); - - status = resolveStatus({ - labels: ['tablet'] - }, multiFormatSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [[728, 90]] - }, - native: { - type: 'image' - }, - video: { - context: 'outstream', - playerSize: [300, 300] - } - }); - - multiFormatSizes.banner.sizes.splice(0, 1, [728, 80]); - status = resolveStatus({ - labels: ['tablet'] - }, multiFormatSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(false); - expect(status.mediaTypes).to.deep.equal({ - banner: { - sizes: [] - }, - native: { - type: 'image' - }, - video: { - context: 'outstream', - playerSize: [300, 300] - } - }); - - delete multiFormatSizes.banner; - status = resolveStatus({ - labels: ['tablet'] - }, multiFormatSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal({ - native: { - type: 'image' - }, - video: { - context: 'outstream', - playerSize: [300, 300] - } - }); - }); - - it('should active/deactivate adUnits/bidders based on requestBids labels', function () { - let activeLabels = ['us-visitor', 'desktop', 'smart']; - - let status = resolveStatus({ - labels: ['uk-visitor'], // from adunit - activeLabels // from requestBids.labels - }, testSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(false); - expect(status.mediaTypes).to.deep.equal(testSizes); - - status = resolveStatus({ - labels: ['us-visitor'], - activeLabels - }, testSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal(testSizes); - - status = resolveStatus({ - labels: ['us-visitor', 'tablet'], - labelAll: true, - activeLabels - }, testSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(false); - expect(status.mediaTypes).to.deep.equal(testSizes); - - status = resolveStatus({ - labels: ['us-visitor', 'desktop'], - labelAll: true, - activeLabels - }, testSizes, undefined, sizeConfigWithLabels); - - expect(status.active).to.equal(true); - expect(status.mediaTypes).to.deep.equal(testSizes); - }); - }); -}); diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index 8d887474180..cff26df2e4d 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -4,7 +4,7 @@ import adapterManager, { coppaDataHandler, _partitionBidders, PARTITIONS, - getS2SBidderSet, _filterBidsForAdUnit + getS2SBidderSet, _filterBidsForAdUnit, dep } from 'src/adapterManager.js'; import { getAdUnits, @@ -16,13 +16,14 @@ import CONSTANTS from 'src/constants.json'; 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 { setSizeConfig } from 'modules/sizeMapping.js'; import {find, includes} from 'src/polyfill.js'; import s2sTesting from 'modules/s2sTesting.js'; import {hook} from '../../../../src/hook.js'; import {auctionManager} from '../../../../src/auctionManager.js'; import {GDPR_GVLIDS} from '../../../../src/consentHandler.js'; import {MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER} from '../../../../src/activities/modules.js'; +import {ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS} from '../../../../src/activities/activities.js'; var events = require('../../../../src/events'); const CONFIG = { @@ -353,103 +354,79 @@ describe('adapterManager tests', function () { }); }); // end callTimedOutBidders - describe('onBidWon', function () { - var criteoSpec = { onBidWon: sinon.stub() } - var criteoAdapter = { - bidder: 'criteo', - getSpec: function() { return criteoSpec; } - } + describe('bidder spec methods', () => { + let adUnits, bids, criteoSpec; before(function () { config.setConfig({s2sConfig: { enabled: false }}); }); - beforeEach(function () { - adapterManager.bidderRegistry['criteo'] = criteoAdapter; - }); - - afterEach(function () { - delete adapterManager.bidderRegistry['criteo']; - }); - - it('should call spec\'s onBidWon callback when a bid is won', function () { - const bids = [ + beforeEach(() => { + criteoSpec = {} + adapterManager.bidderRegistry['criteo'] = { + bidder: 'criteo', + getSpec: function() { return criteoSpec; }, + } + bids = [ {bidder: 'criteo', params: {placementId: 'id'}}, ]; - const adUnits = [{ + adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], bids }]; - - adapterManager.callBidWonBidder(bids[0].bidder, bids[0], adUnits); - sinon.assert.called(criteoSpec.onBidWon); - }); - }); // end onBidWon - - describe('onSetTargeting', function () { - var criteoSpec = { onSetTargeting: sinon.stub() } - var criteoAdapter = { - bidder: 'criteo', - getSpec: function() { return criteoSpec; } - } - before(function () { - config.setConfig({s2sConfig: { enabled: false }}); - }); - - beforeEach(function () { - adapterManager.bidderRegistry['criteo'] = criteoAdapter; }); afterEach(function () { delete adapterManager.bidderRegistry['criteo']; }); - it('should call spec\'s onSetTargeting callback when setTargeting is called', function () { - const bids = [ - {bidder: 'criteo', params: {placementId: 'id'}}, - ]; - const adUnits = [{ - code: 'adUnit-code', - sizes: [[728, 90]], - bids - }]; - adapterManager.callSetTargetingBidder(bids[0].bidder, bids[0], adUnits); - sinon.assert.called(criteoSpec.onSetTargeting); - }); - }); // end onSetTargeting + describe('onBidWon', function () { + beforeEach(() => { + criteoSpec.onBidWon = sinon.stub() + }); + it('should call spec\'s onBidWon callback when a bid is won', function () { + adapterManager.callBidWonBidder(bids[0].bidder, bids[0], adUnits); + sinon.assert.called(criteoSpec.onBidWon); + }); - describe('onBidViewable', function () { - var criteoSpec = { onBidViewable: sinon.stub() } - var criteoAdapter = { - bidder: 'criteo', - getSpec: function() { return criteoSpec; } - } - before(function () { - config.setConfig({s2sConfig: { enabled: false }}); + it('should NOT call onBidWon when the bid is S2S', () => { + bids[0].src = CONSTANTS.S2S.SRC + adapterManager.callBidWonBidder(bids[0].bidder, bids[0], adUnits); + sinon.assert.notCalled(criteoSpec.onBidWon); + }) }); - beforeEach(function () { - adapterManager.bidderRegistry['criteo'] = criteoAdapter; - }); + describe('onSetTargeting', function () { + beforeEach(() => { + criteoSpec.onSetTargeting = sinon.stub() + }) - afterEach(function () { - delete adapterManager.bidderRegistry['criteo']; - }); + it('should call spec\'s onSetTargeting callback when setTargeting is called', function () { + adapterManager.callSetTargetingBidder(bids[0].bidder, bids[0], adUnits); + sinon.assert.called(criteoSpec.onSetTargeting); + }); - it('should call spec\'s onBidViewable callback when callBidViewableBidder is called', function () { - const bids = [ - {bidder: 'criteo', params: {placementId: 'id'}}, - ]; - const adUnits = [{ - code: 'adUnit-code', - sizes: [[728, 90]], - bids - }]; - adapterManager.callBidViewableBidder(bids[0].bidder, bids[0]); - sinon.assert.called(criteoSpec.onBidViewable); + it('should NOT call onSetTargeting when bid is S2S', () => { + bids[0].src = CONSTANTS.S2S.SRC; + adapterManager.callSetTargetingBidder(bids[0].bidder, bids[0], adUnits); + sinon.assert.notCalled(criteoSpec.onSetTargeting); + }) + }); // end onSetTargeting + describe('onBidViewable', function () { + beforeEach(() => { + criteoSpec.onBidViewable = sinon.stub(); + }) + it('should call spec\'s onBidViewable callback when callBidViewableBidder is called', function () { + adapterManager.callBidViewableBidder(bids[0].bidder, bids[0]); + sinon.assert.called(criteoSpec.onBidViewable); + }); + it('should NOT call onBidViewable when bid is S2S', () => { + bids[0].src = CONSTANTS.S2S.SRC; + adapterManager.callBidViewableBidder(bids[0].bidder, bids[0]); + sinon.assert.notCalled(criteoSpec.onBidViewable); + }) }); - }); // end onBidViewable - + }) describe('onBidderError', function () { const bidder = 'appnexus'; const appnexusSpec = { onBidderError: sinon.stub() }; @@ -1721,6 +1698,129 @@ describe('adapterManager tests', function () { expect(sizes1).not.to.deep.equal(sizes2); }); + describe('and activity controls', () => { + let redactOrtb2; + let redactBidRequest; + const MOCK_BIDDERS = ['1', '2', '3', '4', '5'].map((n) => `mockBidder${n}`); + + beforeEach(() => { + sinon.stub(dep, 'isAllowed'); + redactOrtb2 = sinon.stub().callsFake(ob => ob); + redactBidRequest = sinon.stub().callsFake(ob => ob); + sinon.stub(dep, 'redact').callsFake(() => ({ + ortb2: redactOrtb2, + bidRequest: redactBidRequest + })) + MOCK_BIDDERS.forEach((bidder) => adapterManager.bidderRegistry[bidder] = {}); + }); + afterEach(() => { + dep.isAllowed.restore(); + dep.redact.restore(); + MOCK_BIDDERS.forEach(bidder => { delete adapterManager.bidderRegistry[bidder] }); + config.resetConfig(); + }) + it('should not generate requests for bidders that cannot fetchBids', () => { + adUnits = [ + {code: 'one', bids: ['mockBidder1', 'mockBidder2', 'mockBidder3'].map((bidder) => ({bidder}))}, + {code: 'two', bids: ['mockBidder4', 'mockBidder5', 'mockBidder4'].map((bidder) => ({bidder}))} + ]; + const allowed = ['mockBidder2', 'mockBidder5']; + dep.isAllowed.callsFake((activity, {componentType, componentName}) => { + return activity === ACTIVITY_FETCH_BIDS && + componentType === MODULE_TYPE_BIDDER && + allowed.includes(componentName); + }); + let reqs = adapterManager.makeBidRequests( + adUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + [] + ); + const bidders = Array.from(new Set(reqs.flatMap(br => br.bids).map(bid => bid.bidder)).keys()); + expect(bidders).to.have.members(allowed); + }); + + it('should redact ortb2 and bid request objects', () => { + dep.isAllowed.callsFake(() => true); + adUnits = [ + {code: 'one', bids: [{bidder: 'mockBidder1'}]} + ]; + let reqs = adapterManager.makeBidRequests( + adUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + [] + ); + sinon.assert.calledWith(redactBidRequest, reqs[0].bids[0]); + sinon.assert.calledWith(redactOrtb2, reqs[0].ortb2); + }) + + describe('with multiple s2s configs', () => { + beforeEach(() => { + config.setConfig({ + s2sConfig: [ + { + enabled: true, + adapter: 'mockS2SDefault', + bidders: ['mockBidder1'] + }, + { + enabled: true, + adapter: 'mockS2S1', + configName: 'mock1', + }, + { + enabled: true, + adapter: 'mockS2S2', + configName: 'mock2', + } + ] + }); + }); + it('should keep stored impressions, even if everything else is denied', () => { + adUnits = [ + {code: 'one', bids: [{bidder: null}]}, + {code: 'two', bids: [{module: 'pbsBidAdapter', params: {configName: 'mock1'}}, {module: 'pbsBidAdapter', params: {configName: 'mock2'}}]} + ] + dep.isAllowed.callsFake(({componentType}) => componentType !== 'bidder'); + let bidRequests = adapterManager.makeBidRequests( + adUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + [] + ); + expect(new Set(bidRequests.map(br => br.uniquePbsTid)).size).to.equal(3); + }); + + it('should check if the s2s adapter itself is allowed to fetch bids', () => { + adUnits = [ + { + code: 'au', + bids: [ + {bidder: null}, + {module: 'pbsBidAdapter', params: {configName: 'mock1'}}, + {module: 'pbsBidAdapter', params: {configName: 'mock2'}}, + {bidder: 'mockBidder1'} + ] + } + ]; + dep.isAllowed.callsFake((_, {configName, componentName}) => !(componentName === 'pbsBidAdapter' && configName === 'mock1')); + let bidRequests = adapterManager.makeBidRequests( + adUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() { + }, + [] + ); + expect(new Set(bidRequests.map(br => br.uniquePbsTid)).size).to.eql(2) + }); + }); + }); + it('should make FPD available under `ortb2`', () => { const global = { k1: 'v1', @@ -1762,6 +1862,23 @@ describe('adapterManager tests', function () { requests.appnexus.bids.forEach((bid) => expect(bid.ortb2).to.eql(requests.appnexus.ortb2)); }); + describe('source.tid', () => { + beforeEach(() => { + sinon.stub(dep, 'redact').returns({ + ortb2: (o) => o, + bidRequest: (b) => b, + }); + }); + afterEach(() => { + dep.redact.restore(); + }); + + it('should be populated with auctionId', () => { + const reqs = adapterManager.makeBidRequests(adUnits, 0, 'mockAuctionId', 1000, [], {global: {}}); + expect(reqs[0].ortb2.source.tid).to.equal('mockAuctionId'); + }) + }); + it('should merge in bid-level ortb2Imp with adUnit-level ortb2Imp', () => { const adUnit = { ...adUnits[1], @@ -2002,45 +2119,6 @@ describe('adapterManager tests', function () { expect(appnexusBidRequests.bids[1].mediaTypes).to.deep.equal(find(adUnits, adUnit => adUnit.code === appnexusBidRequests.bids[1].adUnitCode).mediaTypes); }); - it('should not filter video bids', function () { - setSizeConfig([{ - 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', - 'sizesSupported': [ - [728, 90], - [300, 250] - ], - 'labels': ['tablet', 'phone'] - }]); - - let videoAdUnits = [{ - code: 'test_video', - mediaTypes: { - video: { - playerSize: [300, 300], - context: 'outstream' - } - }, - bids: [{ - bidder: 'appnexus', - params: { - placementId: 13232385, - video: { - skippable: true, - playback_method: ['auto_play_sound_off'] - } - } - }] - }]; - let bidRequests = adapterManager.makeBidRequests( - videoAdUnits, - Date.now(), - utils.getUniqueIdentifierStr(), - function callback() {}, - [] - ); - expect(bidRequests[0].bids[0].sizes).to.deep.equal([300, 300]); - }); - it('should not filter native bids', function () { setSizeConfig([{ 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', @@ -2692,12 +2770,29 @@ describe('adapterManager tests', function () { beforeEach(() => { bidderRequests = []; + ['mockBidder', 'mockBidder1', 'mockBidder2'].forEach(bidder => { + adapterManager.registerBidAdapter({callBids: sinon.stub(), getSpec: () => ({code: bidder})}, bidder); + }) sinon.stub(auctionManager, 'getBidsRequested').callsFake(() => bidderRequests); }) afterEach(() => { auctionManager.getBidsRequested.restore(); }) + it('can resolve aliases', () => { + adapterManager.aliasBidAdapter('mockBidder', 'mockBidderAlias'); + expect(adapterManager.resolveAlias('mockBidderAlias')).to.eql('mockBidder'); + }); + it('does not stuck in alias cycles', () => { + adapterManager.aliasRegistry['alias1'] = 'alias2'; + adapterManager.aliasRegistry['alias2'] = 'alias2'; + expect(adapterManager.resolveAlias('alias2')).to.eql('alias2'); + }) + it('returns self when not an alias', () => { + delete adapterManager.aliasRegistry['missing']; + expect(adapterManager.resolveAlias('missing')).to.eql('missing'); + }) + it('does not invoke onDataDeletionRequest on aliases', () => { const del = delMethodForBidder('mockBidder'); adapterManager.aliasBidAdapter('mockBidder', 'mockBidderAlias'); @@ -2740,6 +2835,45 @@ describe('adapterManager tests', function () { }) }); + describe('reportAnalytics check', () => { + beforeEach(() => { + sinon.stub(dep, 'isAllowed'); + }); + afterEach(() => { + dep.isAllowed.restore(); + }); + + it('should check for reportAnalytics before registering analytics adapter', () => { + const enabled = {}; + ['mockAnalytics1', 'mockAnalytics2'].forEach((code) => { + adapterManager.registerAnalyticsAdapter({ + code, + adapter: { + enableAnalytics: sinon.stub().callsFake(() => { enabled[code] = true }) + } + }) + }) + + const anlCfg = [ + { + provider: 'mockAnalytics1', + random: 'values' + }, + { + provider: 'mockAnalytics2' + } + ] + dep.isAllowed.callsFake((activity, {component, _config}) => { + return activity === ACTIVITY_REPORT_ANALYTICS && + component === `${MODULE_TYPE_ANALYTICS}.${anlCfg[0].provider}` && + _config === anlCfg[0] + }) + + adapterManager.enableAnalytics(anlCfg); + expect(enabled).to.eql({mockAnalytics1: true}); + }); + }); + describe('registers GVL IDs', () => { beforeEach(() => { sinon.stub(GDPR_GVLIDS, 'register'); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 568c3bfdd09..751c90af50f 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1,18 +1,10 @@ -import { - addComponentAuction, - isValid, - newBidder, - preloadBidderMappingFile, - registerBidder, - storage -} from 'src/adapters/bidderFactory.js'; +import {addComponentAuction, isValid, newBidder, registerBidder} from 'src/adapters/bidderFactory.js'; import adapterManager from 'src/adapterManager.js'; import * as ajax from 'src/ajax.js'; import {expect} from 'chai'; import {userSync} from 'src/userSync.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; -import {server} from 'test/mocks/xhr.js'; import CONSTANTS from 'src/constants.json'; import * as events from 'src/events.js'; import {hook} from '../../../../src/hook.js'; @@ -20,6 +12,10 @@ import {auctionManager} from '../../../../src/auctionManager.js'; import {stubAuctionIndex} from '../../../helpers/indexStub.js'; import {bidderSettings} from '../../../../src/bidderSettings.js'; import {decorateAdUnitsWithNativeParams} from '../../../../src/native.js'; +import * as activityRules from 'src/activities/rules.js'; +import {sandbox} from 'sinon'; +import {MODULE_TYPE_BIDDER} from '../../../../src/activities/modules.js'; +import {ACTIVITY_TRANSMIT_TID} from '../../../../src/activities/activities.js'; const CODE = 'sampleBidder'; const MOCK_BIDS_REQUEST = { @@ -74,24 +70,25 @@ describe('bidders created by newBidder', function () { }); describe('when the ajax response is irrelevant', function () { + let sandbox; let ajaxStub; let getConfigSpy; let aliasRegistryStub, aliasRegistry; beforeEach(function () { - ajaxStub = sinon.stub(ajax, 'ajax'); + sandbox = sinon.sandbox.create(); + sandbox.stub(activityRules, 'isActivityAllowed').callsFake(() => true); + ajaxStub = sandbox.stub(ajax, 'ajax'); addBidResponseStub.reset(); - getConfigSpy = sinon.spy(config, 'getConfig'); + getConfigSpy = sandbox.spy(config, 'getConfig'); doneStub.reset(); aliasRegistry = {}; - aliasRegistryStub = sinon.stub(adapterManager, 'aliasRegistry'); + aliasRegistryStub = sandbox.stub(adapterManager, 'aliasRegistry'); aliasRegistryStub.get(() => aliasRegistry); }); afterEach(function () { - ajaxStub.restore(); - getConfigSpy.restore(); - aliasRegistryStub.restore(); + sandbox.restore(); }); it('should let registerSyncs run with invalid alias and aliasSync enabled', function () { @@ -143,6 +140,74 @@ describe('bidders created by newBidder', function () { expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(false); }); + describe('transaction IDs', () => { + Object.entries({ + 'be hidden': false, + 'not be hidden': true, + }).forEach(([t, allowed]) => { + const expectation = allowed ? (val) => expect(val).to.exist : (val) => expect(val).to.not.exist; + + function checkBidRequest(br) { + ['auctionId', 'transactionId'].forEach((prop) => expectation(br[prop])); + } + + function checkBidderRequest(br) { + expectation(br.auctionId); + br.bids.forEach(checkBidRequest); + } + + it(`should ${t} from the spec logic when the transmitTid activity is${allowed ? '' : ' not'} allowed`, () => { + spec.isBidRequestValid.callsFake(br => { + checkBidRequest(br); + return true; + }); + spec.buildRequests.callsFake((bidReqs, bidderReq) => { + checkBidderRequest(bidderReq); + bidReqs.forEach(checkBidRequest); + return {method: 'POST'}; + }); + spec.interpretResponse.callsFake(() => [ + { + requestId: 'bid', + cpm: 123, + ttl: 300, + creativeId: 'crid', + netRevenue: true, + currency: 'USD' + } + ]) + activityRules.isActivityAllowed.reset(); + activityRules.isActivityAllowed.callsFake(() => allowed); + + ajaxStub.callsFake((_, callback) => callback.success(null, {getResponseHeader: sinon.stub()})); + + const bidder = newBidder(spec); + + bidder.callBids({ + bidderCode: 'mockBidder', + auctionId: 'aid', + bids: [ + { + adUnitCode: 'mockAU', + bidId: 'bid', + transactionId: 'tid', + auctionId: 'aid' + } + ] + }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + sinon.assert.calledWithMatch(activityRules.isActivityAllowed, ACTIVITY_TRANSMIT_TID, { + componentType: MODULE_TYPE_BIDDER, + componentName: 'mockBidder' + }); + sinon.assert.calledWithMatch(addBidResponseStub, sinon.match.any, { + transactionId: 'tid', + auctionId: 'aid' + }) + }); + }); + }); + it('should handle bad bid requests gracefully', function () { const bidder = newBidder(spec); @@ -1283,143 +1348,6 @@ describe('validate bid response: ', function () { }) }); -describe('preload mapping url hook', function() { - if (!FEATURES.VIDEO) { - return - } - - let fakeTranslationServer; - let getLocalStorageStub; - let adapterManagerStub; - let adUnits = [{ - code: 'midroll_1', - mediaTypes: { - video: { - context: 'adpod' - } - }, - bids: [ - { - bidder: 'sampleBidder1', - params: { - placementId: 14542875, - } - } - ] - }]; - - beforeEach(function () { - fakeTranslationServer = server; - getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); - adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); - config.setConfig({ - 'adpod': { - 'brandCategoryExclusion': true - } - }); - adapterManagerStub.withArgs('sampleBidder1').returns({ - getSpec: function() { - return { - 'getMappingFileInfo': function() { - return { - url: 'http://sample.com', - refreshInDays: 7, - key: `sampleBidder1MappingFile` - } - } - } - } - }); - }); - - afterEach(function() { - getLocalStorageStub.restore(); - adapterManagerStub.restore(); - config.resetConfig(); - }); - - it('should preload mapping url file', function() { - getLocalStorageStub.returns(null); - preloadBidderMappingFile(sinon.spy(), adUnits); - expect(fakeTranslationServer.requests.length).to.equal(1); - }); - - it('should preload mapping url file for all bidders', function() { - let adUnits = [{ - code: 'midroll_1', - mediaTypes: { - video: { - context: 'adpod' - } - }, - bids: [ - { - bidder: 'sampleBidder1', - params: { - placementId: 14542875, - } - }, - { - bidder: 'sampleBidder2', - params: { - placementId: 123456, - } - } - ] - }]; - getLocalStorageStub.returns(null); - adapterManagerStub.withArgs('sampleBidder2').returns({ - getSpec: function() { - return { - 'getMappingFileInfo': function() { - return { - url: 'http://sample.com', - refreshInDays: 7, - key: `sampleBidder2MappingFile` - } - } - } - } - }); - preloadBidderMappingFile(sinon.spy(), adUnits); - expect(fakeTranslationServer.requests.length).to.equal(2); - - config.setConfig({ - 'adpod': { - 'brandCategoryExclusion': false - } - }); - preloadBidderMappingFile(sinon.spy(), adUnits); - expect(fakeTranslationServer.requests.length).to.equal(2); - }); - - it('should make ajax call to update mapping file if data found in localstorage is expired', function() { - let clock = sinon.useFakeTimers(utils.timestamp()); - getLocalStorageStub.returns(JSON.stringify({ - lastUpdated: utils.timestamp() - 8 * 24 * 60 * 60 * 1000, - mapping: { - 'iab-1': '1' - } - })); - preloadBidderMappingFile(sinon.spy(), adUnits); - expect(fakeTranslationServer.requests.length).to.equal(1); - clock.restore(); - }); - - it('should not make ajax call to update mapping file if data found in localstorage and is not expired', function () { - let clock = sinon.useFakeTimers(utils.timestamp()); - getLocalStorageStub.returns(JSON.stringify({ - lastUpdated: utils.timestamp(), - mapping: { - 'iab-1': '1' - } - })); - preloadBidderMappingFile(sinon.spy(), adUnits); - expect(fakeTranslationServer.requests.length).to.equal(0); - clock.restore(); - }); -}); - describe('bid response isValid', () => { describe('size check', () => { let req, index; diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js index 9e31389d96f..edead126c2c 100644 --- a/test/spec/unit/core/storageManager_spec.js +++ b/test/spec/unit/core/storageManager_spec.js @@ -1,14 +1,25 @@ import { - getCoreStorageManager, getStorageManager, + deviceAccessRule, + getCoreStorageManager, newStorageManager, resetData, + STORAGE_TYPE_COOKIES, + STORAGE_TYPE_LOCALSTORAGE, + storageAllowedRule, storageCallbacks, - validateStorageEnforcement } from 'src/storageManager.js'; +import adapterManager from 'src/adapterManager.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import {hook} from '../../../../src/hook.js'; -import {MODULE_TYPE_BIDDER} from '../../../../src/activities/modules.js'; +import {MODULE_TYPE_BIDDER, MODULE_TYPE_PREBID} from '../../../../src/activities/modules.js'; +import {ACTIVITY_ACCESS_DEVICE} from '../../../../src/activities/activities.js'; +import { + ACTIVITY_PARAM_COMPONENT_NAME, + ACTIVITY_PARAM_COMPONENT_TYPE, + ACTIVITY_PARAM_STORAGE_TYPE +} from '../../../../src/activities/params.js'; +import {activityParams} from '../../../../src/activities/activityParams.js'; describe('storage manager', function() { before(() => { @@ -56,41 +67,42 @@ describe('storage manager', function() { deviceAccessSpy.restore(); }); - describe(`enforcement`, () => { - let validateHook; + describe(`accessDevice activity check`, () => { + let isAllowed; + + function mkManager(moduleType, moduleName) { + return newStorageManager({moduleType, moduleName}, {isAllowed}); + } beforeEach(() => { - validateHook = sinon.stub().callsFake(function (next, ...args) { - next.apply(this, args); - }); - validateStorageEnforcement.before(validateHook, 99); + isAllowed = sinon.stub(); }); - afterEach(() => { - validateStorageEnforcement.getHooks({hook: validateHook}).remove(); - config.resetConfig(); - }) - - Object.entries({ - 'core': () => getCoreStorageManager('mock'), - 'other': () => getStorageManager({moduleType: 'other', moduleName: 'mock'}) - }).forEach(([moduleType, getMgr]) => { - describe(`for ${moduleType} modules`, () => { - let storage; - beforeEach(() => { - storage = getMgr(); - }); - it(`should pass '${moduleType}' module type to consent enforcement`, () => { - storage.localStorageIsEnabled(); - expect(validateHook.args[0][1]).to.equal(moduleType); - }); + it('should pass module type and name as activity params', () => { + mkManager(MODULE_TYPE_PREBID, 'mockMod').localStorageIsEnabled(); + sinon.assert.calledWith(isAllowed, ACTIVITY_ACCESS_DEVICE, sinon.match({ + [ACTIVITY_PARAM_COMPONENT_TYPE]: MODULE_TYPE_PREBID, + [ACTIVITY_PARAM_COMPONENT_NAME]: 'mockMod', + [ACTIVITY_PARAM_STORAGE_TYPE]: STORAGE_TYPE_LOCALSTORAGE + })); + }); - it('should respect the deviceAccess flag', () => { - config.setConfig({deviceAccess: false}); - expect(storage.localStorageIsEnabled()).to.be.false - }); - }); + it('should deny access if activity is denied', () => { + isAllowed.returns(false); + const mgr = mkManager(MODULE_TYPE_PREBID, 'mockMod'); + mgr.setDataInLocalStorage('testKey', 'val'); + expect(mgr.getDataFromLocalStorage('testKey')).to.not.exist; }); + + it('should use bidder aliases when possible', () => { + adapterManager.registerBidAdapter({callBids: sinon.stub(), getSpec: () => ({})}, 'mockBidder'); + adapterManager.aliasBidAdapter('mockBidder', 'mockAlias'); + const mgr = mkManager(MODULE_TYPE_BIDDER, 'mockBidder'); + config.runWithBidder('mockAlias', () => mgr.cookiesAreEnabled()); + sinon.assert.calledWith(isAllowed, ACTIVITY_ACCESS_DEVICE, sinon.match({ + [ACTIVITY_PARAM_COMPONENT_NAME]: 'mockAlias' + })) + }) }) describe('localstorage forbidden access in 3rd-party context', function() { @@ -145,13 +157,26 @@ describe('storage manager', function() { }); }); - describe('when bidderSettings.allowStorage is defined', () => { + describe('deviceAccess control', () => { + afterEach(() => { + config.resetConfig() + }); + + it('should allow by default', () => { + config.resetConfig(); + expect(deviceAccessRule()).to.not.exist; + }); + + it('should deny access when set', () => { + config.setConfig({deviceAccess: false}); + sinon.assert.match(deviceAccessRule(), {allow: false}); + }) + }); + + describe('allowStorage access control rule', () => { const ALLOWED_BIDDER = 'allowed-bidder'; const ALLOW_KEY = 'storageAllowed'; - const COOKIE = 'test-cookie'; - const LS_KEY = 'test-localstorage'; - function mockBidderSettings(val) { return { get(bidder, key) { @@ -213,39 +238,22 @@ describe('storage manager', function() { }).forEach(([t, {configValues, shouldWork: {cookie, html5}}]) => { describe(`when ${t} is allowed`, () => { configValues.forEach(configValue => describe(`storageAllowed = ${configValue}`, () => { - let mgr; - - beforeEach(() => { - mgr = newStorageManager({moduleType: MODULE_TYPE_BIDDER, moduleName: bidderCode}, {bidderSettings: mockBidderSettings(configValue)}); - }) - - afterEach(() => { - mgr.setCookie(COOKIE, 'delete', new Date().toUTCString()); - mgr.removeDataFromLocalStorage(LS_KEY); - }) - - function scenario(type, desc, fn) { + Object.entries({ + [STORAGE_TYPE_LOCALSTORAGE]: 'allow localStorage', + [STORAGE_TYPE_COOKIES]: 'allow cookies' + }).forEach(([type, desc]) => { const shouldWork = isBidderAllowed && ({html5, cookie})[type]; - it(`${shouldWork ? '' : 'NOT'} ${desc}`, () => fn(shouldWork)); - } - - scenario('cookie', 'allow cookies', (shouldWork) => { - mgr.setCookie(COOKIE, 'value'); - expect(mgr.getCookie(COOKIE)).to.equal(shouldWork ? 'value' : null); - }); - - scenario('html5', 'allow localStorage', (shouldWork) => { - mgr.setDataInLocalStorage(LS_KEY, 'value'); - expect(mgr.getDataFromLocalStorage(LS_KEY)).to.equal(shouldWork ? 'value' : null); - }); - - scenario('html5', 'report localStorage as available', (shouldWork) => { - expect(mgr.hasLocalStorage()).to.equal(shouldWork); - }); - - scenario('cookie', 'report cookies as available', (shouldWork) => { - expect(mgr.cookiesAreEnabled()).to.equal(shouldWork); - }); + it(`${shouldWork ? '' : 'NOT'} ${desc}`, () => { + const res = storageAllowedRule(activityParams(MODULE_TYPE_BIDDER, bidderCode, { + [ACTIVITY_PARAM_STORAGE_TYPE]: type + }), mockBidderSettings(configValue)); + if (shouldWork) { + expect(res).to.not.exist; + } else { + sinon.assert.match(res, {allow: false}); + } + }); + }) })); }); }); diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index f9fe2e398b0..f16d6208087 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -334,12 +334,6 @@ describe('targeting tests', function () { bidExpiryStub.restore(); }); - it('should filter out NO_BID bids', () => { - bidsReceived = [mkBid(sampleBid, CONSTANTS.STATUS.NO_BID)]; - const tg = targetingInstance.getAllTargeting(); - expect(tg[bidsReceived[0].adUnitCode]).to.eql({}); - }); - describe('when handling different adunit targeting value types', function () { const adUnitCode = '/123456/header-bid-tag-0'; const adServerTargeting = {}; diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 58b90d38ddb..5c361d186c0 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -25,6 +25,7 @@ import {stubAuctionIndex} from '../../helpers/indexStub.js'; import {createBid} from '../../../src/bidfactory.js'; import {enrichFPD} from '../../../src/fpd/enrichment.js'; import {mockFpdEnrichments} from '../../helpers/fpd.js'; + var assert = require('chai').assert; var expect = require('chai').expect; @@ -3349,66 +3350,55 @@ describe('Unit: Prebid Module', function () { if (FEATURES.VIDEO) { describe('markWinningBidAsUsed', function () { - it('marks the bid object as used for the given adUnitCode/adId combination', function () { - // make sure the auction has "state" and does not reload the fixtures - const adUnitCode = '/19968336/header-bid-tag-0'; + const adUnitCode = '/19968336/header-bid-tag-0'; + let winningBid; + + beforeEach(() => { const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); auction.getBidsReceived = function() { return bidsReceived.bids }; // mark the bid and verify the state has changed to RENDERED - const winningBid = targeting.getWinningBids(adUnitCode)[0]; + winningBid = targeting.getWinningBids(adUnitCode)[0]; + auction.getAuctionId = function() { return winningBid.auctionId }; + }) + + afterEach(() => { + resetAuction(); + }) + + it('marks the bid object as used for the given adUnitCode/adId combination', function () { + // make sure the auction has "state" and does not reload the fixtures $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode, adId: winningBid.adId }); const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, bid => bid.adId === winningBid.adId); expect(markedBid.status).to.equal(CONSTANTS.BID_STATUS.RENDERED); - resetAuction(); }); it('try and mark the bid object, but fail because we supplied the wrong adId', function () { - const adUnitCode = '/19968336/header-bid-tag-0'; - const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); - auction.getBidsReceived = function() { return bidsReceived.bids }; - - const winningBid = targeting.getWinningBids(adUnitCode)[0]; $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode, adId: 'miss' }); const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, bid => bid.adId === winningBid.adId); expect(markedBid.status).to.not.equal(CONSTANTS.BID_STATUS.RENDERED); - resetAuction(); }); it('marks the winning bid object as used for the given adUnitCode', function () { // make sure the auction has "state" and does not reload the fixtures - const adUnitCode = '/19968336/header-bid-tag-0'; - const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); - auction.getBidsReceived = function() { return bidsReceived.bids }; - - // mark the bid and verify the state has changed to RENDERED - const winningBid = targeting.getWinningBids(adUnitCode)[0]; $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode }); const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, bid => bid.adId === winningBid.adId); expect(markedBid.status).to.equal(CONSTANTS.BID_STATUS.RENDERED); - resetAuction(); }); it('marks a bid object as used for the given adId', function () { // make sure the auction has "state" and does not reload the fixtures - const adUnitCode = '/19968336/header-bid-tag-0'; - const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); - auction.getBidsReceived = function() { return bidsReceived.bids }; - - // mark the bid and verify the state has changed to RENDERED - const winningBid = targeting.getWinningBids(adUnitCode)[0]; $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adId: winningBid.adId }); const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, bid => bid.adId === winningBid.adId); expect(markedBid.status).to.equal(CONSTANTS.BID_STATUS.RENDERED); - resetAuction(); }); }); } diff --git a/test/spec/userSync_spec.js b/test/spec/userSync_spec.js index 6d0953f68ac..c403014fcd6 100644 --- a/test/spec/userSync_spec.js +++ b/test/spec/userSync_spec.js @@ -1,5 +1,13 @@ import { expect } from 'chai'; import { config } from 'src/config.js'; +import {ruleRegistry} from '../../src/activities/rules.js'; +import {ACTIVITY_SYNC_USER} from '../../src/activities/activities.js'; +import { + ACTIVITY_PARAM_COMPONENT, + ACTIVITY_PARAM_SYNC_TYPE, + ACTIVITY_PARAM_SYNC_URL +} from '../../src/activities/params.js'; +import {MODULE_TYPE_BIDDER} from '../../src/activities/modules.js'; // Use require since we need to be able to write to these vars const utils = require('../../src/utils'); let { newUserSync, USERSYNC_DEFAULT_CONFIG } = require('../../src/userSync'); @@ -14,12 +22,18 @@ describe('user sync', function () { let idPrefix = 'test-generated-id-'; let lastId = 0; let defaultUserSyncConfig = config.getConfig('userSync'); - function getUserSyncConfig(userSyncConfig) { - return Object.assign({}, defaultUserSyncConfig, userSyncConfig); + let regRule, isAllowed; + + function mkUserSync(deps) { + [regRule, isAllowed] = ruleRegistry(); + return newUserSync(Object.assign({ + regRule, isAllowed + }, deps)) } + function newTestUserSync(configOverrides, disableBrowserCookies) { const thisConfig = Object.assign({}, defaultUserSyncConfig, configOverrides); - return newUserSync({ + return mkUserSync({ config: thisConfig, browserSupportsCookies: !disableBrowserCookies, }) @@ -59,6 +73,22 @@ describe('user sync', function () { expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); }); + it('should NOT fire a sync if a rule blocks syncUser', () => { + const userSync = newTestUserSync() + regRule(ACTIVITY_SYNC_USER, 'testRule', (params) => { + if ( + params[ACTIVITY_PARAM_COMPONENT] === `${MODULE_TYPE_BIDDER}.testBidder` && + params[ACTIVITY_PARAM_SYNC_TYPE] === 'image' && + params[ACTIVITY_PARAM_SYNC_URL] === 'http://example.com' + ) { + return {allow: false} + } + }) + userSync.registerSync('image', 'testBidder', 'http://example.com'); + userSync.syncUsers(); + expect(triggerPixelStub.called).to.be.false; + }) + it('should clear queue after sync', function () { const userSync = newTestUserSync(); userSync.syncUsers(); @@ -371,14 +401,13 @@ describe('user sync', function () { userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync.syncUsers(); - expect(logWarnStub.getCall(0).args[0]).to.exist; expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.be.null; }); it('should still allow default image syncs if setConfig only defined iframe', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: config.getConfig('userSync'), browserSupportsCookies: true }); @@ -403,7 +432,7 @@ describe('user sync', function () { }); it('should not fire image pixel for a bidder if iframe pixel is fired for same bidder', function() { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: config.getConfig('userSync'), browserSupportsCookies: true }); @@ -430,7 +459,7 @@ describe('user sync', function () { }); it('should override default image syncs if setConfig used image filter', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: config.getConfig('userSync'), browserSupportsCookies: true }); @@ -455,7 +484,7 @@ describe('user sync', function () { }); it('should override default image syncs if setConfig used all filter', function() { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: config.getConfig('userSync'), browserSupportsCookies: true }); @@ -488,7 +517,7 @@ describe('user sync', function () { describe('canBidderRegisterSync', function () { describe('with filterSettings', function () { it('should return false if filter settings does not allow it', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { filterSettings: { image: { @@ -505,7 +534,7 @@ describe('user sync', function () { expect(userSync.canBidderRegisterSync('iframe', 'otherTestBidder')).to.equal(false); }); it('should return false for iframe if there is no iframe filterSettings', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { syncEnabled: true, filterSettings: { @@ -523,7 +552,7 @@ describe('user sync', function () { expect(userSync.canBidderRegisterSync('iframe', 'otherTestBidder')).to.equal(false); }); it('should return true if filter settings does allow it', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { filterSettings: { image: { @@ -543,7 +572,7 @@ describe('user sync', function () { describe('almost deprecated - without filterSettings', function () { describe('enabledBidders contains testBidder', function () { it('should return false if type is iframe and iframeEnabled is false', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { filterSettings: { iframe: { @@ -557,7 +586,7 @@ describe('user sync', function () { }); it('should return true if type is iframe and iframeEnabled is true', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { pixelEnabled: true, iframeEnabled: true, @@ -568,7 +597,7 @@ describe('user sync', function () { }); it('should return false if type is image and pixelEnabled is false', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { filterSettings: { image: { @@ -582,7 +611,7 @@ describe('user sync', function () { }); it('should return true if type is image and pixelEnabled is true', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { pixelEnabled: true, iframeEnabled: true, @@ -595,7 +624,7 @@ describe('user sync', function () { describe('enabledBidders does not container testBidder', function () { it('should return false since testBidder is not in enabledBidders', function () { - const userSync = newUserSync({ + const userSync = mkUserSync({ config: { filterSettings: { image: { diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 9199b259ad6..e26683074c8 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1,8 +1,8 @@ -import { getAdServerTargeting } from 'test/fixtures/fixtures.js'; -import { expect } from 'chai'; +import {getAdServerTargeting} from 'test/fixtures/fixtures.js'; +import {expect} from 'chai'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; -import {memoize, deepEqual, waitForElementToLoad} from 'src/utils.js'; +import {deepEqual, memoize, waitForElementToLoad} from 'src/utils.js'; var assert = require('assert'); @@ -894,93 +894,24 @@ describe('Utils', function () { }); }); - describe('transformBidderParamKeywords', function () { - it('returns an array of objects when keyvalue is an array', function () { - let keywords = { - genre: ['rock', 'pop'] - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'genre', - value: ['rock', 'pop'] - }]); - }); - - it('returns an array of objects when keyvalue is a string', function () { - let keywords = { - genre: 'opera' - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'genre', - value: ['opera'] - }]); - }); - - it('returns an array of objects when keyvalue is a number', function () { - let keywords = { - age: 15 - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'age', - value: ['15'] - }]); - }); - - it('returns an array of objects when using multiple keys with values of differing types', function () { - let keywords = { - genre: 'classical', - mix: ['1', 2, '3', 4], - age: 10 - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'genre', - value: ['classical'] - }, { - key: 'mix', - value: ['1', '2', '3', '4'] - }, { - key: 'age', - value: ['10'] - }]); - }); - - it('returns an array of objects when the keyvalue uses an empty string', function() { - let keywords = { - test: [''], - test2: '' - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'test', - value: [''] - }, { - key: 'test2', - value: [''] - }]); - }); - - describe('insertElement', function () { - it('returns a node at the top of the target by default', function () { - const toInsert = document.createElement('div'); - const target = document.getElementsByTagName('body')[0]; - const inserted = utils.insertElement(toInsert, document, 'body'); - expect(inserted).to.equal(target.firstChild); - }); - it('returns a node at bottom of target if 4th argument is true', function () { - const toInsert = document.createElement('div'); - const target = document.getElementsByTagName('html')[0]; - const inserted = utils.insertElement(toInsert, document, 'html', true); - expect(inserted).to.equal(target.lastChild); - }); - it('returns a node at top of the head if no target is given', function () { - const toInsert = document.createElement('div'); - const target = document.getElementsByTagName('head')[0]; - const inserted = utils.insertElement(toInsert); - expect(inserted).to.equal(target.firstChild); - }); + describe('insertElement', function () { + it('returns a node at the top of the target by default', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('body')[0]; + const inserted = utils.insertElement(toInsert, document, 'body'); + expect(inserted).to.equal(target.firstChild); + }); + it('returns a node at bottom of target if 4th argument is true', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('html')[0]; + const inserted = utils.insertElement(toInsert, document, 'html', true); + expect(inserted).to.equal(target.lastChild); + }); + it('returns a node at top of the head if no target is given', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('head')[0]; + const inserted = utils.insertElement(toInsert); + expect(inserted).to.equal(target.firstChild); }); });