diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js
index d51008559f2..7e71d3be8aa 100644
--- a/modules/conversantBidAdapter.js
+++ b/modules/conversantBidAdapter.js
@@ -1,99 +1,70 @@
-'use strict';
-var VERSION = '2.1.0';
-var CONSTANTS = require('src/constants.json');
-var utils = require('src/utils.js');
-var bidfactory = require('src/bidfactory.js');
-var bidmanager = require('src/bidmanager.js');
-var adloader = require('src/adloader');
-var ajax = require('src/ajax').ajax;
-var adaptermanager = require('src/adaptermanager');
+import * as utils from 'src/utils';
+import {registerBidder} from 'src/adapters/bidderFactory';
+import { VIDEO } from 'src/mediaTypes';
+
+const BIDDER_CODE = 'conversant';
+const URL = '//media.msg.dotomi.com/s2s/header/24';
+const SYNC_URL = '//media.msg.dotomi.com/w/user.sync';
+const VERSION = '2.2.0';
+
+export const spec = {
+ code: BIDDER_CODE,
+ aliases: ['cnvr'], // short code
+ supportedMediaTypes: [VIDEO],
+
+ /**
+ * 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) {
+ if (!bid || !bid.params) {
+ utils.logWarn(BIDDER_CODE + ': Missing bid parameters');
+ return false;
+ }
-/**
- * Adapter for requesting bids from Conversant
- */
-var ConversantAdapter = function () {
- var w = window;
- var n = navigator;
-
- // production endpoint
- var conversantUrl = '//media.msg.dotomi.com/s2s/header/24?callback=$$PREBID_GLOBAL$$.conversantResponse';
-
- // SSAPI returns JSONP with window.pbjs.conversantResponse as the cb
- var appendScript = function (code) {
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.className = 'cnvr-response';
-
- try {
- script.appendChild(document.createTextNode(code));
- document.getElementsByTagName('head')[0].appendChild(script);
- } catch (e) {
- script.text = code;
- document.getElementsByTagName('head')[0].appendChild(script);
+ if (!utils.isStr(bid.params.site_id)) {
+ utils.logWarn(BIDDER_CODE + ': site_id must be specified as a string')
+ return false;
}
- };
- var getDNT = function () {
- return n.doNotTrack === '1' || w.doNotTrack === '1' || n.msDoNotTrack === '1' || n.doNotTrack === 'yes';
- };
+ if (isVideoRequest(bid)) {
+ if (!bid.params.mimes) {
+ // Give a warning but let it pass
+ utils.logWarn(BIDDER_CODE + ': mimes should be specified for videos');
+ } else if (!utils.isArray(bid.params.mimes) || !bid.params.mimes.every(s => utils.isStr(s))) {
+ utils.logWarn(BIDDER_CODE + ': mimes must be an array of strings');
+ return false;
+ }
+ }
- var getDevice = function () {
- const language = n.language ? 'language' : 'userLanguage';
- return {
- h: screen.height,
- w: screen.width,
- dnt: getDNT() ? 1 : 0,
- language: n[language].split('-')[0],
- make: n.vendor ? n.vendor : '',
- ua: n.userAgent
- };
- };
+ return true;
+ },
- var callBids = function (params) {
- var conversantBids = params.bids || [];
- requestBids(conversantBids);
- };
+ /**
+ * Make a server request from the list of BidRequests.
+ *
+ * @param {BidRequest[]} validBidRequests - an array of bids
+ * @return {ServerRequest} Info describing the request to the server.
+ */
+ buildRequests: function(validBidRequests) {
+ const loc = utils.getTopWindowLocation();
+ const page = loc.pathname + loc.search + loc.hash;
+ const isPageSecure = (loc.protocol === 'https:') ? 1 : 0;
+ let siteId = '';
+ let requestId = '';
- var requestBids = function (bidReqs) {
- // build bid request object
- var page = location.pathname + location.search + location.hash;
- var siteId = '';
- var conversantImps = [];
- var conversantBidReqs;
- var secure = 0;
-
- // build impression array for conversant
- utils._each(bidReqs, function (bid) {
- var bidfloor = utils.getBidIdParameter('bidfloor', bid.params);
- var adW = 0;
- var adH = 0;
- var format;
- var tagId;
- var pos;
- var imp;
-
- secure = utils.getBidIdParameter('secure', bid.params) ? 1 : secure;
- siteId = utils.getBidIdParameter('site_id', bid.params) + '';
- tagId = utils.getBidIdParameter('tag_id', bid.params);
- pos = utils.getBidIdParameter('position', bid.params);
-
- // Allow sizes to be overridden per placement
- var bidSizes = Array.isArray(bid.params.sizes) ? bid.params.sizes : bid.sizes;
-
- if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') {
- adW = bidSizes[0];
- adH = bidSizes[1];
- } else {
- format = [];
- utils._each(bidSizes, function (bidSize) {
- format.push({
- w: bidSize[0],
- h: bidSize[1]
- });
- });
- }
+ const conversantImps = validBidRequests.map(function(bid) {
+ const bidfloor = utils.getBidIdParameter('bidfloor', bid.params);
+ const secure = isPageSecure || (utils.getBidIdParameter('secure', bid.params) ? 1 : 0);
+
+ siteId = utils.getBidIdParameter('site_id', bid.params);
+ requestId = bid.requestId;
+
+ const format = convertSizes(bid.sizes);
- imp = {
+ const imp = {
id: bid.bidId,
secure: secure,
bidfloor: bidfloor || 0,
@@ -101,178 +72,187 @@ var ConversantAdapter = function () {
displaymanagerver: VERSION
};
- if (tagId !== '') {
- imp.tagid = tagId;
- }
-
- if (bid.mediaType === 'video') {
- var mimes = [];
- var maxduration = 0;
- var protocols = [];
- var api = [];
-
- var video = Array.isArray(format) ? {format: format} : {w: adW, h: adH};
-
- mimes = utils.getBidIdParameter('mimes', bid.params);
- if (mimes !== '') {
- video.mimes = mimes;
- }
+ copyOptProperty(bid.params, 'tag_id', imp, 'tagid');
- maxduration = utils.getBidIdParameter('maxduration', bid.params);
- if (maxduration !== '') {
- video.maxduration = maxduration;
- }
+ if (isVideoRequest(bid)) {
+ const video = {format: format};
- protocols = utils.getBidIdParameter('protocols', bid.params);
- if (protocols !== '') {
- video.protocols = protocols;
- }
-
- api = utils.getBidIdParameter('api', bid.params);
- if (api !== '') {
- video.api = api;
- }
-
- if (pos !== '') {
- video.pos = pos;
- }
+ copyOptProperty(bid.params, 'position', video, 'pos');
+ copyOptProperty(bid.params, 'mimes', video);
+ copyOptProperty(bid.params, 'maxduration', video);
+ copyOptProperty(bid.params, 'protocols', video);
+ copyOptProperty(bid.params, 'api', video);
imp.video = video;
} else {
- var banner = Array.isArray(format) ? {format: format} : {w: adW, h: adH};
+ const banner = {format: format};
+
+ copyOptProperty(bid.params, 'position', banner, 'pos');
- if (pos !== '') {
- banner.pos = pos;
- }
imp.banner = banner;
}
- conversantImps.push(imp);
+ return imp;
});
- conversantBidReqs = {
- 'id': utils.getUniqueIdentifierStr(),
- 'imp': conversantImps,
-
- 'site': {
- 'id': siteId,
- 'mobile': document.querySelector('meta[name="viewport"][content*="width=device-width"]') !== null ? 1 : 0,
- 'page': page
+ const payload = {
+ id: requestId,
+ imp: conversantImps,
+ site: {
+ id: siteId,
+ mobile: document.querySelector('meta[name="viewport"][content*="width=device-width"]') !== null ? 1 : 0,
+ page: page
},
-
- 'device': getDevice(),
- 'at': 1
+ device: getDevice(),
+ at: 1
};
- var url = secure ? 'https:' + conversantUrl : location.protocol + conversantUrl;
- ajax(url, appendScript, JSON.stringify(conversantBidReqs), {
- withCredentials: true
- });
- };
+ return {
+ method: 'POST',
+ url: URL,
+ data: payload,
+ };
+ },
+ /**
+ * 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) {
+ const bidResponses = [];
+ const requestMap = {};
+ serverResponse = serverResponse.body;
+
+ if (bidRequest && bidRequest.data && bidRequest.data.imp) {
+ utils._each(bidRequest.data.imp, imp => requestMap[imp.id] = imp);
+ }
- var addEmptyBidResponses = function (placementsWithBidsBack) {
- var allConversantBidRequests = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'conversant');
-
- if (allConversantBidRequests && allConversantBidRequests.bids) {
- utils._each(allConversantBidRequests.bids, function (conversantBid) {
- if (!utils.contains(placementsWithBidsBack, conversantBid.placementCode)) {
- // Add a no-bid response for this placement.
- var bid = bidfactory.createBid(2, conversantBid);
- bid.bidderCode = 'conversant';
- bidmanager.addBidResponse(conversantBid.placementCode, bid);
- }
+ if (serverResponse && utils.isArray(serverResponse.seatbid)) {
+ utils._each(serverResponse.seatbid, function(bidList) {
+ utils._each(bidList.bid, function(conversantBid) {
+ const responseCPM = parseFloat(conversantBid.price);
+ if (responseCPM > 0.0 && conversantBid.impid) {
+ const responseAd = conversantBid.adm || '';
+ const responseNurl = conversantBid.nurl || '';
+ const request = requestMap[conversantBid.impid];
+
+ const bid = {
+ requestId: conversantBid.impid,
+ currency: serverResponse.cur || 'USD',
+ cpm: responseCPM,
+ creativeId: conversantBid.crid || ''
+ };
+
+ if (request.video) {
+ bid.vastUrl = responseAd;
+ bid.mediaType = 'video';
+
+ if (request.video.format.length >= 1) {
+ bid.width = request.video.format[0].w;
+ bid.height = request.video.format[0].h;
+ }
+ } else {
+ bid.ad = responseAd + '';
+ bid.width = conversantBid.w;
+ bid.height = conversantBid.h;
+ }
+
+ bidResponses.push(bid);
+ }
+ })
});
}
- };
- var parseSeatbid = function (bidResponse) {
- var placementsWithBidsBack = [];
- utils._each(bidResponse.bid, function (conversantBid) {
- var responseCPM;
- var placementCode = '';
- var id = conversantBid.impid;
- var bid = {};
- var responseAd;
- var responseNurl;
- var sizeArrayLength;
-
- // Bid request we sent Conversant
- var bidRequested = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'conversant').bids.find(bid => bid.bidId === id);
-
- if (bidRequested) {
- placementCode = bidRequested.placementCode;
- bidRequested.status = CONSTANTS.STATUS.GOOD;
- responseCPM = parseFloat(conversantBid.price);
-
- if (responseCPM !== 0.0) {
- conversantBid.placementCode = placementCode;
- placementsWithBidsBack.push(placementCode);
- conversantBid.size = bidRequested.sizes;
- responseAd = conversantBid.adm || '';
- responseNurl = conversantBid.nurl || '';
-
- // Our bid!
- bid = bidfactory.createBid(1, bidRequested);
- bid.creative_id = conversantBid.id || '';
- bid.bidderCode = 'conversant';
- bid.cpm = responseCPM;
-
- if (bidRequested.mediaType === 'video') {
- bid.vastUrl = responseAd;
- } else {
- // Track impression image onto returned html
- bid.ad = responseAd + '';
- }
+ return bidResponses;
+ },
+
+ /**
+ * Return use sync info
+ *
+ * @param {SyncOptions} syncOptions - Info about usersyncs that the adapter should obey
+ * @return {UserSync} Adapter sync type and url
+ */
+ getUserSyncs: function(syncOptions) {
+ if (syncOptions.pixelEnabled) {
+ return [{
+ type: 'image',
+ url: SYNC_URL
+ }];
+ }
+ }
+};
- sizeArrayLength = bidRequested.sizes.length;
- if (sizeArrayLength === 2 && typeof bidRequested.sizes[0] === 'number' && typeof bidRequested.sizes[1] === 'number') {
- bid.width = bidRequested.sizes[0];
- bid.height = bidRequested.sizes[1];
- } else {
- bid.width = bidRequested.sizes[0][0];
- bid.height = bidRequested.sizes[0][1];
- }
+/**
+ * Determine do-not-track state
+ *
+ * @returns {boolean}
+ */
+function getDNT() {
+ return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes';
+}
- bidmanager.addBidResponse(placementCode, bid);
- }
- }
- });
- addEmptyBidResponses(placementsWithBidsBack);
+/**
+ * Return openrtb device object that includes ua, width, and height.
+ *
+ * @returns {Device} Openrtb device object
+ */
+function getDevice() {
+ const language = navigator.language ? 'language' : 'userLanguage';
+ return {
+ h: screen.height,
+ w: screen.width,
+ dnt: getDNT() ? 1 : 0,
+ language: navigator[language].split('-')[0],
+ make: navigator.vendor ? navigator.vendor : '',
+ ua: navigator.userAgent
};
+}
- // Register our callback to the global object:
- $$PREBID_GLOBAL$$.conversantResponse = function (conversantResponseObj, path) {
- // valid object?
- if (conversantResponseObj && conversantResponseObj.id) {
- if (conversantResponseObj.seatbid && conversantResponseObj.seatbid.length > 0 && conversantResponseObj.seatbid[0].bid && conversantResponseObj.seatbid[0].bid.length > 0) {
- utils._each(conversantResponseObj.seatbid, parseSeatbid);
- } else {
- // no response data for any placements
- addEmptyBidResponses([]);
- }
- } else {
- // no response data for any placements
- addEmptyBidResponses([]);
- }
- // for debugging purposes
- if (path) {
- adloader.loadScript(path, function () {
- var allConversantBidRequests = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'conversant');
-
- if ($$PREBID_GLOBAL$$.conversantDebugResponse) {
- $$PREBID_GLOBAL$$.conversantDebugResponse(allConversantBidRequests);
- }
- });
- }
- }; // conversantResponse
+/**
+ * Convert arrays of widths and heights to an array of objects with w and h properties.
+ *
+ * [[300, 250], [300, 600]] => [{w: 300, h: 250}, {w: 300, h: 600}]
+ *
+ * @param {number[2][]|number[2]} bidSizes - arrays of widths and heights
+ * @returns {object[]} Array of objects with w and h
+ */
+function convertSizes(bidSizes) {
+ let format;
- return {
- callBids: callBids
- };
-};
+ if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') {
+ format = [{w: bidSizes[0], h: bidSizes[1]}];
+ } else {
+ format = utils._map(bidSizes, d => { return {w: d[0], h: d[1]}; });
+ }
-adaptermanager.registerBidAdapter(new ConversantAdapter(), 'conversant', {
- supportedMediaTypes: ['video']
-});
+ return format;
+}
-module.exports = ConversantAdapter;
+/**
+ * Check if it's a video bid request
+ *
+ * @param {BidRequest} bid - Bid request generated from ad slots
+ * @returns {boolean} True if it's a video bid
+ */
+function isVideoRequest(bid) {
+ return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video');
+}
+
+/**
+ * Copy property if exists from src to dst
+ *
+ * @param {object} src
+ * @param {string} srcName
+ * @param {object} dst
+ * @param {string} [dstName] - Optional. If not specified then srcName is used.
+ */
+function copyOptProperty(src, srcName, dst, dstName) {
+ dstName = dstName || srcName;
+ const obj = utils.getBidIdParameter(srcName, src);
+ if (obj !== '') {
+ dst[dstName] = obj;
+ }
+}
+
+registerBidder(spec);
diff --git a/modules/conversantBidAdapter.md b/modules/conversantBidAdapter.md
new file mode 100644
index 00000000000..1afdad6d544
--- /dev/null
+++ b/modules/conversantBidAdapter.md
@@ -0,0 +1,41 @@
+# Overview
+
+- Module Name: Conversant Bidder Adapter
+- Module Type: Bidder Adapter
+- Maintainer: mediapsr@conversantmedia.com
+
+# Description
+
+Module that connects to Conversant's demand sources. Supports banners and videos.
+
+# Test Parameters
+```
+var adUnits = [
+ {
+ code: 'banner-test-div',
+ sizes: [[300, 250]],
+ bids: [{
+ bidder: "conversant",
+ params: {
+ site_id: '108060'
+ }
+ }]
+ },{
+ code: 'video-test-div',
+ sizes: [640, 480],
+ mediaTypes: {
+ video: {
+ context: 'instream'
+ }
+ },
+ bids: [{
+ bidder: "conversant",
+ params: {
+ site_id: '88563',
+ api: [2],
+ protocols: [1, 2],
+ mimes: ['video/mp4']
+ }
+ }]
+ }];
+```
\ No newline at end of file
diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js
index 57cd9411e66..81da6867132 100644
--- a/test/spec/modules/conversantBidAdapter_spec.js
+++ b/test/spec/modules/conversantBidAdapter_spec.js
@@ -1,376 +1,277 @@
-var expect = require('chai').expect;
+import {expect} from 'chai';
+import {spec} from 'modules/conversantBidAdapter';
+import * as utils from 'src/utils';
+
var Adapter = require('modules/conversantBidAdapter');
var bidManager = require('src/bidmanager');
-describe('Conversant adapter tests', function () {
- var addBidResponseSpy;
- var adapter;
-
- var bidderRequest = {
- bidderCode: 'conversant',
- bids: [
- {
- bidId: 'bidId1',
- bidder: 'conversant',
- placementCode: 'div1',
- sizes: [[300, 600]],
- params: {
- site_id: '87293',
- position: 1,
- tag_id: 'tagid-1',
- secure: false
- }
- }, {
- bidId: 'bidId2',
- bidder: 'conversant',
- placementCode: 'div2',
- sizes: [[300, 600]],
- params: {
- site_id: '87293',
- secure: false
- }
- }, {
- bidId: 'bidId3',
- bidder: 'conversant',
- placementCode: 'div3',
- sizes: [[300, 600], [160, 600]],
- params: {
- site_id: '87293',
- position: 1,
- tag_id: '',
- secure: false
+describe('Conversant adapter tests', function() {
+ const siteId = '108060';
+
+ const bidRequests = [
+ {
+ bidder: 'conversant',
+ params: {
+ site_id: siteId,
+ position: 1,
+ tag_id: 'tagid-1',
+ secure: false,
+ bidfloor: 0.5
+ },
+ placementCode: 'pcode000',
+ transactionId: 'tx000',
+ sizes: [[300, 250]],
+ bidId: 'bid000',
+ bidderRequestId: '117d765b87bed38',
+ requestId: 'req000'
+ }, {
+ bidder: 'conversant',
+ params: {
+ site_id: siteId,
+ secure: false
+ },
+ placementCode: 'pcode001',
+ transactionId: 'tx001',
+ sizes: [[468, 60]],
+ bidId: 'bid001',
+ bidderRequestId: '117d765b87bed38',
+ requestId: 'req000'
+ }, {
+ bidder: 'conversant',
+ params: {
+ site_id: siteId,
+ position: 2,
+ tag_id: '',
+ secure: false
+ },
+ placementCode: 'pcode002',
+ transactionId: 'tx002',
+ sizes: [[300, 600], [160, 600]],
+ bidId: 'bid002',
+ bidderRequestId: '117d765b87bed38',
+ requestId: 'req000'
+ }, {
+ bidder: 'conversant',
+ params: {
+ site_id: siteId,
+ api: [2],
+ protocols: [1, 2],
+ mimes: ['video/mp4', 'video/x-flv'],
+ maxduration: 30
+ },
+ mediaTypes: {
+ video: {
+ context: 'instream'
}
- }, {
- bidId: 'bidId4',
- bidder: 'conversant',
- placementCode: 'div4',
- mediaType: 'video',
- sizes: [[480, 480]],
- params: {
- site_id: '89192',
- pos: 1,
- tagid: 'tagid-4',
- secure: false
- }
- }
- ]
- };
-
- it('The Conversant response should exist and be a function', function () {
- expect($$PREBID_GLOBAL$$.conversantResponse).to.exist.and.to.be.a('function');
- });
-
- describe('Should submit bid responses correctly', function () {
- beforeEach(function () {
- addBidResponseSpy = sinon.stub(bidManager, 'addBidResponse');
- $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest);
- adapter = new Adapter();
- });
-
- afterEach(function () {
- addBidResponseSpy.restore();
- });
-
- it('Should correctly submit valid and empty bids to the bid manager', function () {
- var bidResponse = {
- id: 123,
- seatbid: [{
- bid: [{
- id: 1111111,
- impid: 'bidId1',
- price: 0
- }, {
- id: 2345,
- impid: 'bidId2',
- price: 0.22,
- nurl: '',
- adm: 'adm2',
- h: 300,
- w: 600
- }]
- }]
- };
-
- $$PREBID_GLOBAL$$.conversantResponse(bidResponse);
-
- // in this case, the valid bid (div2) is submitted before the empty bids (div1, div3)
- var firstBid = addBidResponseSpy.getCall(0).args[1];
- var secondBid = addBidResponseSpy.getCall(1).args[1];
- var thirdBid = addBidResponseSpy.getCall(2).args[1];
- var placementCode1 = addBidResponseSpy.getCall(0).args[0];
- var placementCode2 = addBidResponseSpy.getCall(1).args[0];
- var placementCode3 = addBidResponseSpy.getCall(2).args[0];
-
- expect(firstBid.getStatusCode()).to.equal(1);
- expect(firstBid.bidderCode).to.equal('conversant');
- expect(firstBid.cpm).to.equal(0.22);
- expect(firstBid.ad).to.equal('adm2' + '');
- expect(placementCode1).to.equal('div2');
-
- expect(secondBid.getStatusCode()).to.equal(2);
- expect(secondBid.bidderCode).to.equal('conversant');
- expect(placementCode2).to.equal('div1');
-
- expect(thirdBid.getStatusCode()).to.equal(2);
- expect(thirdBid.bidderCode).to.equal('conversant');
- expect(placementCode3).to.equal('div3');
-
- expect(addBidResponseSpy.getCalls().length).to.equal(4);
- });
-
- it('Should submit bids with statuses of 2 to the bid manager for empty bid responses', function () {
- $$PREBID_GLOBAL$$.conversantResponse({id: 1, seatbid: []});
-
- var placementCode1 = addBidResponseSpy.getCall(0).args[0];
- var firstBid = addBidResponseSpy.getCall(0).args[1];
- var placementCode2 = addBidResponseSpy.getCall(1).args[0];
- var secondBid = addBidResponseSpy.getCall(1).args[1];
- var placementCode3 = addBidResponseSpy.getCall(2).args[0];
- var thirdBid = addBidResponseSpy.getCall(2).args[1];
-
- expect(placementCode1).to.equal('div1');
- expect(firstBid.getStatusCode()).to.equal(2);
- expect(firstBid.bidderCode).to.equal('conversant');
-
- expect(placementCode2).to.equal('div2');
- expect(secondBid.getStatusCode()).to.equal(2);
- expect(secondBid.bidderCode).to.equal('conversant');
-
- expect(placementCode3).to.equal('div3');
- expect(thirdBid.getStatusCode()).to.equal(2);
- expect(thirdBid.bidderCode).to.equal('conversant');
-
- expect(addBidResponseSpy.getCalls().length).to.equal(4);
- });
-
- it('Should submit valid bids to the bid manager', function () {
- var bidResponse = {
- id: 123,
- seatbid: [{
- bid: [{
- id: 1111111,
- impid: 'bidId1',
- price: 0.11,
- nurl: '',
- adm: 'adm',
- h: 250,
- w: 300,
- ext: {}
- }, {
- id: 2345,
- impid: 'bidId2',
- price: 0.22,
- nurl: '',
- adm: 'adm2',
- h: 300,
- w: 600
- }, {
- id: 33333,
- impid: 'bidId3',
- price: 0.33,
- nurl: '',
- adm: 'adm3',
- h: 160,
- w: 600
- }]
- }]
- };
-
- $$PREBID_GLOBAL$$.conversantResponse(bidResponse);
-
- var firstBid = addBidResponseSpy.getCall(0).args[1];
- var secondBid = addBidResponseSpy.getCall(1).args[1];
- var thirdBid = addBidResponseSpy.getCall(2).args[1];
- var placementCode1 = addBidResponseSpy.getCall(0).args[0];
- var placementCode2 = addBidResponseSpy.getCall(1).args[0];
- var placementCode3 = addBidResponseSpy.getCall(2).args[0];
-
- expect(firstBid.getStatusCode()).to.equal(1);
- expect(firstBid.bidderCode).to.equal('conversant');
- expect(firstBid.cpm).to.equal(0.11);
- expect(firstBid.ad).to.equal('adm' + '');
- expect(placementCode1).to.equal('div1');
-
- expect(secondBid.getStatusCode()).to.equal(1);
- expect(secondBid.bidderCode).to.equal('conversant');
- expect(secondBid.cpm).to.equal(0.22);
- expect(secondBid.ad).to.equal('adm2' + '');
- expect(placementCode2).to.equal('div2');
-
- expect(thirdBid.getStatusCode()).to.equal(1);
- expect(thirdBid.bidderCode).to.equal('conversant');
- expect(thirdBid.cpm).to.equal(0.33);
- expect(thirdBid.ad).to.equal('adm3' + '');
- expect(placementCode3).to.equal('div3');
-
- expect(addBidResponseSpy.getCalls().length).to.equal(4);
- });
-
- it('Should submit video bid responses correctly.', function () {
- var bidResponse = {
- id: 123,
- seatbid: [{
- bid: [{
- id: 1111111,
- impid: 'bidId4',
- price: 0.11,
- nurl: 'imp_tracker',
- adm: 'vasturl'
- }]
- }]
- };
-
- $$PREBID_GLOBAL$$.conversantResponse(bidResponse);
-
- var videoBid = addBidResponseSpy.getCall(0).args[1];
- var placementCode = addBidResponseSpy.getCall(0).args[0];
-
- expect(videoBid.getStatusCode()).to.equal(1);
- expect(videoBid.bidderCode).to.equal('conversant');
- expect(videoBid.cpm).to.equal(0.11);
- expect(videoBid.vastUrl).to.equal('vasturl');
- expect(placementCode).to.equal('div4');
- })
- });
-
- describe('Should submit the correct headers in the xhr', function () {
- var server,
- adapter;
-
- var bidResponse = {
- id: 123,
+ },
+ placementCode: 'pcode003',
+ transactionId: 'tx003',
+ sizes: [640, 480],
+ bidId: 'bid003',
+ bidderRequestId: '117d765b87bed38',
+ requestId: 'req000'
+ }];
+
+ const bidResponses = {
+ body: {
+ id: 'req000',
seatbid: [{
bid: [{
- id: 1111,
- impid: 'bidId1',
- price: 0.11,
- nurl: '',
- adm: 'adm',
- h: 250,
+ nurl: 'notify000',
+ adm: 'markup000',
+ crid: '1000',
+ impid: 'bid000',
+ price: 0.99,
w: 300,
- ext: {}
+ h: 250,
+ adomain: ['https://example.com'],
+ id: 'bid000'
}, {
- id: 2222,
- impid: 'bidId2',
- price: 0.22,
- nurl: '',
- adm: 'adm2',
- h: 300,
- w: 600
+ impid: 'bid001',
+ price: 0.00000,
+ id: 'bid001'
}, {
- id: 3333,
- impid: 'bidId3',
- price: 0.33,
- nurl: '',
- adm: 'adm3',
- h: 160,
- w: 600
- }]
- }]
- };
-
- beforeEach(function () {
- server = sinon.fakeServer.create();
- adapter = new Adapter();
- });
-
- afterEach(function () {
- server.restore();
- });
-
- beforeEach(function () {
- var resp = [200, {'Content-type': 'text/javascript'}, '$$PREBID_GLOBAL$$.conversantResponse(\'' + JSON.stringify(bidResponse) + '\')'];
- server.respondWith('POST', new RegExp('media.msg.dotomi.com/s2s/header'), resp);
- });
-
- it('Should contain valid request header properties', function () {
- adapter.callBids(bidderRequest);
- server.respond();
-
- var request = server.requests[0];
- expect(request.requestBody).to.not.be.empty;
- });
- });
- describe('Should create valid bid requests.', function () {
- var server,
- adapter;
-
- var bidResponse = {
- id: 123,
- seatbid: [{
- bid: [{
- id: 1111,
- impid: 'bidId1',
- price: 0.11,
- nurl: '',
- adm: 'adm',
- h: 250,
+ nurl: 'notify002',
+ adm: 'markup002',
+ crid: '1002',
+ impid: 'bid002',
+ price: 2.99,
w: 300,
- ext: {}
- }, {
- id: 2222,
- impid: 'bidId2',
- price: 0.22,
- nurl: '',
- adm: 'adm2',
- h: 300,
- w: 600
+ h: 600,
+ adomain: ['https://example.com'],
+ id: 'bid002'
}, {
- id: 3333,
- impid: 'bidId3',
- price: 0.33,
- nurl: '',
- adm: 'adm3',
- h: 160,
- w: 600
+ nurl: 'notify003',
+ adm: 'markup003',
+ crid: '1003',
+ impid: 'bid003',
+ price: 3.99,
+ adomain: ['https://example.com'],
+ id: 'bid003'
}]
}]
- };
-
- beforeEach(function () {
- server = sinon.fakeServer.create();
- adapter = new Adapter();
- });
-
- afterEach(function () {
- server.restore();
- });
+ },
+ headers: {}};
+
+ it('Verify basic properties', function() {
+ expect(spec.code).to.equal('conversant');
+ expect(spec.aliases).to.be.an('array').with.lengthOf(1);
+ expect(spec.aliases[0]).to.equal('cnvr');
+ expect(spec.supportedMediaTypes).to.be.an('array').with.lengthOf(1);
+ expect(spec.supportedMediaTypes[0]).to.equal('video');
+ });
- beforeEach(function () {
- var resp = [200, {'Content-type': 'text/javascript'}, '$$PREBID_GLOBAL$$.conversantResponse(\'' + JSON.stringify(bidResponse) + '\')'];
- server.respondWith('POST', new RegExp('media.msg.dotomi.com/s2s/header'), resp);
- });
+ it('Verify user syncs', function() {
+ expect(spec.getUserSyncs({})).to.be.undefined;
+ expect(spec.getUserSyncs({iframeEnabled: true})).to.be.undefined;
+ expect(spec.getUserSyncs({pixelEnabled: false})).to.be.undefined;
- it('Should create valid bid requests.', function () {
- adapter.callBids(bidderRequest);
- server.respond();
- var request = JSON.parse(server.requests[0].requestBody);
- expect(request.imp[0].banner.format[0].w).to.equal(300);
- expect(request.imp[0].banner.format[0].h).to.equal(600);
- expect(request.imp[0].tagid).to.equal('tagid-1');
- expect(request.imp[0].banner.pos).to.equal(1);
- expect(request.imp[0].secure).to.equal(0);
- expect(request.site.id).to.equal('89192');
- });
+ const syncs = spec.getUserSyncs({pixelEnabled: true});
+ expect(syncs).to.be.an('array').with.lengthOf(1);
+ expect(syncs[0].type).to.equal('image');
+ expect(syncs[0].url).to.equal('//media.msg.dotomi.com/w/user.sync');
+ });
- it('Should not pass empty or missing optional parameters on requests.', function () {
- adapter.callBids(bidderRequest);
- server.respond();
+ it('Verify isBidRequestValid', function() {
+ expect(spec.isBidRequestValid({})).to.be.false;
+ expect(spec.isBidRequestValid({params: {}})).to.be.false;
+ expect(spec.isBidRequestValid({params: {site_id: '123'}})).to.be.true;
+ expect(spec.isBidRequestValid(bidRequests[0])).to.be.true;
+ expect(spec.isBidRequestValid(bidRequests[1])).to.be.true;
+ expect(spec.isBidRequestValid(bidRequests[2])).to.be.true;
+ expect(spec.isBidRequestValid(bidRequests[3])).to.be.true;
+
+ const simpleVideo = JSON.parse(JSON.stringify(bidRequests[3]));
+ simpleVideo.params.site_id = 123;
+ expect(spec.isBidRequestValid(simpleVideo)).to.be.false;
+ simpleVideo.params.site_id = siteId;
+ simpleVideo.params.mimes = [1, 2, 3];
+ expect(spec.isBidRequestValid(simpleVideo)).to.be.false;
+ simpleVideo.params.mimes = 'bad type';
+ expect(spec.isBidRequestValid(simpleVideo)).to.be.false;
+ delete simpleVideo.params.mimes;
+ expect(spec.isBidRequestValid(simpleVideo)).to.be.true;
+ });
- var request = JSON.parse(server.requests[0].requestBody);
- expect(request.imp[1].tagid).to.equal(undefined);
- expect(request.imp[2].tagid).to.equal(undefined);
- expect(request.imp[1].pos).to.equal(undefined);
- });
+ it('Verify buildRequest', function() {
+ const request = spec.buildRequests(bidRequests);
+ expect(request.method).to.equal('POST');
+ expect(request.url).to.equal('//media.msg.dotomi.com/s2s/header/24');
+ const payload = request.data;
+
+ expect(payload).to.have.property('id', 'req000');
+ expect(payload).to.have.property('at', 1);
+ expect(payload).to.have.property('imp');
+ expect(payload.imp).to.be.an('array').with.lengthOf(4);
+
+ expect(payload.imp[0]).to.have.property('id', 'bid000');
+ expect(payload.imp[0]).to.have.property('secure', 0);
+ expect(payload.imp[0]).to.have.property('bidfloor', 0.5);
+ expect(payload.imp[0]).to.have.property('displaymanager', 'Prebid.js');
+ expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/);
+ expect(payload.imp[0]).to.have.property('tagid', 'tagid-1');
+ expect(payload.imp[0]).to.have.property('banner');
+ expect(payload.imp[0].banner).to.have.property('pos', 1);
+ expect(payload.imp[0].banner).to.have.property('format');
+ expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]);
+ expect(payload.imp[0]).to.not.have.property('video');
+
+ expect(payload.imp[1]).to.have.property('id', 'bid001');
+ expect(payload.imp[1]).to.have.property('secure', 0);
+ expect(payload.imp[1]).to.have.property('bidfloor', 0);
+ expect(payload.imp[1]).to.have.property('displaymanager', 'Prebid.js');
+ expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/);
+ expect(payload.imp[1]).to.not.have.property('tagid');
+ expect(payload.imp[1]).to.have.property('banner');
+ expect(payload.imp[1].banner).to.not.have.property('pos');
+ expect(payload.imp[1].banner).to.have.property('format');
+ expect(payload.imp[1].banner.format).to.deep.equal([{w: 468, h: 60}]);
+
+ expect(payload.imp[2]).to.have.property('id', 'bid002');
+ expect(payload.imp[2]).to.have.property('secure', 0);
+ expect(payload.imp[2]).to.have.property('bidfloor', 0);
+ expect(payload.imp[2]).to.have.property('displaymanager', 'Prebid.js');
+ expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/);
+ expect(payload.imp[2]).to.have.property('banner');
+ expect(payload.imp[2].banner).to.have.property('pos', 2);
+ expect(payload.imp[2].banner).to.have.property('format');
+ expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 600}, {w: 160, h: 600}]);
+
+ expect(payload.imp[3]).to.have.property('id', 'bid003');
+ expect(payload.imp[3]).to.have.property('secure', 0);
+ expect(payload.imp[3]).to.have.property('bidfloor', 0);
+ expect(payload.imp[3]).to.have.property('displaymanager', 'Prebid.js');
+ expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/);
+ expect(payload.imp[3]).to.not.have.property('tagid');
+ expect(payload.imp[3]).to.have.property('video');
+ expect(payload.imp[3].video).to.not.have.property('pos');
+ expect(payload.imp[3].video).to.have.property('format');
+ expect(payload.imp[3].video.format).to.deep.equal([{w: 640, h: 480}]);
+ expect(payload.imp[3].video).to.have.property('mimes');
+ expect(payload.imp[3].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']);
+ expect(payload.imp[3].video).to.have.property('protocols');
+ expect(payload.imp[3].video.protocols).to.deep.equal([1, 2]);
+ expect(payload.imp[3].video).to.have.property('api');
+ expect(payload.imp[3].video.api).to.deep.equal([2]);
+ expect(payload.imp[3].video).to.have.property('maxduration', 30);
+ expect(payload.imp[3]).to.not.have.property('banner');
+
+ expect(payload).to.have.property('site');
+ expect(payload.site).to.have.property('id', siteId);
+ expect(payload.site).to.have.property('mobile').that.is.oneOf([0, 1]);
+ const loc = utils.getTopWindowLocation();
+ const page = loc.pathname + loc.search + loc.hash;
+ expect(payload.site).to.have.property('page', page);
+
+ expect(payload).to.have.property('device');
+ expect(payload.device).to.have.property('w', screen.width);
+ expect(payload.device).to.have.property('h', screen.height);
+ expect(payload.device).to.have.property('dnt').that.is.oneOf([0, 1]);
+ expect(payload.device).to.have.property('ua', navigator.userAgent);
+ });
- it('Should create the format objects correctly.', function () {
- adapter.callBids(bidderRequest);
- server.respond();
+ it('Verify interpretResponse', function() {
+ const request = spec.buildRequests(bidRequests);
+ const response = spec.interpretResponse(bidResponses, request);
+ expect(response).to.be.an('array').with.lengthOf(3);
+
+ let bid = response[0];
+ expect(bid).to.have.property('requestId', 'bid000');
+ expect(bid).to.have.property('currency', 'USD');
+ expect(bid).to.have.property('cpm', 0.99);
+ expect(bid).to.have.property('creativeId', '1000');
+ expect(bid).to.have.property('width', 300);
+ expect(bid).to.have.property('height', 250);
+ expect(bid).to.have.property('ad', 'markup000');
+
+ // There is no bid001 because cpm is $0
+
+ bid = response[1];
+ expect(bid).to.have.property('requestId', 'bid002');
+ expect(bid).to.have.property('currency', 'USD');
+ expect(bid).to.have.property('cpm', 2.99);
+ expect(bid).to.have.property('creativeId', '1002');
+ expect(bid).to.have.property('width', 300);
+ expect(bid).to.have.property('height', 600);
+ expect(bid).to.have.property('ad', 'markup002');
+
+ bid = response[2];
+ expect(bid).to.have.property('requestId', 'bid003');
+ expect(bid).to.have.property('currency', 'USD');
+ expect(bid).to.have.property('cpm', 3.99);
+ expect(bid).to.have.property('creativeId', '1003');
+ expect(bid).to.have.property('width', 640);
+ expect(bid).to.have.property('height', 480);
+ expect(bid).to.have.property('vastUrl', 'markup003');
+ expect(bid).to.have.property('mediaType', 'video');
+ });
- var request = JSON.parse(server.requests[0].requestBody);
- expect(request.imp[2].banner.format.length).to.equal(2);
- expect(request.imp[2].banner.format[0].w).to.equal(300);
- expect(request.imp[2].banner.format[1].w).to.equal(160);
- });
+ it('Verify handling of bad responses', function() {
+ let response = spec.interpretResponse({}, {});
+ expect(response).to.be.an('array').with.lengthOf(0);
+ response = spec.interpretResponse({id: '123'}, {});
+ expect(response).to.be.an('array').with.lengthOf(0);
+ response = spec.interpretResponse({id: '123', seatbid: []}, {});
+ expect(response).to.be.an('array').with.lengthOf(0);
});
-});
+})