From 8c42f029fc18e7ea3a9f820e3770e2f593e0d0bd Mon Sep 17 00:00:00 2001 From: Aziz Saleh Date: Tue, 7 Jan 2020 19:38:44 -0500 Subject: [PATCH] Add Revcontent Adapter (#4654) * Initial commit * Update * Update js * Update file name * Making it work (at least getting a response) * Update native logic * Updates to bidder * Update prebidder * Remove debugs * Updates * Add new templates * Update templates * Update email address * Resolve conflict * Resolve conflict * Rebase from master * Rebase from master * Add Revcontent Adapter * Force https, update supportedMediaTypes and add default return for extractHostname method * Add test file + remove console.log statement --- .../gpt/revcontent_example.html | 109 ++++++ modules/revcontentBidAdapter.js | 290 +++++++++++++++ modules/revcontentBidAdapter.md | 67 ++++ package-lock.json | 0 .../spec/modules/revcontentBidAdapter_spec.js | 330 ++++++++++++++++++ 5 files changed, 796 insertions(+) create mode 100644 integrationExamples/gpt/revcontent_example.html create mode 100644 modules/revcontentBidAdapter.js create mode 100644 modules/revcontentBidAdapter.md mode change 100644 => 100755 package-lock.json create mode 100644 test/spec/modules/revcontentBidAdapter_spec.js diff --git a/integrationExamples/gpt/revcontent_example.html b/integrationExamples/gpt/revcontent_example.html new file mode 100644 index 00000000000..ad0933885f3 --- /dev/null +++ b/integrationExamples/gpt/revcontent_example.html @@ -0,0 +1,109 @@ + + + + + + + + + + + +

Basic Prebid.js Example

+
Div-1
+
+ +
+ + + \ No newline at end of file diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js new file mode 100644 index 00000000000..8adc166caac --- /dev/null +++ b/modules/revcontentBidAdapter.js @@ -0,0 +1,290 @@ +// jshint esversion: 6, es3: false, node: true +'use strict'; + +import {registerBidder} from '../src/adapters/bidderFactory'; +import * as utils from '../src/utils'; +import {ajax} from '../src/ajax'; + +const BIDDER_CODE = 'revcontent'; +const NATIVE_PARAMS = { + title: { + id: 0, + name: 'title' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + } +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: ['native'], + isBidRequestValid: function (bid) { + return (typeof bid.params.apiKey !== 'undefined' && typeof bid.params.userId !== 'undefined' && bid.hasOwnProperty('nativeParams')); + }, + buildRequests: (validBidRequests, bidderRequest) => { + const userId = validBidRequests[0].params.userId; + const widgetId = validBidRequests[0].params.widgetId; + const apiKey = validBidRequests[0].params.apiKey; + var domain = validBidRequests[0].params.domain; + var host = validBidRequests[0].params.endpoint; + + if (typeof host === 'undefined') { + host = 'trends.revcontent.com'; + } + + let serverRequests = []; + var refererInfo; + if (bidderRequest && bidderRequest.refererInfo) { + refererInfo = bidderRequest.refererInfo.referer; + } + + if (typeof domain === 'undefined') { + domain = extractHostname(refererInfo); + } + + var endpoint = 'https://' + host + '/rtb?apiKey=' + apiKey + '&userId=' + userId; + + if (!isNaN(widgetId) && widgetId > 0) { + endpoint = endpoint + '&widgetId=' + widgetId; + } + + const imp = validBidRequests.map((bid, id) => { + if (bid.hasOwnProperty('nativeParams')) { + const assets = utils._map(bid.nativeParams, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1 + }; + if (props) { + asset.id = props.id; + let wmin, hmin, w, h; + let aRatios = bidParams.aspect_ratios; + + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } + + asset[props.name] = { + len: bidParams.len, + type: props.type, + wmin, + hmin, + w, + h + }; + + return asset; + } + }).filter(Boolean); + + return { + id: id + 1, + tagid: bid.params.mid, + bidderRequestId: bid.bidderRequestId, + auctionId: bid.auctionId, + transactionId: bid.transactionId, + native: { + request: { + ver: '1.1', + context: 2, + contextsubtype: 21, + plcmttype: 4, + plcmtcnt: 4, + assets: assets + }, + ver: '1.1', + battr: [1, 3, 8, 11, 17] + }, + instl: 0, + bidfloor: 0.1, + secure: '1' + }; + } + }); + + let data = { + id: bidderRequest.auctionId, + imp: imp, + site: { + id: widgetId, + domain: domain, + page: refererInfo, + cat: ['IAB17'], + publisher: { + id: userId, + domain: domain + } + }, + device: { + ua: navigator.userAgent, + language: 'en' + }, + user: { + id: 1 + }, + at: 2, + bcat: [ + 'IAB24', + 'IAB25', + 'IAB25-1', + 'IAB25-2', + 'IAB25-3', + 'IAB25-4', + 'IAB25-5', + 'IAB25-6', + 'IAB25-7', + 'IAB26', + 'IAB26-1', + 'IAB26-2', + 'IAB26-3', + 'IAB26-4' + ] + }; + + serverRequests.push({ + method: 'POST', + options: { + contentType: 'application/json' + }, + url: endpoint, + data: JSON.stringify(data), + bid: validBidRequests + }); + + return serverRequests; + }, + interpretResponse: function (serverResponse, originalBidRequest) { + if (!serverResponse.body) { + return; + } + + const seatbid = serverResponse.body.seatbid[0]; + const bidResponses = []; + + for (var x in seatbid.bid) { + let adm = JSON.parse(seatbid.bid[x]['adm']); + let ad = { + clickUrl: adm.link.url + }; + + adm.assets.forEach(asset => { + switch (asset.id) { + case 3: + ad['image'] = { + url: asset.img.url, + height: 1, + width: 1 + }; + break; + case 0: + ad['title'] = asset.title.text; + break; + case 5: + ad['sponsoredBy'] = asset.data.value; + break; + } + }); + + var size = originalBidRequest.bid[0].params.size; + + const bidResponse = { + bidder: BIDDER_CODE, + requestId: originalBidRequest.bid[0].bidId, + cpm: seatbid.bid[x]['price'], + creativeId: seatbid.bid[x]['adid'], + currency: 'USD', + netRevenue: true, + ttl: 360, + nurl: seatbid.bid[x]['nurl'], + bidderCode: 'revcontent', + mediaType: 'native', + native: ad, + width: size.width, + height: size.height, + ad: displayNative(ad, getTemplate(size, originalBidRequest.bid[0].params.template)) + }; + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + onBidWon: function (bid) { + var winUrl = bid.nurl; + winUrl = winUrl.replace(/\$\{AUCTION_PRICE\}/, bid.cpm); + var host = extractHostname(winUrl); + + ajax(winUrl + '&viewed=1', null, {withCredentials: true}); + ajax('https://' + host + '/imp.php', null, 'v=' + encodeURIComponent(encodeURIComponent(getQueryVariable('d', winUrl))) + '&i=' + encodeURIComponent(window.location.href), {method: 'POST', contentType: 'application/x-www-form-urlencoded'}); + + return true; + } +}; + +registerBidder(spec); + +function displayNative(ad, template) { + template = template.replace(/{image}/g, ad['image']['url']); + template = template.replace(/{title}/g, ad['title']); + template = template.replace(/{clickUrl}/g, ad['clickUrl']); + template = template.replace(/{sponsoredBy}/g, ad['sponsoredBy']); + return template; +} + +function getTemplate(size, customTemplate) { + if (typeof (customTemplate) !== 'undefined' && customTemplate !== '') { + return customTemplate; + } + + if (size.width == 300 && size.height == 250) { + return '

{title}

SEE MORE
'; + } + + if (size.width == 728 && size.height == 90) { + return '

{title}

>
'; + } + + if (size.width == 300 && size.height == 600) { + return '

{title}

>
'; + } + + return ''; +} + +function extractHostname(url) { + if (typeof url == 'undefined' || url == null) { + return ''; + } + var hostname; + if (url.indexOf('//') > -1) { + hostname = url.split('/')[2]; + } else { + hostname = url.split('/')[0]; + } + + hostname = hostname.split(':')[0]; + hostname = hostname.split('?')[0]; + + return hostname; +} + +function getQueryVariable(variable, url) { + var query = url; + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + if (decodeURIComponent(pair[0]) == variable) { + return decodeURIComponent(pair[1]); + } + } +} diff --git a/modules/revcontentBidAdapter.md b/modules/revcontentBidAdapter.md new file mode 100644 index 00000000000..8f9e5ed48c7 --- /dev/null +++ b/modules/revcontentBidAdapter.md @@ -0,0 +1,67 @@ +# Overview + +Module Name: Revcontent Adapater +Maintainer: aziz@revcontent.com + +# Description + +Revcontent Adpater + +# Test Parameters +``` + /* + Supported sizes: + ---------------- + 300x250 - Medium rectangle + 728x90 - Leaderboard + 300x600 - Half page or large skyscraper + */ + var size = {width: 300, height: 250}; + + var adUnits = [{ + code: '/19968336/header-bid-tag-1', + sizes: sizes, + mediaTypes: { + native: { + native: { + image: { + required: false, + sizes: sizes[0] + }, + title: { + required: false, + len: 140 + }, + clickUrl: { + required: false + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + } + } + } + }, + bids: [{ + bidder: 'revcontent', + params: { + size: size, + + /* -> Modify this section <- */ + apiKey: '8a33sdfsdfdsfsdfssss544f8sdfsdfsdfd3b1c', // Required + userId: 69565, // Required + widgetId: 599995, // Optional + domain: 'test.com', // Optional - Default referral hostname + endpoint: 'trends.revcontent.com' // Optional/Debug - Set different endpoint + /* + Optional - Set different template. Template variables: + {clickUrl} -> Target Url + {image} -> Image URL + {title} -> Ad Title + */ + template: '

{title}

SEE MORE
' + } + }] + }]; +``` diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 diff --git a/test/spec/modules/revcontentBidAdapter_spec.js b/test/spec/modules/revcontentBidAdapter_spec.js new file mode 100644 index 00000000000..810dcf1010a --- /dev/null +++ b/test/spec/modules/revcontentBidAdapter_spec.js @@ -0,0 +1,330 @@ +// jshint esversion: 6, es3: false, node: true +import {assert, expect} from 'chai'; +import * as url from 'src/url'; +import {spec} from 'modules/revcontentBidAdapter'; +import { NATIVE } from 'src/mediaTypes'; +import { config } from 'src/config'; + +describe('revcontent adapter', function () { + let serverResponse, bidRequest, bidResponses; + let bids = []; + + describe('isBidRequestValid', function () { + let bid = { + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + } + }; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when required params are missing', function () { + bid.params.apiKey = undefined; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + it('should send request with correct structure', function () { + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + widgetId: 33861, + endpoint: 'trends-s0.revcontent.com' + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + request = request[0]; + assert.equal(request.method, 'POST'); + assert.equal(request.url, 'https://trends-s0.revcontent.com/rtb?apiKey=8a33fa9cf220ae685dcc3544f847cdda858d3b1c&userId=673&widgetId=33861'); + assert.deepEqual(request.options, {contentType: 'application/json'}); + assert.ok(request.data); + }); + + it('should have default request structure', function () { + let keys = 'method,options,url,data,bid'.split(','); + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + + request = request[0]; + let data = Object.keys(request); + + assert.deepEqual(keys, data); + }); + + it('should send info about device', function () { + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + request = JSON.parse(request[0].data); + assert.equal(request.device.ua, navigator.userAgent); + }); + + it('should send info about the site', function () { + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: { + image: { + required: false + }, + "title": { + required: false, + len: 140 + }, + clickUrl: { + required: false + }, + sponsoredBy: { + id: 5, + name: "data", + type: 1 + } + }, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + } + }]; + let refererInfo = {referer: 'page'}; + let request = spec.buildRequests(validBidRequests, {refererInfo}); + + request = JSON.parse(request[0].data); + + assert.deepEqual(request.site, { + domain: 'test.com', + page: 'page', + cat: ['IAB17'], + publisher: {id: 673, domain: 'test.com'} + }); + }); + }); + + describe('interpretResponse', function () { + it('should return if no body in response', function () { + let serverResponse = {}; + let bidRequest = {}; + + assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); + }); + + const serverResponse = { + body: { + id: "5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58", + seatbid: [ + { + bid: [ + { + id: "6bbe3eed-f443-4e2b-a8da-57fd6327b37d", + impid: "1", + price: 0.1, + adid: "4162547", + nurl: "https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D", + adm: "{\"ver\":\"1.1\",\"assets\":[{\"id\":3,\"required\":1,\"img\":{\"url\":\"//img.revcontent.com/?url=https://revcontent-p0.s3.amazonaws.com/content/images/15761052960288727821.jpg&static=true\"}},{\"id\":0,\"required\":1,\"title\":{\"text\":\"Do You Eat Any of These Craving-trigger Foods?\"}},{\"id\":5,\"required\":1,\"data\":{\"value\":\"\"}}],\"link\":{\"url\":\"https://trends-s0.revcontent.com/click.php?d=A7EVbNYBVyonty19Ak08zCr9J54qg%2Bmduq6p0Zyn5%2F%2Bapm4deUo9VAXmOGEIbUBf6i7m3%2F%2FWJm%2FzTha8SJ%2Br9MZL9jhhUxDeiKb6aRY1biLrvr6tFUd1phvtKqVmPd76l9VBLFMxS1brSzKjRCJlIGmyGJg7ueFvxpE9X%2BpHmdbE2uqUdRC49ENO3XZyHCCKMAZ8XD29fasX9Kli9mKpZTqw8vayFlXbVYSUwB8wfSwCt1sIUrt0aICYc0jcyWU3785GTS1xXzQj%2FIVszFYYrdTWd%2BDijjNZtFny0OomPHp8lRy5VcQVCuLpw0Fks4myvsE38XcNvs4wO3tWTNrI%2BMqcW1%2BD2OnMSq5nN5FCbmi2ly%2F1LbN9fibaFvW%2FQbzQhN9ZsAwmhm409UTtdmSA6hd96vDxDWLeUJhVO3UQyI0yq2TtVnB9tEICD8mZNWwYehOab%2BQ1EWmTerF6ZCDx8RyZus1UrsDfRwvTCyUjCmkZhmeo4QVJkpPy6QobCsngSaxkkKhH%2Fb7coZyBXXEt3ORoYBLUbfRO6nR8GdIt8413vrYr4gTAroh46VcWK0ls0gFNe2u3%2FqP%2By1yLKbzDVaR%2Fa02G%2Biiqbw86sCYfsy7qK9atyjNTm8RkH6JLESUzxc6IEazu4iwHKGnu5phTacmseXCi8y9Y5AdBZn8VnLP%2F2a%2FyAqq93xEH%2BIrkAdhGRY1tY39rBYAtvH%2FVyNFZcong%2FutUMYbp0WhDNyfl6iWxmpE28Cx9KDcqXss0NIwQm0AWeu8ogJCIG3faAkm5PdFsUdf2X9h3HuFDbnbvnXW27ml6z9GykEzv%2F8aSZlMZ\"}}" + } + ] + } + ], + bidid: "7f729368-edb2-427a-bde7-a55b3bf8837c" + }, + headers: {} + }; + + const bidRequest = { + method: "POST", + options: { + contentType: "application/json" + }, + url: "https://trends-s0.revcontent.com/rtb?apiKey=8a33fa9cf220ae685dcc3544f847cdda858d3b1c&userId=673", + data: "{\"id\":\"5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58\",\"imp\":[{\"id\":1,\"bidderRequestId\":\"14e4dab7b5396e8\",\"auctionId\":\"5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58\",\"transactionId\":\"69e69abf-a3ea-484d-a81c-d48dd0d5eaa3\",\"native\":{\"request\":{\"ver\":\"1.1\",\"context\":2,\"contextsubtype\":21,\"plcmttype\":4,\"plcmtcnt\":4,\"assets\":[{\"required\":0,\"id\":3,\"img\":{\"type\":3}},{\"required\":0,\"id\":0,\"title\":{\"len\":140}},{\"required\":0,\"id\":5,\"data\":{\"type\":1}}]},\"ver\":\"1.1\",\"battr\":[1,3,8,11,17]},\"instl\":0,\"bidfloor\":0.1,\"secure\":\"1\"}],\"site\":{\"domain\":\"test.com\",\"page\":\"https://feudfun.com/test22/revcontent_example.php\",\"cat\":[\"IAB17\"],\"publisher\":{\"id\":673,\"domain\":\"test.com\"}},\"device\":{\"ua\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:71.0) Gecko/20100101 Firefox/71.0\",\"language\":\"en\"},\"user\":{\"id\":1},\"at\":2,\"bcat\":[\"IAB24\",\"IAB25\",\"IAB25-1\",\"IAB25-2\",\"IAB25-3\",\"IAB25-4\",\"IAB25-5\",\"IAB25-6\",\"IAB25-7\",\"IAB26\",\"IAB26-1\",\"IAB26-2\",\"IAB26-3\",\"IAB26-4\"]}", + bid: [ + { + bidder: "revcontent", + params: { + size: { + width: 300, + height: 250 + }, + apiKey: "8a33fa9cf220ae685dcc3544f847cdda858d3b1c", + userId: 673, + domain: "test.com", + endpoint: "trends-s0.revcontent.com" + }, + crumbs: { + pubcid: "7a0b4adc-c109-49f0-aadc-4a4b62ebe269" + }, + nativeParams: { + image: { + required: false + }, + "title": { + required: false, + len: 140 + }, + clickUrl: { + required: false + }, + sponsoredBy: { + id: 5, + name: "data", + type: 1 + } + }, + mediaTypes: { + native: { + image: { + required: false + }, + title: { + required: false, + len: 140 + }, + clickUrl: { + required: false + }, + sponsoredBy: { + id: 5, + name: "data", + type: 1 + } + } + }, + adUnitCode: "/19968336/header-bid-tag-1", + transactionId: "69e69abf-a3ea-484d-a81c-d48dd0d5eaa3", + sizes: [], + bidId: "294a7f446202848", + bidderRequestId: "14e4dab7b5396e8", + auctionId: "5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58", + src: "client", + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + } + ] + }; + + it('should set correct native params', function () { + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template 728x90', function () { + bidRequest.bid[0].params.size.width = 728; + bidRequest.bid[0].params.size.height = 90; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template 300x600', function () { + bidRequest.bid[0].params.size.width = 300; + bidRequest.bid[0].params.size.height = 600; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template custom template', function () { + + bidRequest.bid[0].params.template = '

{title}

SEE MORE
'; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template custom invalid template', function () { + bidRequest.bid[0].params.size.width = 100; + bidRequest.bid[0].params.size.height = 200; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('should return empty when there is no bids in response', function () { + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{bid: []}], + cur: 'USD' + } + }; + let bidRequest = { + data: {}, + bids: [{bidId: 'bidId1'}] + }; + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.ok(!result); + }); + }); + + describe('onBidWon', function () { + it('default bid won', function () { + const bid = { + nurl: 'https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D', + cpm: '0.1' + }; + const result = spec.onBidWon(bid); + assert.ok(result); + }); + }); +}); \ No newline at end of file