diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js
new file mode 100644
index 000000000000..a7a99ad3056b
--- /dev/null
+++ b/modules/ixBidAdapter.js
@@ -0,0 +1,255 @@
+import * as utils from 'src/utils';
+import { BANNER } from 'src/mediaTypes';
+import { config } from 'src/config';
+import isArray from 'core-js/library/fn/array/is-array';
+import isInteger from 'core-js/library/fn/number/is-integer';
+import { registerBidder } from 'src/adapters/bidderFactory';
+
+const BIDDER_CODE = 'ix';
+const BANNER_SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus';
+const BANNER_INSECURE_BID_URL = 'http://as.casalemedia.com/cygnus';
+const SUPPORTED_AD_TYPES = [BANNER];
+const ENDPOINT_VERSION = 7.2;
+const CENT_TO_DOLLAR_FACTOR = 100;
+const TIME_TO_LIVE = 60;
+const NET_REVENUE = true;
+const isSecureWeb = utils.getTopWindowLocation().protocol === 'https:';
+const baseUrl = isSecureWeb ? BANNER_SECURE_BID_URL : BANNER_INSECURE_BID_URL;
+const PRICE_TO_DOLLAR_FACTOR = {
+ JPY: 1
+};
+
+/**
+ * Transform valid bid request config object to impression object that will be sent to ad server.
+ *
+ * @param {object} bid A valid bid request config object.
+ * @return {object} A impression object that will be sent to ad server.
+ */
+function bidToBannerImp(bid) {
+ const imp = {};
+
+ imp.id = bid.bidId;
+
+ imp.banner = {};
+ imp.banner.w = bid.params.size[0];
+ imp.banner.h = bid.params.size[1];
+ imp.banner.topframe = utils.inIframe() ? 0 : 1;
+
+ imp.ext = {};
+ imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`;
+ imp.ext.siteID = bid.params.siteId;
+
+ if (bid.params.hasOwnProperty('bidFloor') && bid.params.hasOwnProperty('bidFloorCur')) {
+ imp.bidfloor = bid.params.bidFloor;
+ imp.bidfloorcur = bid.params.bidFloorCur;
+ }
+
+ return imp;
+}
+
+/**
+ * Parses a raw bid for the relevant information.
+ *
+ * @param {object} rawBid The bid to be parsed.
+ * @param {string} currency Global currency in bid response.
+ * @return {object} bid The parsed bid.
+ */
+function parseBid(rawBid, currency) {
+ const bid = {};
+
+ if (PRICE_TO_DOLLAR_FACTOR.hasOwnProperty(currency)) {
+ bid.cpm = rawBid.price / PRICE_TO_DOLLAR_FACTOR[currency];
+ } else {
+ bid.cpm = rawBid.price / CENT_TO_DOLLAR_FACTOR;
+ }
+
+ bid.requestId = rawBid.impid;
+ bid.width = rawBid.w;
+ bid.height = rawBid.h;
+ bid.ad = rawBid.adm;
+ bid.dealId = utils.deepAccess(rawBid, 'ext.dealid');
+ bid.ttl = TIME_TO_LIVE;
+ bid.netRevenue = NET_REVENUE;
+ bid.currency = currency;
+ bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-';
+
+ return bid;
+}
+
+/**
+ * Determines whether or not the given object is valid size format.
+ *
+ * @param {*} size The object to de validated.
+ * @return {boolean} True if this is a valid size format, and false otherwise.
+ */
+function isValidSize(size) {
+ return isArray(size) && size.length === 2 && isInteger(size[0]) && isInteger(size[1]);
+}
+
+/**
+ * Determines whether or not the given size object is an element of the size array.
+ *
+ * @param {array} sizeArray The size array.
+ * @param {object} size The size object.
+ * @return {boolean} True if the size object is an element of the size array, and false otherwise.
+ */
+function includesSize(sizeArray, size) {
+ if (isValidSize(sizeArray)) {
+ return sizeArray[0] === size[0] && sizeArray[1] === size[1];
+ }
+
+ for (let i = 0; i < sizeArray.length; i++) {
+ if (sizeArray[i][0] === size[0] && sizeArray[i][1] === size[1]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Determines whether or not the given bidFloor parameters are valid.
+ *
+ * @param {*} bidFloor The bidFloor parameter inside bid request config.
+ * @param {*} bidFloorCur The bidFloorCur parameter inside bid request config.
+ * @return {boolean} True if this is a valid biFfloor parameters format, and false otherwise.
+ */
+function isValidBidFloorParams(bidFloor, bidFloorCur) {
+ const curRegex = /^[A-Z]{3}$/;
+
+ return Boolean(typeof bidFloor === 'number' && typeof bidFloorCur === 'string'
+ && bidFloorCur.match(curRegex));
+}
+
+export const spec = {
+
+ code: BIDDER_CODE,
+ supportedMediaTypes: SUPPORTED_AD_TYPES,
+
+ /**
+ * Determines whether or not the given bid request is valid.
+ *
+ * @param {object} bid The bid to validate.
+ * @return {boolean} True if this is a valid bid, and false otherwise.
+ */
+ isBidRequestValid: function (bid) {
+ if (!isValidSize(bid.params.size)) {
+ return false;
+ }
+
+ if (!includesSize(bid.sizes, bid.params.size)) {
+ return false;
+ }
+
+ if (typeof bid.params.siteId !== 'string') {
+ return false;
+ }
+
+ const hasBidFloor = bid.params.hasOwnProperty('bidFloor');
+ const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur');
+
+ if (hasBidFloor || hasBidFloorCur) {
+ return hasBidFloor && hasBidFloorCur
+ && isValidBidFloorParams(bid.params.bidFloor, bid.params.bidFloorCur);
+ }
+
+ return true;
+ },
+
+ /**
+ * Make a server request from the list of BidRequests.
+ *
+ * @param {array} validBidRequests A list of valid bid request config objects.
+ * @return {object} Info describing the request to the server.
+ */
+ buildRequests: function (validBidRequests) {
+ const bannerImps = [];
+ let validBidRequest = null;
+ let bannerImp = null;
+
+ for (let i = 0; i < validBidRequests.length; i++) {
+ validBidRequest = validBidRequests[i];
+
+ // If the bid request is for banner, then transform the bid request based on banner format
+ if (utils.deepAccess(validBidRequest, 'mediaTypes.banner')
+ || validBidRequest.mediaType === 'banner') {
+ bannerImp = bidToBannerImp(validBidRequest);
+ bannerImps.push(bannerImp);
+ }
+ }
+
+ // Since bidderRequestId are the same for diffrent bid request, just use the first one
+ const r = {};
+ r.id = validBidRequests[0].bidderRequestId;
+ r.imp = bannerImps;
+ r.site = {};
+ r.site.page = utils.getTopWindowUrl();
+ r.site.ref = utils.getTopWindowReferrer();
+ r.ext = {};
+ r.ext.source = 'prebid';
+
+ // Append firstPartyData to r.site.page if firstPartyData exists
+ const otherIxConfig = config.getConfig('ix');
+
+ if (otherIxConfig && otherIxConfig.firstPartyData) {
+ const firstPartyData = otherIxConfig.firstPartyData;
+ let firstPartyString = '?';
+ for (const key in firstPartyData) {
+ if (firstPartyData.hasOwnProperty(key)) {
+ firstPartyString += `${encodeURIComponent(key)}=${encodeURIComponent(firstPartyData[key])}&`;
+ }
+ }
+ firstPartyString = firstPartyString.slice(0, -1);
+
+ r.site.page += firstPartyString;
+ }
+
+ // Use the siteId in the first bid request as the main siteId
+ const payload = {};
+ payload.s = validBidRequests[0].params.siteId;
+ payload.v = ENDPOINT_VERSION;
+ payload.r = JSON.stringify(r);
+ payload.ac = 'j';
+ payload.sd = 1;
+
+ return {
+ method: 'GET',
+ url: baseUrl,
+ data: payload
+ };
+ },
+
+ /**
+ * Unpack the response from the server into a list of bids.
+ *
+ * @param {object} serverResponse A successful response from the server.
+ * @return {array} An array of bids which were nested inside the server.
+ */
+ interpretResponse: function (serverResponse) {
+ const bids = [];
+ let bid = null;
+
+ if (!serverResponse.hasOwnProperty('body') || !serverResponse.body.hasOwnProperty('seatbid')) {
+ return bids;
+ }
+
+ const responseBody = serverResponse.body;
+ const seatbid = responseBody.seatbid;
+ for (let i = 0; i < seatbid.length; i++) {
+ if (!seatbid[i].hasOwnProperty('bid')) {
+ continue;
+ }
+
+ // Transform rawBid in bid response to the format that will be accepted by prebid
+ const innerBids = seatbid[i].bid;
+ for (let j = 0; j < innerBids.length; j++) {
+ bid = parseBid(innerBids[j], responseBody.cur);
+ bids.push(bid);
+ }
+ }
+
+ return bids;
+ }
+};
+
+registerBidder(spec);
diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md
new file mode 100644
index 000000000000..c8322d99412e
--- /dev/null
+++ b/modules/ixBidAdapter.md
@@ -0,0 +1,253 @@
+Overview
+========
+
+```
+Module Name: Index Exchange Adapter
+Module Type: Bidder Adapter
+Maintainer: prebid.support@indexexchange.com
+```
+
+Description
+===========
+
+This module connects publishers to Index Exchange's (IX) network of demand
+sources through Prebid.js.
+
+It is compatible with both the older ad unit format where the `sizes` and
+`mediaType` properties are placed at the top-level of the ad unit, and the newer
+format where this information is encapsulated within the `mediaTypes` object. We
+recommend that you use the newer format when possible as it will be better able
+to accommodate new feature additions.
+
+If a mix of properties from both formats are present within an ad unit, the
+newer format's properties will take precedence.
+
+Here are examples of both formats.
+
+##### Older Format
+```javascript
+var adUnits = [{
+ // ...
+
+ mediaType: 'banner',
+
+ sizes: [
+ [300, 250],
+ [300, 600]
+ ]
+
+ // ...
+}];
+```
+
+##### Newer Format
+```javascript
+var adUnits = [{
+ // ...
+
+ mediaTypes: {
+ banner: {
+ sizes: [
+ [300, 250],
+ [300, 600]
+ ]
+ }
+ }
+
+ // ...
+}];
+```
+
+### Supported Media Types
+
+| Type | Support
+| --- | ---
+| Banner | Fully supported for all IX approved sizes.
+| Video | Only in-stream supported.
+| Native | Not supported.
+
+# Bid Parameters
+
+Each of the IX-specific parameters provided under the `adUnits[].bids[].params`
+object are detailed here.
+
+### Banner
+
+| Key | Scope | Type | Description
+| --- | --- | --- | ---
+| siteId | Required | String |
An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have.
Examples:
+| size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.banner.sizes`.
Examples:
- `[300, 250]`
- `[300, 600]`
- `[728, 90]`
+| bidFloor | Optional1 | Number | The minimum bid required to participate in an auction for this ad unit. Assuming the bid floor currency that is set has a main unit (e.g. dollars, pounds) and a sub-unit (e.g. cents, pence), the bid floor should be in decimal-point format. If the currency only has main a unit (e.g. JPY), then the bid floor should be a whole number.
Examples:
- 10.26 USD => `bidFloor: 10.26`
- 13.41 GBP => `bidFloor: 13.41`
- 600 JPY => `bidFloor: 600`
| N/A
+| bidFloorCur | Optional1 | String | The currency of the bid floor.
Examples:
+
+
+ 1 bidFloor
and bidFloorCur
must
+ both be set when a bid floor is being configured.
+
+
+Setup Guide
+===========
+
+Follow these steps to configure and add the IX module to your Prebid.js
+integration.
+
+The examples in this guide assume the following starting configuration:
+
+```javascript
+var adUnits = [{
+ code: 'banner-div-a',
+ mediaTypes: {
+ banner: {
+ sizes: [
+ [300, 250],
+ [300, 600]
+ ]
+ }
+ },
+ bids: []
+}];
+```
+
+##### 1. Add IX to the appropriate ad units
+
+For each size in an ad unit that IX will be bidding on, add one of the following
+bid objects under `adUnits[].bids`:
+
+```javascript
+{
+ bidder: 'ix',
+ params: {
+ siteId: '',
+ size: []
+ }
+}
+```
+
+Set `params.siteId` and `params.size` in each bid object to the values provided
+by your IX representative.
+
+**Example**
+```javascript
+var adUnits = [{
+ code: 'banner-div-a',
+ mediaTypes: {
+ banner: {
+ sizes: [
+ [300, 250],
+ [300, 600]
+ ]
+ }
+ },
+ bids: [{
+ bidder: 'ix',
+ params: {
+ siteId: '4622',
+ size: [300, 250]
+ }
+ }, {
+ bidder: 'ix',
+ params: {
+ siteId: '6242',
+ size: [300, 600]
+ }
+ }]
+}];
+```
+
+##### 2. Include `ixBidAdapter` in your build process
+
+When running the build command, include `ixBidAdapter` as a module.
+
+```
+gulp build --modules=ixBidAdapter,fooBidAdapter,bazBidAdapter
+```
+
+If a JSON file is being used to specify the bidder modules, add `"ixBidAdapter"`
+to the top-level array in that file.
+
+```json
+[
+ "ixBidAdapter",
+ "fooBidAdapter",
+ "bazBidAdapter"
+]
+```
+
+And then build.
+
+```
+gulp build --modules=bidderModules.json
+```
+
+Setting First Party Data (FPD)
+==============================
+
+FPD allows you to specify key-value pairs which will be passed as part of the
+query string to IX for use in Private Marketplace Deals which rely on query
+string targeting for activation. For example, if a user is viewing a
+news-related page, you can pass on that information by sending `category=news`.
+Then in the IX Private Marketplace setup screens you can create Deals which
+activate only on pages which contain `category=news`. Please reach out to your
+IX representative if you have any questions or need help setting this up.
+
+To include FPD in a bid request, it must be set before `pbjs.requestBids` is
+called. To set it, call `pbjs.setConfig` and provide it with a map of FPD keys
+to values as such:
+
+```javascript
+pbjs.setConfig({
+ ix: {
+ firstPartyData: {
+ '': '',
+ '': '',
+ // ...
+ }
+ }
+});
+```
+
+The values can be updated at any time by calling `pbjs.setConfig` again. The
+changes will be reflected in any proceeding bid requests.
+
+Additional Information
+======================
+
+### Bid Request Limit
+
+If a single bid request to IX contains more than 20 impression requests (i.e.
+more than 20 objects in `bidRequest.imp`), only the first 20 will be accepted,
+the rest will be ignored.
+
+To avoid this situation, ensure that when `pbjs.requestBid` is invoked, that the
+number of bid objects (i.e. `adUnits[].bids`) with `adUnits[].bids[].bidder` set
+to `'ix'` across all ad units that bids are being requested for does not exceed
+20.
+
+### Time-To-Live (TTL)
+
+All bids received from IX have a TTL of 60 seconds, after which time they become
+invalid.
+
+If an invalid bid wins, and its associated ad is rendered, it will not count
+towards total impressions on IX's side.
+
+FAQs
+====
+
+### Why do I have to input size in `adUnits[].bids[].params` for IX when the size is already in the ad unit?
+
+There are two important reasons why we require it:
+
+1. An IX site ID maps to a single size, whereas an ad unit can have multiple
+sizes. To ensure that the right site ID is mapped to the correct size in the ad
+unit we require the size to be explicitly stated.
+
+2. An ad unit may have sizes that IX does not support. By explicitly stating the
+size, you can choose not to have IX bid on certain sizes that are invalid.
+
+### How do I view IX's bid request in the network?
+
+In your browser of choice, create a new tab and open the developer tools. In
+developer tools, select the network tab. Then, navigate to a page where IX is
+setup to bid. Now, in the network tab, search for requests to
+`casalemedia.com/cygnus`. These are the bid requests.
diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js
new file mode 100644
index 000000000000..081e4d4fdad2
--- /dev/null
+++ b/test/spec/modules/ixBidAdapter_spec.js
@@ -0,0 +1,316 @@
+import * as utils from 'src/utils';
+import { config } from 'src/config';
+import { expect } from 'chai';
+import { newBidder } from 'src/adapters/bidderFactory';
+import { spec } from 'modules/ixBidAdapter';
+
+describe('IndexexchangeAdapter', () => {
+ const IX_ENDPOINT = 'http://as.casalemedia.com/cygnus';
+ const BIDDER_VERSION = 7.2;
+
+ const DEFAULT_BANNER_VALID_BID = [
+ {
+ bidder: 'ix',
+ params: {
+ siteId: '123',
+ size: [300, 250]
+ },
+ sizes: [[300, 250], [300, 600]],
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250], [300, 600]]
+ }
+ },
+ adUnitCode: 'div-gpt-ad-1460505748561-0',
+ transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229',
+ bidId: '1a2b3c4d',
+ bidderRequestId: '11a22b33c44d',
+ auctionId: '1aa2bb3cc4dd'
+ }
+ ];
+ const DEFAULT_BANNER_BID_RESPONSE = {
+ cur: 'USD',
+ id: '11a22b33c44d',
+ seatbid: [
+ {
+ bid: [
+ {
+ crid: '12345',
+ adomain: ['www.abc.com'],
+ adid: '14851455',
+ impid: '1a2b3c4d',
+ cid: '3051266',
+ price: 100,
+ w: 300,
+ h: 250,
+ id: '1',
+ ext: {
+ dspid: 50,
+ pricelevel: '_100',
+ advbrandid: 303325,
+ advbrand: 'OECTA'
+ },
+ adm: ''
+ }
+ ],
+ seat: '3970'
+ }
+ ]
+ };
+
+ describe('inherited functions', () => {
+ it('should exists and is a function', () => {
+ const adapter = newBidder(spec);
+ expect(adapter.callBids).to.exist.and.to.be.a('function');
+ });
+ });
+
+ describe('isBidRequestValid', () => {
+ it('should return true when required params found for a banner ad', () => {
+ expect(spec.isBidRequestValid(DEFAULT_BANNER_VALID_BID[0])).to.equal(true);
+ });
+
+ it('should return true when optional params found for a banner ad', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.bidFloor = 50;
+ bid.params.bidFloorCur = 'USD';
+ expect(spec.isBidRequestValid(bid)).to.equal(true);
+ });
+
+ it('should return false when siteID is number', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.siteId = 123;
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when siteID is missing', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ delete bid.params.siteId;
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when size is missing', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ delete bid.params.size;
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when size array is wrong length', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.size = [
+ 300,
+ 250,
+ 250
+ ];
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when size array is array of strings', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.size = ['300', '250'];
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when there is only bidFloor', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.bidFloor = 50;
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when there is only bidFloorCur', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.bidFloorCur = 'USD';
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when bidFloor is string', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.bidFloor = '50';
+ bid.params.bidFloorCur = 'USD';
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+
+ it('should return false when bidFloorCur is number', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.bidFloor = 50;
+ bid.params.bidFloorCur = 70;
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+ });
+
+ describe('buildRequestsBanner', () => {
+ const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID);
+ const requestUrl = request.url;
+ const requestMethod = request.method;
+ const query = request.data;
+
+ it('request should be made to IX endpoint with GET method', () => {
+ expect(requestMethod).to.equal('GET');
+ expect(requestUrl).to.equal(IX_ENDPOINT);
+ });
+
+ it('query object (version, siteID and request) should be correct', () => {
+ expect(query.v).to.equal(BIDDER_VERSION);
+ expect(query.s).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId);
+ expect(query.r).to.exist;
+ expect(query.ac).to.equal('j');
+ expect(query.sd).to.equal(1);
+ });
+
+ it('payload should have correct format and value', () => {
+ const payload = JSON.parse(query.r);
+
+ expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId);
+ expect(payload.site).to.exist;
+ expect(payload.site.page).to.exist;
+ expect(payload.site.page).to.contain('http');
+ expect(payload.site.ref).to.exist;
+ expect(payload.site.ref).to.be.a('string');
+ expect(payload.ext).to.exist;
+ expect(payload.ext.source).to.equal('prebid');
+ expect(payload.imp).to.exist;
+ expect(payload.imp).to.be.an('array');
+ expect(payload.imp).to.have.lengthOf(1);
+ });
+
+ it('impression should have correct format and value', () => {
+ const impression = JSON.parse(query.r).imp[0];
+ const sidValue = `${DEFAULT_BANNER_VALID_BID[0].params.size[0].toString()}x${DEFAULT_BANNER_VALID_BID[0].params.size[1].toString()}`;
+
+ expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId);
+ expect(impression.banner).to.exist;
+ expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]);
+ expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]);
+ expect(impression.banner.topframe).to.exist;
+ expect(impression.banner.topframe).to.be.oneOf([0, 1]);
+ expect(impression.ext).to.exist;
+ expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString());
+ expect(impression.ext.sid).to.equal(sidValue);
+ });
+
+ it('impression should have bidFloor and bidFloorCur if configured', () => {
+ const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
+ bid.params.bidFloor = 50;
+ bid.params.bidFloorCur = 'USD';
+ const requestBidFloor = spec.buildRequests([bid]);
+ const impression = JSON.parse(requestBidFloor.data.r).imp[0];
+
+ expect(impression.bidfloor).to.equal(bid.params.bidFloor);
+ expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur);
+ });
+
+ it('should add first party data to page url in bid request if it exists in config', () => {
+ config.setConfig({
+ ix: {
+ firstPartyData: {
+ ab: 123,
+ cd: '123#ab',
+ 'e/f': 456,
+ 'h?g': '456#cd'
+ }
+ }
+ });
+
+ const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID);
+ const pageUrl = JSON.parse(requestWithFirstPartyData.data.r).site.page;
+ const expectedPageUrl = `${utils.getTopWindowUrl()}?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd`;
+
+ expect(pageUrl).to.equal(expectedPageUrl);
+ });
+
+ it('should not add first party data to page url in bid request if it is not present', () => {
+ config.setConfig({
+ ix: {}
+ });
+
+ const requestWithOutFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID);
+ const pageUrl = JSON.parse(requestWithOutFirstPartyData.data.r).site.page;
+
+ expect(pageUrl).to.equal(utils.getTopWindowUrl());
+ });
+ });
+
+ describe('interpretResponseBanner', () => {
+ it('should get correct bid response', () => {
+ const expectedParse = [
+ {
+ requestId: '1a2b3c4d',
+ cpm: 1,
+ creativeId: '12345',
+ width: 300,
+ height: 250,
+ ad: '',
+ currency: 'USD',
+ ttl: 60,
+ netRevenue: true,
+ dealId: undefined
+ }
+ ];
+ const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE });
+ expect(result[0]).to.deep.equal(expectedParse[0]);
+ });
+
+ it('should set creativeId to default value if not provided', () => {
+ const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE);
+ delete bidResponse.seatbid[0].bid[0].crid;
+ const expectedParse = [
+ {
+ requestId: '1a2b3c4d',
+ cpm: 1,
+ creativeId: '-',
+ width: 300,
+ height: 250,
+ ad: '',
+ currency: 'USD',
+ ttl: 60,
+ netRevenue: true,
+ dealId: undefined
+ }
+ ];
+ const result = spec.interpretResponse({ body: bidResponse });
+ expect(result[0]).to.deep.equal(expectedParse[0]);
+ });
+
+ it('should set Japanese price correctly', () => {
+ const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE);
+ bidResponse.cur = 'JPY';
+ const expectedParse = [
+ {
+ requestId: '1a2b3c4d',
+ cpm: 100,
+ creativeId: '12345',
+ width: 300,
+ height: 250,
+ ad: '',
+ currency: 'JPY',
+ ttl: 60,
+ netRevenue: true,
+ dealId: undefined
+ }
+ ];
+ const result = spec.interpretResponse({ body: bidResponse });
+ expect(result[0]).to.deep.equal(expectedParse[0]);
+ });
+
+ it('should set dealId correctly', () => {
+ const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE);
+ bidResponse.seatbid[0].bid[0].ext.dealid = 'deal';
+ const expectedParse = [
+ {
+ requestId: '1a2b3c4d',
+ cpm: 1,
+ creativeId: '12345',
+ width: 300,
+ height: 250,
+ ad: '',
+ currency: 'USD',
+ ttl: 60,
+ netRevenue: true,
+ dealId: 'deal'
+ }
+ ];
+ const result = spec.interpretResponse({ body: bidResponse });
+ expect(result[0]).to.deep.equal(expectedParse[0]);
+ });
+ });
+});