Skip to content

Commit

Permalink
Update to Rubicon Adapter for mediaTypes support (prebid#2272)
Browse files Browse the repository at this point in the history
* Added support for mediaTypes bid requests

* Fixed linting errors

* Replaced missing test for ad position
  • Loading branch information
idettman authored and snapwich committed Mar 19, 2018
1 parent 292b554 commit 6aa8c18
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 23 deletions.
32 changes: 20 additions & 12 deletions modules/rubiconBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';
import { config } from 'src/config';
import { BANNER, VIDEO } from 'src/mediaTypes';

const INTEGRATION = 'pbjs_lite_v$prebid.version$';

Expand Down Expand Up @@ -73,7 +74,7 @@ utils._each(sizeMap, (item, key) => sizeMap[item] = key);
export const spec = {
code: 'rubicon',
aliases: ['rubiconLite'],
supportedMediaTypes: ['banner', 'video'],
supportedMediaTypes: [BANNER, VIDEO],
/**
* @param {object} bid
* @return boolean
Expand All @@ -93,8 +94,9 @@ export const spec = {
return false;
}

if (bid.mediaType === 'video') {
if (typeof params.video !== 'object' || !params.video.size_id) {
if (spec.hasVideoMediaType(bid)) {
// support instream only
if ((utils.deepAccess(bid, `mediaTypes.${VIDEO}`) && utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) !== 'instream') || typeof params.video !== 'object' || !params.video.size_id) {
return false;
}
}
Expand All @@ -118,7 +120,7 @@ export const spec = {

page_url = bidRequest.params.secure ? page_url.replace(/^http:/i, 'https:') : page_url;

if (bidRequest.mediaType === 'video') {
if (spec.hasVideoMediaType(bidRequest)) {
let params = bidRequest.params;
let size = parseSizes(bidRequest);

Expand Down Expand Up @@ -237,6 +239,15 @@ export const spec = {
};
});
},
/**
* Test if bid has mediaType or mediaTypes set for video.
* note: 'mediaType' has been deprecated, however support will remain for a transitional period
* @param {BidRequest} bidRequest
* @returns {boolean}
*/
hasVideoMediaType: function(bidRequest) {
return bidRequest.mediaType === VIDEO || typeof utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`) !== 'undefined';
},
/**
* @param {*} responseObj
* @param {BidRequest} bidRequest
Expand All @@ -252,7 +263,7 @@ export const spec = {
}

// video ads array is wrapped in an object
if (typeof bidRequest === 'object' && bidRequest.mediaType === 'video' && typeof ads === 'object') {
if (typeof bidRequest === 'object' && spec.hasVideoMediaType(bidRequest) && typeof ads === 'object') {
ads = ads[bidRequest.adUnitCode];
}

Expand Down Expand Up @@ -287,7 +298,7 @@ export const spec = {
bid.mediaType = ad.creative_type;
}

if (bidRequest.mediaType === 'video') {
if (ad.creative_type === VIDEO) {
bid.width = bidRequest.params.video.playerWidth;
bid.height = bidRequest.params.video.playerHeight;
bid.vastUrl = ad.creative_depot_url;
Expand Down Expand Up @@ -360,17 +371,14 @@ function _renderCreative(script, impId) {

function parseSizes(bid) {
let params = bid.params;
if (bid.mediaType === 'video') {
if (spec.hasVideoMediaType(bid)) {
let size = [];
if (params.video.playerWidth && params.video.playerHeight) {
if (typeof params.video === 'object' && params.video.playerWidth && params.video.playerHeight) {
size = [
params.video.playerWidth,
params.video.playerHeight
];
} else if (
Array.isArray(bid.sizes) && bid.sizes.length > 0 &&
Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1
) {
} else if (Array.isArray(bid.sizes) && bid.sizes.length > 0 && Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1) {
size = bid.sizes[0];
}
return size;
Expand Down
243 changes: 232 additions & 11 deletions test/spec/modules/rubiconBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,31 @@ describe('the rubicon adapter', () => {

function createVideoBidderRequest() {
let bid = bidderRequest.bids[0];

bid.mediaTypes = {
video: {
context: 'instream'
}
};

bid.params.video = {
'language': 'en',
'p_aso.video.ext.skip': true,
'p_aso.video.ext.skipdelay': 15,
'playerHeight': 320,
'playerWidth': 640,
'size_id': 201,
'aeParams': {
'p_aso.video.ext.skip': '1',
'p_aso.video.ext.skipdelay': '15'
}
};
}

function createLegacyVideoBidderRequest() {
let bid = bidderRequest.bids[0];

// Legacy property (Prebid <1.0)
bid.mediaType = 'video';
bid.params.video = {
'language': 'en',
Expand All @@ -33,12 +58,62 @@ describe('the rubicon adapter', () => {
}

function createVideoBidderRequestNoVideo() {
let bid = bidderRequest.bids[0];
bid.mediaTypes = {
video: {
context: 'instream'
},
};
bid.params.video = '';
}

function createLegacyVideoBidderRequestNoVideo() {
let bid = bidderRequest.bids[0];
bid.mediaType = 'video';
bid.params.video = '';
}

function createVideoBidderRequestOutstream() {
let bid = bidderRequest.bids[0];
bid.mediaTypes = {
video: {
context: 'outstream'
},
};
bid.params.video = {
'language': 'en',
'p_aso.video.ext.skip': true,
'p_aso.video.ext.skipdelay': 15,
'playerHeight': 320,
'playerWidth': 640,
'size_id': 201,
'aeParams': {
'p_aso.video.ext.skip': '1',
'p_aso.video.ext.skipdelay': '15'
}
};
}

function createVideoBidderRequestNoPlayer() {
let bid = bidderRequest.bids[0];
bid.mediaTypes = {
video: {
context: 'instream'
},
};
bid.params.video = {
'language': 'en',
'p_aso.video.ext.skip': true,
'p_aso.video.ext.skipdelay': 15,
'size_id': 201,
'aeParams': {
'p_aso.video.ext.skip': '1',
'p_aso.video.ext.skipdelay': '15'
}
};
}

function createLegacyVideoBidderRequestNoPlayer() {
let bid = bidderRequest.bids[0];
bid.mediaType = 'video';
bid.params.video = {
Expand Down Expand Up @@ -81,6 +156,7 @@ describe('the rubicon adapter', () => {
referrer: 'localhost'
},
adUnitCode: '/19968336/header-bid-tag-0',
code: 'div-1',
sizes: [[300, 250], [320, 50]],
bidId: '2ffb201a808da7',
bidderRequestId: '178e34bad3658f',
Expand Down Expand Up @@ -120,8 +196,7 @@ describe('the rubicon adapter', () => {
describe('for requests', () => {
describe('to fastlane', () => {
it('should make a well-formed request objects', () => {
sandbox.stub(Math, 'random').returns(0.1);

sandbox.stub(Math, 'random').callsFake(() => 0.1);
let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
let data = parseQuery(request.data);

Expand Down Expand Up @@ -451,6 +526,67 @@ describe('the rubicon adapter', () => {
});

describe('for video requests', () => {
it('should make a well-formed video request with legacy mediaType config', () => {
createLegacyVideoBidderRequest();

sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
let post = request.data;

let url = request.url;

expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video');

expect(post).to.have.property('page_url').that.is.a('string');
expect(post.resolution).to.match(/\d+x\d+/);
expect(post.account_id).to.equal('14062');
expect(post.integration).to.equal(INTEGRATION);
expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b');
expect(post).to.have.property('timeout').that.is.a('number');
expect(post.timeout < 5000).to.equal(true);
expect(post.stash_creatives).to.equal(true);

expect(post).to.have.property('ae_pass_through_parameters');
expect(post.ae_pass_through_parameters)
.to.have.property('p_aso.video.ext.skip')
.that.equals('1');
expect(post.ae_pass_through_parameters)
.to.have.property('p_aso.video.ext.skipdelay')
.that.equals('15');

expect(post).to.have.property('slots')
.with.length.of(1);

let slot = post.slots[0];

expect(slot.site_id).to.equal('70608');
expect(slot.zone_id).to.equal('335918');
expect(slot.position).to.equal('atf');
expect(slot.floor).to.equal(0.01);
expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode);
expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode);
expect(slot.language).to.equal('en');
expect(slot.width).to.equal(640);
expect(slot.height).to.equal(320);
expect(slot.size_id).to.equal(201);

expect(slot).to.have.property('inventory').that.is.an('object');
expect(slot.inventory).to.have.property('rating').that.equals('5-star');
expect(slot.inventory).to.have.property('prodtype').that.equals('tech');

expect(slot).to.have.property('keywords')
.that.is.an('array')
.of.length(3)
.that.deep.equals(['a', 'b', 'c']);

expect(slot).to.have.property('visitor').that.is.an('object');
expect(slot.visitor).to.have.property('ucat').that.equals('new');
expect(slot.visitor).to.have.property('lastsearch').that.equals('iphone');
});

it('should make a well-formed video request', () => {
createVideoBidderRequest();

Expand Down Expand Up @@ -576,17 +712,60 @@ describe('the rubicon adapter', () => {
expect(floor).to.equal(3.25);
});

it('should not validate bid request when no video object is passed in', () => {
it('should not validate bid request when a invalid video object is passed in', () => {
createVideoBidderRequestNoVideo();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

var floorBidderRequest = clone(bidderRequest);
const bidRequestCopy = clone(bidderRequest.bids[0]);
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

let result = spec.isBidRequestValid(floorBidderRequest.bids[0]);
bidRequestCopy.params.video = {};
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

expect(result).to.equal(false);
bidRequestCopy.params.video = undefined;
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

bidRequestCopy.params.video = 123;
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

bidRequestCopy.params.video = { size_id: '' };
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

delete bidRequestCopy.params.video;
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);
});

it('should not validate bid request when an invalid video object is passed in with legacy config mediaType', () => {
createLegacyVideoBidderRequestNoVideo();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

const bidderRequestCopy = clone(bidderRequest);
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

bidderRequestCopy.bids[0].params.video = {};
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

bidderRequestCopy.bids[0].params.video = undefined;
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

bidderRequestCopy.bids[0].params.video = NaN;
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

delete bidderRequestCopy.bids[0].params.video;
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);
});

it('should not validate bid request when video is outstream', () => {
createVideoBidderRequestOutstream();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false);
});

it('should get size from bid.sizes too', () => {
Expand All @@ -595,13 +774,55 @@ describe('the rubicon adapter', () => {
bidderRequest.auctionStart + 100
);

var floorBidderRequest = clone(bidderRequest);
const bidRequestCopy = clone(bidderRequest);

let [request] = spec.buildRequests(floorBidderRequest.bids, floorBidderRequest);
let post = request.data;
let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy);

expect(post.slots[0].width).to.equal(300);
expect(post.slots[0].height).to.equal(250);
expect(request.data.slots[0].width).to.equal(300);
expect(request.data.slots[0].height).to.equal(250);
});

it('should get size from bid.sizes too with legacy config mediaType', () => {
createLegacyVideoBidderRequestNoPlayer();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

const bidRequestCopy = clone(bidderRequest);

let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy);

expect(request.data.slots[0].width).to.equal(300);
expect(request.data.slots[0].height).to.equal(250);
});
});

describe('hasVideoMediaType', () => {
it('should return true if mediaType is true', () => {
createVideoBidderRequest();
const legacyVideoTypeBidRequest = spec.hasVideoMediaType(bidderRequest.bids[0]);
expect(legacyVideoTypeBidRequest).is.equal(true);
});

it('should return false if bidRequest.mediaType is not equal to video', () => {
expect(spec.hasVideoMediaType({
mediaType: 'banner'
})).is.equal(false);
});

it('should return false if bidRequest.mediaType is not defined', () => {
expect(spec.hasVideoMediaType({})).is.equal(false);
});

it('should return true if bidRequest.mediaTypes.video object exists', () => {
expect(spec.hasVideoMediaType({
mediaTypes: {
video: {
context: 'outstream',
playerSize: [300, 250]
}
}
})).is.equal(true);
});
});
});
Expand Down

0 comments on commit 6aa8c18

Please sign in to comment.