diff --git a/modules/smartyadsBidAdapter.js b/modules/smartyadsBidAdapter.js new file mode 100644 index 00000000000..0c553b567ef --- /dev/null +++ b/modules/smartyadsBidAdapter.js @@ -0,0 +1,91 @@ +import {registerBidder} from 'src/adapters/bidderFactory'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes'; +import * as utils from 'src/utils'; + +const BIDDER_CODE = 'smartyads'; +const URL = '//ssp-nj.webtradehub.com/?c=o&m=multi'; +const URL_SYNC = '//ssp-nj.webtradehub.com/?c=o&m=cookie'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || + !bid.ttl || !bid.currency) { + return false; + } + switch (bid['mediaType']) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl); + case NATIVE: + return Boolean(bid.title && bid.image && bid.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(bid.params.placementId)); + }, + + buildRequests: (validBidRequests = []) => { + let winTop = window; + try { + window.top.location.toString(); + winTop = window.top; + } catch (e) { + utils.logMessage(e); + } + let location = utils.getTopWindowLocation(); + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language : '', + 'secure': location.protocol === 'https:' ? 1 : 0, + 'host': location.host, + 'page': location.pathname, + 'placements': placements + }; + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + let bid = validBidRequests[i]; + placements.push({ + placementId: bid.params.placementId, + bidId: bid.bidId, + traffic: bid.params.traffic || BANNER + }); + } + return { + method: 'POST', + url: URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + serverResponse = serverResponse.body; + for (let i = 0; i < serverResponse.length; i++) { + let resItem = serverResponse[i]; + if (isBidResponseValid(resItem)) { + delete resItem.mediaType; + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses) => { + return [{ + type: 'image', + url: URL_SYNC + }]; + } + +}; + +registerBidder(spec); diff --git a/modules/smartyadsBidAdapter.md b/modules/smartyadsBidAdapter.md new file mode 100644 index 00000000000..5102a6fd128 --- /dev/null +++ b/modules/smartyadsBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +``` +Module Name: SmartyAds Bidder Adapter +Module Type: Bidder Adapter +Maintainer: supply@smartyads.com +``` + +# Description + +Module that connects to SmartyAds' demand sources + +# Test Parameters +``` + var adUnits = [{ + code: 'placementId_0', + sizes: [[300, 250]], + bids: [{ + bidder: 'smartyads', + params: { + placementId: 0, + traffic: 'banner' + } + }] + } + ]; +``` \ No newline at end of file diff --git a/package.json b/package.json index fdc8e5b7ea8..5fb9e66c3a1 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "eslint-plugin-standard": "^3.0.1", "faker": "^3.1.0", "fs.extra": "^1.3.2", - "gulp": "^3.8.7", + "gulp": "^3.9.1", "gulp-babel": "^6.1.2", "gulp-clean": "^0.3.2", "gulp-concat": "^2.6.0", diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js new file mode 100644 index 00000000000..858e8bf37a0 --- /dev/null +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -0,0 +1,230 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/smartyadsBidAdapter'; + +describe('SmartyadsAdapter', () => { + let bid = { + bidId: '23fhj33i987f', + bidder: 'smartyads', + params: { + placementId: 0, + traffic: 'banner' + } + }; + + describe('isBidRequestValid', () => { + it('Should return true if there are bidId, params and placementId parameters present', () => { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false if at least one of parameters is not present', () => { + delete bid.params.placementId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', () => { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', () => { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', () => { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', () => { + expect(serverRequest.url).to.equal('//ssp-nj.webtradehub.com/?c=o&m=multi'); + }); + it('Returns valid data if array of bids is valid', () => { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'traffic'); + expect(placement.placementId).to.equal(0); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal('banner'); + }); + it('Returns empty data if no valid requests are passed', () => { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', () => { + it('Should interpret banner response', () => { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.width).to.equal(300); + expect(dataItem.height).to.equal(250); + expect(dataItem.ad).to.equal('Test'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret video response', () => { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId'); + expect(dataItem.mediaType).to.not.exist; + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret native response', () => { + const native = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'clickUrl', 'impressionTrackers', 'title', 'image', 'ttl', 'creativeId', 'netRevenue', 'currency'); + expect(dataItem.mediaType).to.not.exist; + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.clickUrl).to.equal('test.com'); + expect(dataItem.title).to.equal('Test'); + expect(dataItem.image).to.equal('test.com'); + expect(dataItem.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should return an empty array if invalid banner response is passed', () => { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', () => { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', () => { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', () => { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + describe('getUserSyncs', () => { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and type', () => { + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.exist; + expect(userSync[0].url).to.exist; + expect(userSync[0].type).to.be.equal('image'); + expect(userSync[0].url).to.be.equal('//ssp-nj.webtradehub.com/?c=o&m=cookie'); + }); + }); +});