From 82fd5a59c967eb398e1d5331c2234c0753fa7d9b Mon Sep 17 00:00:00 2001 From: guillaume-sticky Date: Thu, 9 Feb 2017 06:04:09 +0100 Subject: [PATCH] Add StickyAdsTV Bidder adapter (#916) * add stickyadsTV bidder adapter * init unit test file * ad some unit tests * fix unit test on ad format with parameters * add some unit tests * add unit tests on getBid method * add some test cases in unit tests * minor fix on component id tag. * remove adapters-sticky.json test file * use top most accessible window instead of window.top * Pass in the bid request in the createBid call. * use top most accessible window instead of window.top * add unit tests * update unit tests * fix unit test. * fix CI build --- adapters.json | 1 + src/adapters/stickyadstv.js | 292 +++++++++++++++++++++++++ test/spec/adapters/stickyadstv_spec.js | 222 +++++++++++++++++++ 3 files changed, 515 insertions(+) create mode 100644 src/adapters/stickyadstv.js create mode 100644 test/spec/adapters/stickyadstv_spec.js diff --git a/adapters.json b/adapters.json index 914961b6ca7..41948e5920d 100644 --- a/adapters.json +++ b/adapters.json @@ -33,6 +33,7 @@ "sonobi", "sovrn", "springserve", + "stickyadstv", "triplelift", "twenga", "yieldbot", diff --git a/src/adapters/stickyadstv.js b/src/adapters/stickyadstv.js new file mode 100644 index 00000000000..803cd626d56 --- /dev/null +++ b/src/adapters/stickyadstv.js @@ -0,0 +1,292 @@ +var bidfactory = require('../bidfactory.js'); +var bidmanager = require('../bidmanager.js'); +var adloader = require('../adloader.js'); + +var StickyAdsTVAdapter = function StickyAdsTVAdapter() { + + var MUSTANG_URL = "//cdn.stickyadstv.com/mustang/mustang.min.js"; + var INTEXTROLL_URL = "//cdn.stickyadstv.com/prime-time/intext-roll.min.js"; + var SCREENROLL_URL = "//cdn.stickyadstv.com/prime-time/screen-roll.min.js"; + + var topMostWindow = getTopMostWindow(); + topMostWindow.stickyadstv_cache = {}; + + function _callBids(params) { + + var bids = params.bids || []; + for (var i = 0; i < bids.length; i++) { + var bid = bids[i]; + // Send out bid request for each bid given its tag IDs and query strings + + if(bid.placementCode && bid.params.zoneId) { + sendBidRequest(bid); + } + else { + console.warn("StickyAdsTV: Missing mandatory field(s)."); + } + + } + } + + function sendBidRequest(bid){ + + var placementCode = bid.placementCode; + + var integrationType = bid.params.format ? bid.params.format : "inbanner"; + var urltoLoad = MUSTANG_URL; + + if(integrationType === "intext-roll"){ + urltoLoad = INTEXTROLL_URL; + } + if(integrationType === "screen-roll"){ + urltoLoad = SCREENROLL_URL; + } + + var bidRegistered = false; + adloader.loadScript(urltoLoad, function(){ + + getBid(bid, function(bidObject){ + + if(!bidRegistered){ + bidRegistered = true; + bidmanager.addBidResponse(placementCode, bidObject); + } + + }); + }, true); + } + + function getBid(bid, callback){ + var zoneId = bid.params.zoneId || bid.params.zone; //accept both + var size = getBiggerSize(bid.sizes); + + var vastLoader = new window.com.stickyadstv.vast.VastLoader(); + bid.vast = topMostWindow.stickyadstv_cache[bid.placementCode] = vastLoader.getVast(); + + var vastCallback = { + onSuccess : bind(function(){ + + //'this' is the bid request here + var bidRequest = this; + + var adHtml = formatAdHTML(bidRequest,size); + var price = extractPrice(bidRequest.vast); + + callback(formatBidObject(bidRequest, true, price, adHtml, size[0], size[1])); + + },bid), + onError : bind(function(){ + var bidRequest = this; + callback(formatBidObject(bidRequest, false)); + },bid) + }; + + var config = { + zoneId:zoneId, + playerSize:size[0]+"x"+size[1], + vastUrlParams: bid.params.vastUrlParams, + componentId: "prebid-sticky"+(bid.params.format ? "-"+bid.params.format : "") + }; + + if(bid.params.format === "screen-roll"){ + //in screenroll case we never use the original div size. + config.playerSize = window.com.stickyadstv.screenroll.getPlayerSize(); + } + + vastLoader.load(config, vastCallback); + } + + function getBiggerSize(array){ + var result = [1,1]; + for(var i = 0; i< array.length; i++){ + if(array[i][0]*array[i][1] > result[0]*result[1]){ + result = array[i]; + } + } + return result; + } + + var formatInBannerHTML = function(bid,size){ + var placementCode = bid.placementCode; + + var divHtml = "
"; + + var script = ""; + + return divHtml+script; + }; + + var formatIntextHTML = function(bid){ + var placementCode = bid.placementCode; + + var config = bid.params; + + //default placement if no placement is set + if(!config.hasOwnProperty("domId") && !config.hasOwnProperty("auto") && !config.hasOwnProperty("p") && !config.hasOwnProperty("article")){ + config.domId = placementCode; + } + + var script = ""; + + return script; + }; + + var formatScreenRollHTML = function(bid){ + var placementCode = bid.placementCode; + + var config = bid.params; + + var script = ""; + + return script; + }; + + function formatAdHTML(bid, size){ + + var integrationType = bid.params.format; + + var html = ""; + if(integrationType === "intext-roll"){ + html = formatIntextHTML(bid); + } + else if(integrationType === "screen-roll"){ + html = formatScreenRollHTML(bid); + } + else { + html = formatInBannerHTML(bid,size); + } + + return html; + } + + + function extractPrice(vast){ + var priceData = vast.getPricing(); + + if(!priceData) { + console.warn("StickyAdsTV: Bid pricing Can't be retreived. You may need to enable pricing on you're zone. Please get in touch with your sticky contact."); + } + + return priceData; + } + + function formatBidObject(bidRequest, valid, priceData, html, width, height){ + var bidObject; + if(valid && priceData) { + // valid bid response + bidObject = bidfactory.createBid(1, bidRequest); + bidObject.bidderCode = 'stickyadstv'; + bidObject.cpm = priceData.price; + bidObject.currencyCode = priceData.currency; + bidObject.ad = html; + bidObject.width = width; + bidObject.height = height; + + } + else { + // invalid bid response + bidObject = bidfactory.createBid(2, bidRequest); + bidObject.bidderCode = 'stickyadstv'; + } + return bidObject; + } + + + /** + * returns the top most accessible window + */ + function getTopMostWindow(){ + var res=window; + + try { + while(top !== res){ + if(res.parent.location.href.length) + res=res.parent; + } + } + catch(e){} + + return res; + } + + /* Create a function bound to a given object (assigning `this`, and arguments, + * optionally). Binding with arguments is also known as `curry`. + * Delegates to **ECMAScript 5**'s native `Function.bind` if available. + * We check for `func.bind` first, to fail fast when `func` is undefined. + * + * @param {function} func + * @param {optional} context + * @param {...any} var_args + * @return {function} + */ + var bind = function(func, context) { + + return function() { + return func.apply(context,arguments); + }; + }; + + + // Export the callBids function, so that prebid.js can execute + // this function when the page asks to send out bid requests. + return { + callBids: _callBids, + formatBidObject: formatBidObject, + formatAdHTML: formatAdHTML, + getBiggerSize:getBiggerSize, + getBid:getBid, + getTopMostWindow:getTopMostWindow + }; +}; + +module.exports = StickyAdsTVAdapter; \ No newline at end of file diff --git a/test/spec/adapters/stickyadstv_spec.js b/test/spec/adapters/stickyadstv_spec.js new file mode 100644 index 00000000000..eb665ec658c --- /dev/null +++ b/test/spec/adapters/stickyadstv_spec.js @@ -0,0 +1,222 @@ +import {expect} from 'chai'; +import {assert} from 'chai'; +import Adapter from '../../../src/adapters/stickyadstv'; +import bidManager from '../../../src/bidmanager'; +import adLoader from '../../../src/adloader'; + +describe('StickyAdsTV Adapter', function () { + var adapter = void 0; + var sandbox = void 0; + var bidsRequestBuff = void 0; + var bidderRequest = { + bidderCode: 'stickyadstv', + bids: [{ + bidId: 'bidId1', + bidder: 'stickyadstv', + placementCode: 'foo', + sizes: [[300, 250]], + params: { + zoneId: '2003', + format:"screen-roll" + } + }, { + bidId: 'bidId2', + bidder: 'stickyadstv', + placementCode: 'bar', + sizes: [[728, 90]], + params: { + zoneId: '5562003' + } + }, { + bidId: 'bidId3', + bidder: 'stickyadstv', + placementCode: '', + sizes: [[300, 600]], + params: { + zoneId: '123456' + } + }, { + bidId: 'bidId4', + bidder: 'stickyadstv', + placementCode: 'coo', + sizes: [[300, 600]], + params: { + wrong: "missing zoneId" + } + }] + }; + + beforeEach(function () { + adapter = new Adapter(); + sandbox = sinon.sandbox.create(); + bidsRequestBuff = pbjs._bidsRequested; + pbjs._bidsRequested = []; + }); + + afterEach(function () { + sandbox.restore(); + pbjs._bidsRequested = bidsRequestBuff; + }); + + describe('callBids', function () { + beforeEach(function () { + sandbox.stub(adLoader, 'loadScript'); + adapter.callBids(bidderRequest); + }); + + it('should be called twice', function () { + sinon.assert.calledTwice(adLoader.loadScript); + }); + + it('should have load screenroll and mustang script', function () { + var url = void 0; + + url = adLoader.loadScript.firstCall.args[0]; + expect(url).to.equal("//cdn.stickyadstv.com/prime-time/screen-roll.min.js"); + + url = adLoader.loadScript.secondCall.args[0]; + expect(url).to.equal("//cdn.stickyadstv.com/mustang/mustang.min.js"); + }); + }); + + describe('getBid', function () { + let bidResponse; + let loadConfig; + let getPricingCalled; + + beforeEach(function () { + //Mock VastLoader for test purpose + window.com = { + stickyadstv : { + vast : { + VastLoader : function(){ + this.getVast = function(){ + return { + getPricing : function(){ + getPricingCalled = true; + return {currency:"USD", price: 4.000} + } + }; + }; + + this.load = function(config, listener){ + loadConfig = config; + listener.onSuccess(); + }; + } + }, + screenroll : { + getPlayerSize: function(){ + return "123x456"; + } + } + } + }; + + adapter.getBid(bidderRequest.bids[0], function(bidObject){ + bidResponse = bidObject; + }); + }); + + afterEach(function() { + delete window.com.stickyadstv.vast.VastLoader; + delete window.com.stickyadstv.vast; + delete window.com.stickyadstv.screenroll; + delete window.com.stickyadstv; + }); + + it('should have returned a valid bidObject', function () { + + expect(bidResponse).to.have.property('cpm', 4.000); + expect(bidResponse).to.have.property('ad', ""); + expect(bidResponse).to.have.property('bidderCode', "stickyadstv"); + expect(bidResponse).to.have.property('currencyCode', "USD"); + expect(bidResponse).to.have.property('width', 300); + expect(bidResponse).to.have.property('height', 250); + expect(bidResponse.getStatusCode()).to.equal(1); + }); + + it('should have called load with proper config', function () { + + expect(loadConfig).to.have.property('playerSize', "123x456"); + expect(loadConfig).to.have.property('zoneId', "2003"); + + }); + + it('should have called getPricing', function () { + + expect(getPricingCalled).to.equal(true); + + }); + }); + + describe('formatBidObject', function () { + + it('should create a valid bid object', function () { + let result = adapter.formatBidObject("", true, {currency:"EUR",price:"1.2345"}, "
sample
", 200, 300); + + expect(result).to.have.property('cpm', '1.2345'); + expect(result).to.have.property('ad', "
sample
"); + expect(result).to.have.property('bidderCode', "stickyadstv"); + expect(result).to.have.property('currencyCode', "EUR"); + expect(result).to.have.property('width', 200); + expect(result).to.have.property('height', 300); + expect(result.getStatusCode()).to.equal(1); + }); + + it('should create a invalid bid object because price is not defined', function () { + let result = adapter.formatBidObject("", true, null, "
sample
", 200, 300); + + expect(result).to.have.property('bidderCode', "stickyadstv"); + expect(result.getStatusCode()).to.equal(2); + }); + + it('should create a invalid bid object', function () { + let result = adapter.formatBidObject("", false, {currency:"EUR",price:"1.2345"}, "
sample
", 200, 300); + + expect(result).to.have.property('bidderCode', "stickyadstv"); + expect(result.getStatusCode()).to.equal(2); + }); + }); + + describe('formatAdHTML', function () { + + it('should create an inBanner ad format', function () { + let result = adapter.formatAdHTML({placementCode:"placementCodeValue", params:{}}, [200,300]); + + expect(result).to.equal('
'); + }); + + it('should create an intext ad format', function () { + let result = adapter.formatAdHTML({placementCode:"placementCodeValue", params:{format:"intext-roll", auto:"v2", smartPlay:"true"}}, [200,300]); + + expect(result).to.equal(''); + }); + + it('should create a screenroll ad format', function () { + let result = adapter.formatAdHTML({placementCode:"placementCodeValue", params:{format:"screen-roll", smartPlay:"true"}}, [200,300]); + + expect(result).to.equal(''); + }); + }); + + describe('getBiggerSize', function () { + + it('should returns the bigger size', function () { + let result = adapter.getBiggerSize([[1,4000],[4000,1],[200,300],[0,0]]); + + expect(result[0]).to.equal(200); + expect(result[1]).to.equal(300); + }); + }); + + describe('top most window', function () { + + it('should returns the top most window', function () { + let result = adapter.getTopMostWindow(); + + expect(result).to.equal(window.top); + }); + }); + +}); \ No newline at end of file