From 58c49e5fcf77deae120f895f243c79e860fdb340 Mon Sep 17 00:00:00 2001
From: Matt Crute <872334+mbcrute@users.noreply.github.com>
Date: Thu, 20 Apr 2023 14:46:10 -0400
Subject: [PATCH] Support VIDEO feature flag in AppNexus bid adapter (#9653)
---
modules/appnexusBidAdapter.js | 210 ++--
test/spec/modules/appnexusBidAdapter_spec.js | 912 +++++++++---------
.../modules/big-richmediaBidAdapter_spec.js | 132 +--
test/spec/unit/pbjs_api_spec.js | 22 +-
4 files changed, 659 insertions(+), 617 deletions(-)
diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js
index 8f499e1e31e..cf3763be9c8 100644
--- a/modules/appnexusBidAdapter.js
+++ b/modules/appnexusBidAdapter.js
@@ -333,14 +333,16 @@ export const spec = {
payload.referrer_detection = refererinfo;
}
- const hasAdPodBid = find(bidRequests, hasAdPod);
- if (hasAdPodBid) {
- bidRequests.filter(hasAdPod).forEach(adPodBid => {
- const adPodTags = createAdPodRequest(tags, adPodBid);
- // don't need the original adpod placement because it's in adPodTags
- const nonPodTags = payload.tags.filter(tag => tag.uuid !== adPodBid.bidId);
- payload.tags = [...nonPodTags, ...adPodTags];
- });
+ if (FEATURES.VIDEO) {
+ const hasAdPodBid = find(bidRequests, hasAdPod);
+ if (hasAdPodBid) {
+ bidRequests.filter(hasAdPod).forEach(adPodBid => {
+ const adPodTags = createAdPodRequest(tags, adPodBid);
+ // don't need the original adpod placement because it's in adPodTags
+ const nonPodTags = payload.tags.filter(tag => tag.uuid !== adPodBid.bidId);
+ payload.tags = [...nonPodTags, ...adPodTags];
+ });
+ }
}
if (bidRequests[0].userId) {
@@ -653,7 +655,7 @@ function newBid(serverBid, rtbBid, bidderRequest) {
bid.meta = Object.assign({}, bid.meta, { brandId: rtbBid.brand_id });
}
- if (rtbBid.rtb.video) {
+ if (FEATURES.VIDEO && rtbBid.rtb.video) {
// shared video properties used for all 3 contexts
Object.assign(bid, {
width: rtbBid.rtb.video.player_width,
@@ -865,107 +867,111 @@ function bidToTag(bid) {
}
}
- const videoMediaType = deepAccess(bid, `mediaTypes.${VIDEO}`);
- const context = deepAccess(bid, 'mediaTypes.video.context');
-
- if (videoMediaType && context === 'adpod') {
- tag.hb_source = 7;
- } else {
- tag.hb_source = 1;
- }
- if (bid.mediaType === VIDEO || videoMediaType) {
- tag.ad_types.push(VIDEO);
- }
-
- // instream gets vastUrl, outstream gets vastXml
- if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) {
- tag.require_asset_url = true;
- }
+ if (FEATURES.VIDEO) {
+ const videoMediaType = deepAccess(bid, `mediaTypes.${VIDEO}`);
+ const context = deepAccess(bid, 'mediaTypes.video.context');
- if (bid.params.video) {
- tag.video = {};
- // place any valid video params on the tag
- Object.keys(bid.params.video)
- .filter(param => includes(VIDEO_TARGETING, param))
- .forEach(param => {
- switch (param) {
- case 'context':
- case 'playback_method':
- let type = bid.params.video[param];
- type = (isArray(type)) ? type[0] : type;
- tag.video[param] = VIDEO_MAPPING[param][type];
- break;
- // Deprecating tags[].video.frameworks in favor of tags[].video_frameworks
- case 'frameworks':
- break;
- default:
- tag.video[param] = bid.params.video[param];
- }
- });
+ if (videoMediaType && context === 'adpod') {
+ tag.hb_source = 7;
+ } else {
+ tag.hb_source = 1;
+ }
+ if (bid.mediaType === VIDEO || videoMediaType) {
+ tag.ad_types.push(VIDEO);
+ }
- if (bid.params.video.frameworks && isArray(bid.params.video.frameworks)) {
- tag['video_frameworks'] = bid.params.video.frameworks;
+ // instream gets vastUrl, outstream gets vastXml
+ if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) {
+ tag.require_asset_url = true;
}
- }
- // use IAB ORTB values if the corresponding values weren't already set by bid.params.video
- if (videoMediaType) {
- tag.video = tag.video || {};
- Object.keys(videoMediaType)
- .filter(param => includes(VIDEO_RTB_TARGETING, param))
- .forEach(param => {
- switch (param) {
- case 'minduration':
- case 'maxduration':
- if (typeof tag.video[param] !== 'number') tag.video[param] = videoMediaType[param];
- break;
- case 'skip':
- if (typeof tag.video['skippable'] !== 'boolean') tag.video['skippable'] = (videoMediaType[param] === 1);
- break;
- case 'skipafter':
- if (typeof tag.video['skipoffset'] !== 'number') tag.video['skippoffset'] = videoMediaType[param];
- break;
- case 'playbackmethod':
- if (typeof tag.video['playback_method'] !== 'number') {
- let type = videoMediaType[param];
+ if (bid.params.video) {
+ tag.video = {};
+ // place any valid video params on the tag
+ Object.keys(bid.params.video)
+ .filter(param => includes(VIDEO_TARGETING, param))
+ .forEach(param => {
+ switch (param) {
+ case 'context':
+ case 'playback_method':
+ let type = bid.params.video[param];
type = (isArray(type)) ? type[0] : type;
+ tag.video[param] = VIDEO_MAPPING[param][type];
+ break;
+ // Deprecating tags[].video.frameworks in favor of tags[].video_frameworks
+ case 'frameworks':
+ break;
+ default:
+ tag.video[param] = bid.params.video[param];
+ }
+ });
- // we only support iab's options 1-4 at this time.
- if (type >= 1 && type <= 4) {
- tag.video['playback_method'] = type;
- }
- }
- break;
- case 'api':
- if (!tag['video_frameworks'] && isArray(videoMediaType[param])) {
- // need to read thru array; remove 6 (we don't support it), swap 4 <> 5 if found (to match our adserver mapping for these specific values)
- let apiTmp = videoMediaType[param].map(val => {
- let v = (val === 4) ? 5 : (val === 5) ? 4 : val;
-
- if (v >= 1 && v <= 5) {
- return v;
+ if (bid.params.video.frameworks && isArray(bid.params.video.frameworks)) {
+ tag['video_frameworks'] = bid.params.video.frameworks;
+ }
+ }
+
+ // use IAB ORTB values if the corresponding values weren't already set by bid.params.video
+ if (videoMediaType) {
+ tag.video = tag.video || {};
+ Object.keys(videoMediaType)
+ .filter(param => includes(VIDEO_RTB_TARGETING, param))
+ .forEach(param => {
+ switch (param) {
+ case 'minduration':
+ case 'maxduration':
+ if (typeof tag.video[param] !== 'number') tag.video[param] = videoMediaType[param];
+ break;
+ case 'skip':
+ if (typeof tag.video['skippable'] !== 'boolean') tag.video['skippable'] = (videoMediaType[param] === 1);
+ break;
+ case 'skipafter':
+ if (typeof tag.video['skipoffset'] !== 'number') tag.video['skippoffset'] = videoMediaType[param];
+ break;
+ case 'playbackmethod':
+ if (typeof tag.video['playback_method'] !== 'number') {
+ let type = videoMediaType[param];
+ type = (isArray(type)) ? type[0] : type;
+
+ // we only support iab's options 1-4 at this time.
+ if (type >= 1 && type <= 4) {
+ tag.video['playback_method'] = type;
}
- }).filter(v => v);
- tag['video_frameworks'] = apiTmp;
- }
- break;
-
- case 'startdelay':
- case 'placement':
- const contextKey = 'context';
- if (typeof tag.video[contextKey] !== 'number') {
- const placement = videoMediaType['placement'];
- const startdelay = videoMediaType['startdelay'];
- const context = getContextFromPlacement(placement) || getContextFromStartDelay(startdelay);
- tag.video[contextKey] = VIDEO_MAPPING[contextKey][context];
- }
- break;
- }
- });
- }
+ }
+ break;
+ case 'api':
+ if (!tag['video_frameworks'] && isArray(videoMediaType[param])) {
+ // need to read thru array; remove 6 (we don't support it), swap 4 <> 5 if found (to match our adserver mapping for these specific values)
+ let apiTmp = videoMediaType[param].map(val => {
+ let v = (val === 4) ? 5 : (val === 5) ? 4 : val;
+
+ if (v >= 1 && v <= 5) {
+ return v;
+ }
+ }).filter(v => v);
+ tag['video_frameworks'] = apiTmp;
+ }
+ break;
+
+ case 'startdelay':
+ case 'placement':
+ const contextKey = 'context';
+ if (typeof tag.video[contextKey] !== 'number') {
+ const placement = videoMediaType['placement'];
+ const startdelay = videoMediaType['startdelay'];
+ const context = getContextFromPlacement(placement) || getContextFromStartDelay(startdelay);
+ tag.video[contextKey] = VIDEO_MAPPING[contextKey][context];
+ }
+ break;
+ }
+ });
+ }
- if (bid.renderer) {
- tag.video = Object.assign({}, tag.video, { custom_renderer_present: true });
+ if (bid.renderer) {
+ tag.video = Object.assign({}, tag.video, { custom_renderer_present: true });
+ }
+ } else {
+ tag.hb_source = 1;
}
if (bid.params.frameworks && isArray(bid.params.frameworks)) {
diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js
index 9e88e2875c7..1603c6e9397 100644
--- a/test/spec/modules/appnexusBidAdapter_spec.js
+++ b/test/spec/modules/appnexusBidAdapter_spec.js
@@ -257,11 +257,15 @@ describe('AppNexusAdapter', function () {
transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843'
}];
- let types = ['banner', 'video'];
+ let types = ['banner'];
if (FEATURES.NATIVE) {
types.push('native');
}
+ if (FEATURES.VIDEO) {
+ types.push('video');
+ }
+
types.forEach(type => {
getAdUnitsStub.callsFake(function (...args) {
return adUnits;
@@ -290,122 +294,303 @@ describe('AppNexusAdapter', function () {
expect(payload.tags[0].ad_types).to.not.exist;
});
- it('should populate the ad_types array on outstream requests', function () {
- const bidRequest = Object.assign({}, bidRequests[0]);
- bidRequest.mediaTypes = {};
- bidRequest.mediaTypes.video = { context: 'outstream' };
+ if (FEATURES.VIDEO) {
+ it('should populate the ad_types array on outstream requests', function () {
+ const bidRequest = Object.assign({}, bidRequests[0]);
+ bidRequest.mediaTypes = {};
+ bidRequest.mediaTypes.video = { context: 'outstream' };
- const request = spec.buildRequests([bidRequest]);
- const payload = JSON.parse(request.data);
+ const request = spec.buildRequests([bidRequest]);
+ const payload = JSON.parse(request.data);
- expect(payload.tags[0].ad_types).to.deep.equal(['video']);
- expect(payload.tags[0].hb_source).to.deep.equal(1);
- });
+ expect(payload.tags[0].ad_types).to.deep.equal(['video']);
+ expect(payload.tags[0].hb_source).to.deep.equal(1);
+ });
- it('sends bid request to ENDPOINT via POST', function () {
- const request = spec.buildRequests(bidRequests);
- expect(request.url).to.equal(ENDPOINT);
- expect(request.method).to.equal('POST');
- });
+ it('should attach valid video params to the tag', function () {
+ let bidRequest = Object.assign({},
+ bidRequests[0],
+ {
+ params: {
+ placementId: '10433394',
+ video: {
+ id: 123,
+ minduration: 100,
+ foobar: 'invalid'
+ }
+ }
+ }
+ );
- it('should attach valid video params to the tag', function () {
- let bidRequest = Object.assign({},
- bidRequests[0],
- {
+ const request = spec.buildRequests([bidRequest]);
+ const payload = JSON.parse(request.data);
+ expect(payload.tags[0].video).to.deep.equal({
+ id: 123,
+ minduration: 100
+ });
+ expect(payload.tags[0].hb_source).to.deep.equal(1);
+ });
+
+ it('should include ORTB video values when video params were not set', function () {
+ let bidRequest = deepClone(bidRequests[0]);
+ bidRequest.params = {
+ placementId: '1234235',
+ video: {
+ skippable: true,
+ playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'],
+ context: 'outstream'
+ }
+ };
+ bidRequest.mediaTypes = {
+ video: {
+ playerSize: [640, 480],
+ context: 'outstream',
+ mimes: ['video/mp4'],
+ skip: 0,
+ minduration: 5,
+ api: [1, 5, 6],
+ playbackmethod: [2, 4]
+ }
+ };
+
+ const request = spec.buildRequests([bidRequest]);
+ const payload = JSON.parse(request.data);
+
+ expect(payload.tags[0].video).to.deep.equal({
+ minduration: 5,
+ playback_method: 2,
+ skippable: true,
+ context: 4
+ });
+ expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4])
+ });
+
+ it('should add video property when adUnit includes a renderer', function () {
+ const videoData = {
+ mediaTypes: {
+ video: {
+ context: 'outstream',
+ mimes: ['video/mp4']
+ }
+ },
params: {
placementId: '10433394',
video: {
- id: 123,
- minduration: 100,
- foobar: 'invalid'
+ skippable: true,
+ playback_method: ['auto_play_sound_off']
}
}
- }
- );
+ };
- const request = spec.buildRequests([bidRequest]);
- const payload = JSON.parse(request.data);
- expect(payload.tags[0].video).to.deep.equal({
- id: 123,
- minduration: 100
- });
- expect(payload.tags[0].hb_source).to.deep.equal(1);
- });
+ let bidRequest1 = deepClone(bidRequests[0]);
+ bidRequest1 = Object.assign({}, bidRequest1, videoData, {
+ renderer: {
+ url: 'https://test.renderer.url',
+ render: function () { }
+ }
+ });
- it('should include ORTB video values when video params were not set', function () {
- let bidRequest = deepClone(bidRequests[0]);
- bidRequest.params = {
- placementId: '1234235',
- video: {
+ let bidRequest2 = deepClone(bidRequests[0]);
+ bidRequest2.adUnitCode = 'adUnit_code_2';
+ bidRequest2 = Object.assign({}, bidRequest2, videoData);
+
+ const request = spec.buildRequests([bidRequest1, bidRequest2]);
+ const payload = JSON.parse(request.data);
+ expect(payload.tags[0].video).to.deep.equal({
skippable: true,
- playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'],
- context: 'outstream'
- }
- };
- bidRequest.mediaTypes = {
- video: {
- playerSize: [640, 480],
- context: 'outstream',
- mimes: ['video/mp4'],
- skip: 0,
- minduration: 5,
- api: [1, 5, 6],
- playbackmethod: [2, 4]
- }
- };
+ playback_method: 2,
+ custom_renderer_present: true
+ });
+ expect(payload.tags[1].video).to.deep.equal({
+ skippable: true,
+ playback_method: 2
+ });
+ });
- const request = spec.buildRequests([bidRequest]);
- const payload = JSON.parse(request.data);
+ it('should duplicate adpod placements into batches and set correct maxduration', function () {
+ let bidRequest = Object.assign({},
+ bidRequests[0],
+ {
+ params: { placementId: '14542875' }
+ },
+ {
+ mediaTypes: {
+ video: {
+ context: 'adpod',
+ playerSize: [640, 480],
+ adPodDurationSec: 300,
+ durationRangeSec: [15, 30],
+ }
+ }
+ }
+ );
- expect(payload.tags[0].video).to.deep.equal({
- minduration: 5,
- playback_method: 2,
- skippable: true,
- context: 4
+ const request = spec.buildRequests([bidRequest]);
+ const payload1 = JSON.parse(request[0].data);
+ const payload2 = JSON.parse(request[1].data);
+
+ // 300 / 15 = 20 total
+ expect(payload1.tags.length).to.equal(15);
+ expect(payload2.tags.length).to.equal(5);
+
+ expect(payload1.tags[0]).to.deep.equal(payload1.tags[1]);
+ expect(payload1.tags[0].video.maxduration).to.equal(30);
+
+ expect(payload2.tags[0]).to.deep.equal(payload1.tags[1]);
+ expect(payload2.tags[0].video.maxduration).to.equal(30);
});
- expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4])
- });
- it('should add video property when adUnit includes a renderer', function () {
- const videoData = {
- mediaTypes: {
- video: {
- context: 'outstream',
- mimes: ['video/mp4']
+ it('should round down adpod placements when numbers are uneven', function () {
+ let bidRequest = Object.assign({},
+ bidRequests[0],
+ {
+ params: { placementId: '14542875' }
+ },
+ {
+ mediaTypes: {
+ video: {
+ context: 'adpod',
+ playerSize: [640, 480],
+ adPodDurationSec: 123,
+ durationRangeSec: [45],
+ }
+ }
}
- },
- params: {
- placementId: '10433394',
- video: {
- skippable: true,
- playback_method: ['auto_play_sound_off']
+ );
+
+ const request = spec.buildRequests([bidRequest]);
+ const payload = JSON.parse(request.data);
+ expect(payload.tags.length).to.equal(2);
+ });
+
+ it('should duplicate adpod placements when requireExactDuration is set', function () {
+ let bidRequest = Object.assign({},
+ bidRequests[0],
+ {
+ params: { placementId: '14542875' }
+ },
+ {
+ mediaTypes: {
+ video: {
+ context: 'adpod',
+ playerSize: [640, 480],
+ adPodDurationSec: 300,
+ durationRangeSec: [15, 30],
+ requireExactDuration: true,
+ }
+ }
}
- }
- };
+ );
- let bidRequest1 = deepClone(bidRequests[0]);
- bidRequest1 = Object.assign({}, bidRequest1, videoData, {
- renderer: {
- url: 'https://test.renderer.url',
- render: function () { }
- }
+ // 20 total placements with 15 max impressions = 2 requests
+ const request = spec.buildRequests([bidRequest]);
+ expect(request.length).to.equal(2);
+
+ // 20 spread over 2 requests = 15 in first request, 5 in second
+ const payload1 = JSON.parse(request[0].data);
+ const payload2 = JSON.parse(request[1].data);
+ expect(payload1.tags.length).to.equal(15);
+ expect(payload2.tags.length).to.equal(5);
+
+ // 10 placements should have max/min at 15
+ // 10 placemenst should have max/min at 30
+ const payload1tagsWith15 = payload1.tags.filter(tag => tag.video.maxduration === 15);
+ const payload1tagsWith30 = payload1.tags.filter(tag => tag.video.maxduration === 30);
+ expect(payload1tagsWith15.length).to.equal(10);
+ expect(payload1tagsWith30.length).to.equal(5);
+
+ // 5 placemenst with min/max at 30 were in the first request
+ // so 5 remaining should be in the second
+ const payload2tagsWith30 = payload2.tags.filter(tag => tag.video.maxduration === 30);
+ expect(payload2tagsWith30.length).to.equal(5);
});
- let bidRequest2 = deepClone(bidRequests[0]);
- bidRequest2.adUnitCode = 'adUnit_code_2';
- bidRequest2 = Object.assign({}, bidRequest2, videoData);
+ it('should set durations for placements when requireExactDuration is set and numbers are uneven', function () {
+ let bidRequest = Object.assign({},
+ bidRequests[0],
+ {
+ params: { placementId: '14542875' }
+ },
+ {
+ mediaTypes: {
+ video: {
+ context: 'adpod',
+ playerSize: [640, 480],
+ adPodDurationSec: 105,
+ durationRangeSec: [15, 30, 60],
+ requireExactDuration: true,
+ }
+ }
+ }
+ );
- const request = spec.buildRequests([bidRequest1, bidRequest2]);
- const payload = JSON.parse(request.data);
- expect(payload.tags[0].video).to.deep.equal({
- skippable: true,
- playback_method: 2,
- custom_renderer_present: true
+ const request = spec.buildRequests([bidRequest]);
+ const payload = JSON.parse(request.data);
+ expect(payload.tags.length).to.equal(7);
+
+ const tagsWith15 = payload.tags.filter(tag => tag.video.maxduration === 15);
+ const tagsWith30 = payload.tags.filter(tag => tag.video.maxduration === 30);
+ const tagsWith60 = payload.tags.filter(tag => tag.video.maxduration === 60);
+ expect(tagsWith15.length).to.equal(3);
+ expect(tagsWith30.length).to.equal(3);
+ expect(tagsWith60.length).to.equal(1);
});
- expect(payload.tags[1].video).to.deep.equal({
- skippable: true,
- playback_method: 2
+
+ it('should break adpod request into batches', function () {
+ let bidRequest = Object.assign({},
+ bidRequests[0],
+ {
+ params: { placementId: '14542875' }
+ },
+ {
+ mediaTypes: {
+ video: {
+ context: 'adpod',
+ playerSize: [640, 480],
+ adPodDurationSec: 225,
+ durationRangeSec: [5],
+ }
+ }
+ }
+ );
+
+ const request = spec.buildRequests([bidRequest]);
+ const payload1 = JSON.parse(request[0].data);
+ const payload2 = JSON.parse(request[1].data);
+ const payload3 = JSON.parse(request[2].data);
+
+ expect(payload1.tags.length).to.equal(15);
+ expect(payload2.tags.length).to.equal(15);
+ expect(payload3.tags.length).to.equal(15);
+ });
+
+ it('should contain hb_source value for adpod', function () {
+ let bidRequest = Object.assign({},
+ bidRequests[0],
+ {
+ params: { placementId: '14542875' }
+ },
+ {
+ mediaTypes: {
+ video: {
+ context: 'adpod',
+ playerSize: [640, 480],
+ adPodDurationSec: 300,
+ durationRangeSec: [15, 30],
+ }
+ }
+ }
+ );
+ const request = spec.buildRequests([bidRequest])[0];
+ const payload = JSON.parse(request.data);
+ expect(payload.tags[0].hb_source).to.deep.equal(7);
});
+ } // VIDEO
+
+ it('sends bid request to ENDPOINT via POST', function () {
+ const request = spec.buildRequests(bidRequests);
+ expect(request.url).to.equal(ENDPOINT);
+ expect(request.method).to.equal('POST');
});
it('should attach valid user params to the tag', function () {
@@ -486,185 +671,6 @@ describe('AppNexusAdapter', function () {
expect(payload.tags[0].reserve).to.exist.and.to.equal(3);
});
- it('should duplicate adpod placements into batches and set correct maxduration', function () {
- let bidRequest = Object.assign({},
- bidRequests[0],
- {
- params: { placement_id: '14542875' }
- },
- {
- mediaTypes: {
- video: {
- context: 'adpod',
- playerSize: [640, 480],
- adPodDurationSec: 300,
- durationRangeSec: [15, 30],
- }
- }
- }
- );
-
- const request = spec.buildRequests([bidRequest]);
- const payload1 = JSON.parse(request[0].data);
- const payload2 = JSON.parse(request[1].data);
-
- // 300 / 15 = 20 total
- expect(payload1.tags.length).to.equal(15);
- expect(payload2.tags.length).to.equal(5);
-
- expect(payload1.tags[0]).to.deep.equal(payload1.tags[1]);
- expect(payload1.tags[0].video.maxduration).to.equal(30);
-
- expect(payload2.tags[0]).to.deep.equal(payload1.tags[1]);
- expect(payload2.tags[0].video.maxduration).to.equal(30);
- });
-
- it('should round down adpod placements when numbers are uneven', function () {
- let bidRequest = Object.assign({},
- bidRequests[0],
- {
- params: { placement_id: '14542875' }
- },
- {
- mediaTypes: {
- video: {
- context: 'adpod',
- playerSize: [640, 480],
- adPodDurationSec: 123,
- durationRangeSec: [45],
- }
- }
- }
- );
-
- const request = spec.buildRequests([bidRequest]);
- const payload = JSON.parse(request.data);
- expect(payload.tags.length).to.equal(2);
- });
-
- it('should duplicate adpod placements when requireExactDuration is set', function () {
- let bidRequest = Object.assign({},
- bidRequests[0],
- {
- params: { placement_id: '14542875' }
- },
- {
- mediaTypes: {
- video: {
- context: 'adpod',
- playerSize: [640, 480],
- adPodDurationSec: 300,
- durationRangeSec: [15, 30],
- requireExactDuration: true,
- }
- }
- }
- );
-
- // 20 total placements with 15 max impressions = 2 requests
- const request = spec.buildRequests([bidRequest]);
- expect(request.length).to.equal(2);
-
- // 20 spread over 2 requests = 15 in first request, 5 in second
- const payload1 = JSON.parse(request[0].data);
- const payload2 = JSON.parse(request[1].data);
- expect(payload1.tags.length).to.equal(15);
- expect(payload2.tags.length).to.equal(5);
-
- // 10 placements should have max/min at 15
- // 10 placemenst should have max/min at 30
- const payload1tagsWith15 = payload1.tags.filter(tag => tag.video.maxduration === 15);
- const payload1tagsWith30 = payload1.tags.filter(tag => tag.video.maxduration === 30);
- expect(payload1tagsWith15.length).to.equal(10);
- expect(payload1tagsWith30.length).to.equal(5);
-
- // 5 placemenst with min/max at 30 were in the first request
- // so 5 remaining should be in the second
- const payload2tagsWith30 = payload2.tags.filter(tag => tag.video.maxduration === 30);
- expect(payload2tagsWith30.length).to.equal(5);
- });
-
- it('should set durations for placements when requireExactDuration is set and numbers are uneven', function () {
- let bidRequest = Object.assign({},
- bidRequests[0],
- {
- params: { placement_id: '14542875' }
- },
- {
- mediaTypes: {
- video: {
- context: 'adpod',
- playerSize: [640, 480],
- adPodDurationSec: 105,
- durationRangeSec: [15, 30, 60],
- requireExactDuration: true,
- }
- }
- }
- );
-
- const request = spec.buildRequests([bidRequest]);
- const payload = JSON.parse(request.data);
- expect(payload.tags.length).to.equal(7);
-
- const tagsWith15 = payload.tags.filter(tag => tag.video.maxduration === 15);
- const tagsWith30 = payload.tags.filter(tag => tag.video.maxduration === 30);
- const tagsWith60 = payload.tags.filter(tag => tag.video.maxduration === 60);
- expect(tagsWith15.length).to.equal(3);
- expect(tagsWith30.length).to.equal(3);
- expect(tagsWith60.length).to.equal(1);
- });
-
- it('should break adpod request into batches', function () {
- let bidRequest = Object.assign({},
- bidRequests[0],
- {
- params: { placement_id: '14542875' }
- },
- {
- mediaTypes: {
- video: {
- context: 'adpod',
- playerSize: [640, 480],
- adPodDurationSec: 225,
- durationRangeSec: [5],
- }
- }
- }
- );
-
- const request = spec.buildRequests([bidRequest]);
- const payload1 = JSON.parse(request[0].data);
- const payload2 = JSON.parse(request[1].data);
- const payload3 = JSON.parse(request[2].data);
-
- expect(payload1.tags.length).to.equal(15);
- expect(payload2.tags.length).to.equal(15);
- expect(payload3.tags.length).to.equal(15);
- });
-
- it('should contain hb_source value for adpod', function () {
- let bidRequest = Object.assign({},
- bidRequests[0],
- {
- params: { placement_id: '14542875' }
- },
- {
- mediaTypes: {
- video: {
- context: 'adpod',
- playerSize: [640, 480],
- adPodDurationSec: 300,
- durationRangeSec: [15, 30],
- }
- }
- }
- );
- const request = spec.buildRequests([bidRequest])[0];
- const payload = JSON.parse(request.data);
- expect(payload.tags[0].hb_source).to.deep.equal(7);
- });
-
it('should contain hb_source value for other media', function () {
let bidRequest = Object.assign({},
bidRequests[0],
@@ -1392,27 +1398,31 @@ describe('AppNexusAdapter', function () {
});
it('should populate iab_support object at the root level if omid support is detected', function () {
- // with bid.params.frameworks
- let bidRequest_A = Object.assign({}, bidRequests[0], {
- params: {
- frameworks: [1, 2, 5, 6],
- video: {
- frameworks: [1, 2, 5, 6]
+ let request, payload;
+
+ if (FEATURES.VIDEO) {
+ // with bid.params.frameworks
+ let bidRequest_A = Object.assign({}, bidRequests[0], {
+ params: {
+ frameworks: [1, 2, 5, 6],
+ video: {
+ frameworks: [1, 2, 5, 6]
+ }
}
- }
- });
- let request = spec.buildRequests([bidRequest_A]);
- let payload = JSON.parse(request.data);
- expect(payload.iab_support).to.be.an('object');
- expect(payload.iab_support).to.deep.equal({
- omidpn: 'Appnexus',
- omidpv: '$prebid.version$'
- });
- expect(payload.tags[0].banner_frameworks).to.be.an('array');
- expect(payload.tags[0].banner_frameworks).to.deep.equal([1, 2, 5, 6]);
- expect(payload.tags[0].video_frameworks).to.be.an('array');
- expect(payload.tags[0].video_frameworks).to.deep.equal([1, 2, 5, 6]);
- expect(payload.tags[0].video.frameworks).to.not.exist;
+ });
+ request = spec.buildRequests([bidRequest_A]);
+ payload = JSON.parse(request.data);
+ expect(payload.iab_support).to.be.an('object');
+ expect(payload.iab_support).to.deep.equal({
+ omidpn: 'Appnexus',
+ omidpv: '$prebid.version$'
+ });
+ expect(payload.tags[0].banner_frameworks).to.be.an('array');
+ expect(payload.tags[0].banner_frameworks).to.deep.equal([1, 2, 5, 6]);
+ expect(payload.tags[0].video_frameworks).to.be.an('array');
+ expect(payload.tags[0].video_frameworks).to.deep.equal([1, 2, 5, 6]);
+ expect(payload.tags[0].video.frameworks).to.not.exist;
+ }
// without bid.params.frameworks
const bidRequest_B = Object.assign({}, bidRequests[0]);
@@ -1422,19 +1432,21 @@ describe('AppNexusAdapter', function () {
expect(payload.tags[0].banner_frameworks).to.not.exist;
expect(payload.tags[0].video_frameworks).to.not.exist;
- // with video.frameworks but it is not an array
- const bidRequest_C = Object.assign({}, bidRequests[0], {
- params: {
- video: {
- frameworks: "'1', '2', '3', '6'"
+ if (FEATURES.VIDEO) {
+ // with video.frameworks but it is not an array
+ const bidRequest_C = Object.assign({}, bidRequests[0], {
+ params: {
+ video: {
+ frameworks: "'1', '2', '3', '6'"
+ }
}
- }
- });
- request = spec.buildRequests([bidRequest_C]);
- payload = JSON.parse(request.data);
- expect(payload.iab_support).to.not.exist;
- expect(payload.tags[0].banner_frameworks).to.not.exist;
- expect(payload.tags[0].video_frameworks).to.not.exist;
+ });
+ request = spec.buildRequests([bidRequest_C]);
+ payload = JSON.parse(request.data);
+ expect(payload.iab_support).to.not.exist;
+ expect(payload.tags[0].banner_frameworks).to.not.exist;
+ expect(payload.tags[0].video_frameworks).to.not.exist;
+ }
});
})
@@ -1591,116 +1603,118 @@ describe('AppNexusAdapter', function () {
expect(result.length).to.equal(0);
});
- it('handles outstream video responses', function () {
- let response = {
- 'tags': [{
- 'uuid': '84ab500420319d',
- 'ads': [{
- 'ad_type': 'video',
- 'cpm': 0.500000,
- 'notify_url': 'imptracker.com',
- 'rtb': {
- 'video': {
- 'content': ''
- }
- },
- 'javascriptTrackers': ''
+ if (FEATURES.VIDEO) {
+ it('handles outstream video responses', function () {
+ let response = {
+ 'tags': [{
+ 'uuid': '84ab500420319d',
+ 'ads': [{
+ 'ad_type': 'video',
+ 'cpm': 0.500000,
+ 'notify_url': 'imptracker.com',
+ 'rtb': {
+ 'video': {
+ 'content': ''
+ }
+ },
+ 'javascriptTrackers': ''
+ }]
}]
- }]
- };
- let bidderRequest = {
- bids: [{
- bidId: '84ab500420319d',
- adUnitCode: 'code',
- mediaTypes: {
- video: {
- context: 'outstream'
+ };
+ let bidderRequest = {
+ bids: [{
+ bidId: '84ab500420319d',
+ adUnitCode: 'code',
+ mediaTypes: {
+ video: {
+ context: 'outstream'
+ }
}
- }
- }]
- }
+ }]
+ }
- let result = spec.interpretResponse({ body: response }, { bidderRequest });
- expect(result[0]).to.have.property('vastXml');
- expect(result[0]).to.have.property('vastImpUrl');
- expect(result[0]).to.have.property('mediaType', 'video');
- });
+ let result = spec.interpretResponse({ body: response }, { bidderRequest });
+ expect(result[0]).to.have.property('vastXml');
+ expect(result[0]).to.have.property('vastImpUrl');
+ expect(result[0]).to.have.property('mediaType', 'video');
+ });
- it('handles instream video responses', function () {
- let response = {
- 'tags': [{
- 'uuid': '84ab500420319d',
- 'ads': [{
- 'ad_type': 'video',
- 'cpm': 0.500000,
- 'notify_url': 'imptracker.com',
- 'rtb': {
- 'video': {
- 'asset_url': 'https://sample.vastURL.com/here/vid'
- }
- },
- 'javascriptTrackers': ''
+ it('handles instream video responses', function () {
+ let response = {
+ 'tags': [{
+ 'uuid': '84ab500420319d',
+ 'ads': [{
+ 'ad_type': 'video',
+ 'cpm': 0.500000,
+ 'notify_url': 'imptracker.com',
+ 'rtb': {
+ 'video': {
+ 'asset_url': 'https://sample.vastURL.com/here/vid'
+ }
+ },
+ 'javascriptTrackers': ''
+ }]
}]
- }]
- };
- let bidderRequest = {
- bids: [{
- bidId: '84ab500420319d',
- adUnitCode: 'code',
- mediaTypes: {
- video: {
- context: 'instream'
+ };
+ let bidderRequest = {
+ bids: [{
+ bidId: '84ab500420319d',
+ adUnitCode: 'code',
+ mediaTypes: {
+ video: {
+ context: 'instream'
+ }
}
- }
- }]
- }
+ }]
+ }
- let result = spec.interpretResponse({ body: response }, { bidderRequest });
- expect(result[0]).to.have.property('vastUrl');
- expect(result[0]).to.have.property('vastImpUrl');
- expect(result[0]).to.have.property('mediaType', 'video');
- });
+ let result = spec.interpretResponse({ body: response }, { bidderRequest });
+ expect(result[0]).to.have.property('vastUrl');
+ expect(result[0]).to.have.property('vastImpUrl');
+ expect(result[0]).to.have.property('mediaType', 'video');
+ });
- it('handles adpod responses', function () {
- let response = {
- 'tags': [{
- 'uuid': '84ab500420319d',
- 'ads': [{
- 'ad_type': 'video',
- 'brand_category_id': 10,
- 'cpm': 0.500000,
- 'notify_url': 'imptracker.com',
- 'rtb': {
- 'video': {
- 'asset_url': 'https://sample.vastURL.com/here/adpod',
- 'duration_ms': 30000,
+ it('handles adpod responses', function () {
+ let response = {
+ 'tags': [{
+ 'uuid': '84ab500420319d',
+ 'ads': [{
+ 'ad_type': 'video',
+ 'brand_category_id': 10,
+ 'cpm': 0.500000,
+ 'notify_url': 'imptracker.com',
+ 'rtb': {
+ 'video': {
+ 'asset_url': 'https://sample.vastURL.com/here/adpod',
+ 'duration_ms': 30000,
+ }
+ },
+ 'viewability': {
+ 'config': ''
}
- },
- 'viewability': {
- 'config': ''
- }
+ }]
}]
- }]
- };
+ };
- let bidderRequest = {
- bids: [{
- bidId: '84ab500420319d',
- adUnitCode: 'code',
- mediaTypes: {
- video: {
- context: 'adpod'
+ let bidderRequest = {
+ bids: [{
+ bidId: '84ab500420319d',
+ adUnitCode: 'code',
+ mediaTypes: {
+ video: {
+ context: 'adpod'
+ }
}
- }
- }]
- };
- bfStub.returns('1');
+ }]
+ };
+ bfStub.returns('1');
- let result = spec.interpretResponse({ body: response }, { bidderRequest });
- expect(result[0]).to.have.property('vastUrl');
- expect(result[0].video.context).to.equal('adpod');
- expect(result[0].video.durationSeconds).to.equal(30);
- });
+ let result = spec.interpretResponse({ body: response }, { bidderRequest });
+ expect(result[0]).to.have.property('vastUrl');
+ expect(result[0].video.context).to.equal('adpod');
+ expect(result[0].video.durationSeconds).to.equal(30);
+ });
+ }
if (FEATURES.NATIVE) {
it('handles native responses', function () {
@@ -1758,59 +1772,61 @@ describe('AppNexusAdapter', function () {
});
}
- it('supports configuring outstream renderers', function () {
- const outstreamResponse = deepClone(response);
- outstreamResponse.tags[0].ads[0].rtb.video = {};
- outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js';
+ if (FEATURES.VIDEO) {
+ it('supports configuring outstream renderers', function () {
+ const outstreamResponse = deepClone(response);
+ outstreamResponse.tags[0].ads[0].rtb.video = {};
+ outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js';
- const bidderRequest = {
- bids: [{
- bidId: '3db3773286ee59',
- renderer: {
- options: {
- adText: 'configured'
- }
- },
- mediaTypes: {
- video: {
- context: 'outstream'
+ const bidderRequest = {
+ bids: [{
+ bidId: '3db3773286ee59',
+ renderer: {
+ options: {
+ adText: 'configured'
+ }
+ },
+ mediaTypes: {
+ video: {
+ context: 'outstream'
+ }
}
- }
- }]
- };
+ }]
+ };
- const result = spec.interpretResponse({ body: outstreamResponse }, { bidderRequest });
- expect(result[0].renderer.config).to.deep.equal(
- bidderRequest.bids[0].renderer.options
- );
- });
+ const result = spec.interpretResponse({ body: outstreamResponse }, { bidderRequest });
+ expect(result[0].renderer.config).to.deep.equal(
+ bidderRequest.bids[0].renderer.options
+ );
+ });
- it('should add deal_priority and deal_code', function () {
- let responseWithDeal = deepClone(response);
- responseWithDeal.tags[0].ads[0].ad_type = 'video';
- responseWithDeal.tags[0].ads[0].deal_priority = 5;
- responseWithDeal.tags[0].ads[0].deal_code = '123';
- responseWithDeal.tags[0].ads[0].rtb.video = {
- duration_ms: 1500,
- player_width: 640,
- player_height: 340,
- };
+ it('should add deal_priority and deal_code', function () {
+ let responseWithDeal = deepClone(response);
+ responseWithDeal.tags[0].ads[0].ad_type = 'video';
+ responseWithDeal.tags[0].ads[0].deal_priority = 5;
+ responseWithDeal.tags[0].ads[0].deal_code = '123';
+ responseWithDeal.tags[0].ads[0].rtb.video = {
+ duration_ms: 1500,
+ player_width: 640,
+ player_height: 340,
+ };
- let bidderRequest = {
- bids: [{
- bidId: '3db3773286ee59',
- adUnitCode: 'code',
- mediaTypes: {
- video: {
- context: 'adpod'
+ let bidderRequest = {
+ bids: [{
+ bidId: '3db3773286ee59',
+ adUnitCode: 'code',
+ mediaTypes: {
+ video: {
+ context: 'adpod'
+ }
}
- }
- }]
- }
- let result = spec.interpretResponse({ body: responseWithDeal }, { bidderRequest });
- expect(Object.keys(result[0].appnexus)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']);
- expect(result[0].video.dealTier).to.equal(5);
- });
+ }]
+ }
+ let result = spec.interpretResponse({ body: responseWithDeal }, { bidderRequest });
+ expect(Object.keys(result[0].appnexus)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']);
+ expect(result[0].video.dealTier).to.equal(5);
+ });
+ }
it('should add advertiser id', function () {
let responseAdvertiserId = deepClone(response);
diff --git a/test/spec/modules/big-richmediaBidAdapter_spec.js b/test/spec/modules/big-richmediaBidAdapter_spec.js
index b2ee0725d49..f01e261ef9f 100644
--- a/test/spec/modules/big-richmediaBidAdapter_spec.js
+++ b/test/spec/modules/big-richmediaBidAdapter_spec.js
@@ -92,40 +92,42 @@ describe('bigRichMediaAdapterTests', function () {
expect(payload.tags[0].sizes).to.have.lengthOf(3);
});
- it('should build video bid request', function() {
- const bidRequest = deepClone(bidRequests[0]);
- bidRequest.params = {
- placementId: '1234235',
- video: {
- skippable: true,
- playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'],
- context: 'outstream',
- format: 'sticky-top'
- }
- };
- bidRequest.mediaTypes = {
- video: {
- playerSize: [640, 480],
- context: 'outstream',
- mimes: ['video/mp4'],
- skip: 0,
- minduration: 5,
- api: [1, 5, 6],
- playbackmethod: [2, 4]
- }
- };
+ if (FEATURES.VIDEO) {
+ it('should build video bid request', function() {
+ const bidRequest = deepClone(bidRequests[0]);
+ bidRequest.params = {
+ placementId: '1234235',
+ video: {
+ skippable: true,
+ playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'],
+ context: 'outstream',
+ format: 'sticky-top'
+ }
+ };
+ bidRequest.mediaTypes = {
+ video: {
+ playerSize: [640, 480],
+ context: 'outstream',
+ mimes: ['video/mp4'],
+ skip: 0,
+ minduration: 5,
+ api: [1, 5, 6],
+ playbackmethod: [2, 4]
+ }
+ };
- const request = spec.buildRequests([bidRequest]);
- const payload = JSON.parse(request.data);
+ const request = spec.buildRequests([bidRequest]);
+ const payload = JSON.parse(request.data);
- expect(payload.tags[0].video).to.deep.equal({
- minduration: 5,
- playback_method: 2,
- skippable: true,
- context: 4
+ expect(payload.tags[0].video).to.deep.equal({
+ minduration: 5,
+ playback_method: 2,
+ skippable: true,
+ context: 4
+ });
+ expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4])
});
- expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4])
- });
+ }
});
describe('interpretResponse', function () {
@@ -227,42 +229,44 @@ describe('bigRichMediaAdapterTests', function () {
expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0]));
});
- it('handles outstream video responses', function () {
- const response = {
- 'tags': [{
- 'uuid': '84ab500420319d',
- 'ads': [{
- 'ad_type': 'video',
- 'cpm': 0.500000,
- 'notify_url': 'imptracker.com',
- 'rtb': {
- 'video': {
- 'content': ''
- }
- },
- 'javascriptTrackers': ''
+ if (FEATURES.VIDEO) {
+ it('handles outstream video responses', function () {
+ const response = {
+ 'tags': [{
+ 'uuid': '84ab500420319d',
+ 'ads': [{
+ 'ad_type': 'video',
+ 'cpm': 0.500000,
+ 'notify_url': 'imptracker.com',
+ 'rtb': {
+ 'video': {
+ 'content': ''
+ }
+ },
+ 'javascriptTrackers': ''
+ }]
}]
- }]
- };
- const bidderRequest = {
- bids: [{
- bidId: '84ab500420319d',
- adUnitCode: 'code',
- mediaTypes: {
- video: {
- context: 'outstream'
+ };
+ const bidderRequest = {
+ bids: [{
+ bidId: '84ab500420319d',
+ adUnitCode: 'code',
+ mediaTypes: {
+ video: {
+ context: 'outstream'
+ }
}
- }
- }]
- }
+ }]
+ }
- const result = spec.interpretResponse({ body: response }, {bidderRequest});
- expect(result[0]).not.to.have.property('vastXml');
- expect(result[0]).not.to.have.property('vastUrl');
- expect(result[0]).to.have.property('width', 1);
- expect(result[0]).to.have.property('height', 1);
- expect(result[0]).to.have.property('mediaType', 'banner');
- });
+ const result = spec.interpretResponse({ body: response }, {bidderRequest});
+ expect(result[0]).not.to.have.property('vastXml');
+ expect(result[0]).not.to.have.property('vastUrl');
+ expect(result[0]).to.have.property('width', 1);
+ expect(result[0]).to.have.property('height', 1);
+ expect(result[0]).to.have.property('mediaType', 'banner');
+ });
+ }
});
describe('getUserSyncs', function() {
diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js
index b069b13fc37..820d87ef49c 100644
--- a/test/spec/unit/pbjs_api_spec.js
+++ b/test/spec/unit/pbjs_api_spec.js
@@ -888,16 +888,32 @@ describe('Unit: Prebid Module', function () {
it('should only apply price granularity if bid media type matches', function () {
initTestConfig({
- adUnits: [ createAdUnit('div-gpt-ad-1460505748561-0', 'video') ],
+ adUnits: [createAdUnit('div-gpt-ad-1460505748561-0')],
adUnitCodes: ['div-gpt-ad-1460505748561-0']
});
- response = videoResponse;
+ response = bannerResponse;
response.tags[0].ads[0].cpm = 3.4288;
auction.callBids(cbTimeout);
let bidTargeting = targeting.getAllTargeting();
- expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.00');
+ expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.25');
+
+ if (FEATURES.VIDEO) {
+ ajaxStub.restore();
+
+ initTestConfig({
+ adUnits: [createAdUnit('div-gpt-ad-1460505748561-0', 'video')],
+ adUnitCodes: ['div-gpt-ad-1460505748561-0']
+ });
+
+ response = videoResponse;
+ response.tags[0].ads[0].cpm = 3.4288;
+
+ auction.callBids(cbTimeout);
+ let bidTargeting = targeting.getAllTargeting();
+ expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.00');
+ }
});
});