diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 5d42055cabb..00000000000 --- a/.babelrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "presets": [ - ["env", { - "targets": { - "browsers": [ - "chrome >= 61", - "safari >=8", - "edge >= 14", - "ff >= 57", - "ie >= 10", - "ios >= 8" - ] - } - }] - ], - "plugins": [ - "transform-object-assign" - ] -} diff --git a/.babelrc.js b/.babelrc.js new file mode 100644 index 00000000000..bece57ec4a5 --- /dev/null +++ b/.babelrc.js @@ -0,0 +1,34 @@ + +let path = require('path'); + +function useLocal(module) { + return require.resolve(module, { + paths: [ + __dirname + ] + }) +} + +module.exports = { + "presets": [ + [ + useLocal('@babel/preset-env'), + { + "targets": { + "browsers": [ + "chrome >= 61", + "safari >=8", + "edge >= 14", + "ff >= 57", + "ie >= 10", + "ios >= 8" + ] + } + } + ] + ], + "plugins": [ + path.resolve(__dirname, './plugins/pbjsGlobals.js'), + useLocal('babel-plugin-transform-object-assign') + ] +}; diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000000..ca6713bb587 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,50 @@ +# Javascript Node CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-javascript/ for more details +# +version: 2 +jobs: + build: + docker: + # specify the version you desire here + - image: circleci/node:8.9.0 + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # - image: circleci/mongo:3.4.4 + + working_directory: ~/Prebid.js + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-{{ checksum "package.json" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: npm install + + - save_cache: + paths: + - node_modules + key: v1-dependencies-{{ checksum "package.json" }} + + - run: sudo npm install -g gulp-cli + # Download and run BrowserStack local + - run: + name : Download BrowserStack Local binary and start it. + command : | + # Download the browserstack binary file + wget "https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip" + # Unzip it + unzip BrowserStackLocal-linux-x64.zip + # Run the file with user's access key + ./BrowserStackLocal ${BROWSERSTACK_ACCESS_KEY} & + # run tests! + - run: + name: BrowserStack testing + command: gulp test --browserstack --nolintfix diff --git a/.eslintrc.js b/.eslintrc.js index e6975951f06..56e4808f985 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,6 @@ + +const allowedModules = require("./allowedModules"); + module.exports = { "env": { "browser": true, @@ -11,6 +14,9 @@ module.exports = { } }, "extends": "standard", + "plugins": [ + "prebid" + ], "globals": { "$$PREBID_GLOBAL$$": false }, @@ -26,11 +32,16 @@ module.exports = { // Violations of these styles should be fixed, and the exceptions removed over time. // // See Issue #1111. - "camelcase": "off", "eqeqeq": "off", "no-return-assign": "off", "no-throw-literal": "off", "no-undef": "off", "no-useless-escape": "off", - } + }, + "overrides": Object.keys(allowedModules).map((key) => ({ + "files": key + "/**/*.js", + "rules": { + "prebid/validate-imports": ["error", allowedModules[key]] + } + })) }; diff --git a/.github/stale.yml b/.github/stale.yml index 0925c69c703..2afa7ecf0ec 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -8,6 +8,7 @@ exemptLabels: - security - bug - feature + - on hold # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable diff --git a/.gitignore b/.gitignore index 88e849a35ad..c0452b7b3d0 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ build/coverage/ .idea/ # if you remove the above rule, at least ignore the following: +# VS Code +.vscode/ + # User-specific stuff: # .idea/workspace.xml # .idea/tasks.xml diff --git a/.nvmrc b/.nvmrc index 4fedf1d20e1..fa97ecedc28 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -7.0 +8.9 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 912f3de8b56..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -sudo: required -dist: trusty -language: node_js -node_js: -- '7.0' -env: -- BROWSERSTACK_USERNAME=info184 -addons: - chrome: stable, - browserstack: - username: info184 - access_key: - secure: Ru286R4pMcEIRKwb2AoaaJY6lEKIzeZraxY7CtbOP4ykNk7uqsnyitk4QwxpCCh0n35b71m30okW6ZmZnl0lJXhOMdYoSOYBAnUw2Vn7Y93oMSKIC5dc2/qmtF1t2b1qX65/Ont2iJUj+UY8VQw5Hk2vIT4/5wifYPBnV5ILK4AI7SVk/ma7OzK4rkp3WThlouddctAd7tx4O3YIyJKDi9lkfcMA0pnH4OZSOlDClRLIy50Q1NE+iyqHtWFZK1TwJ+IhQbSsCLbuyQJBRnyJJEftNmtrs5MCZt/9pwFDj7c8+o11F6HCsTBYFkehFRfbKnmhCc1G+bsNXY8OxIWwEHeuVmSGK7TDPYcPPQBc03mcQ1fY/IPNQOdsVJ/n8RsG2u0IU2CF2hhkuNFzeov7dOHljanc45NKOrLdjwzP1aZCAUvLquOTzvmdF23nJhMs8UO+Du4kTK5VfmKyz1MC91E40a0Q15+O4qmS39rNOlwhxPJSfuxxL1jKVPJ7PsFbTkGM8M/XPJ6dyGLufy225HjdLdJTAOa5BZ4st+nXH/AzqHzy6a2I5vTmAz1j4gHLgVU+iNxAkX8znb25s3Rs1ZuFVj+aBSBmNoQA1FA5f/uXWeruTdDig7ksp+XdjsjLm9Md8cWwYaEn04FYj1ztJrylrEMfnc0Kcs6zQ3fll1g= -before_install: -- npm install -g gulp -- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost & -script: |- - if [[ ${TRAVIS_PULL_REQUEST} == "false" ]]; then - gulp test --browserstack - else - gulp run-tests - fi -notifications: - slack: - on_success: never - on_failure: always - on_pull_requests: false - secure: C4O77VtABLE5DiPDeKGqUcsBdTBMNjQRLc8iBfSp231e95K1rA/JXJJEQN/lVhhiFJyPhxueE0i6cR0zD8uAMC8HRShGGfPjEZ7f6glawPzap2wFwjAyVkknYV+BMKcX0jvn7CiSKBj+zTbHQfn/Uj3nMSbDZQIdbNDiFGh4NuDr3/Yd/efhsw/miExlSPSWqGVCKV3WPpTrU3BRpLNDq4sZMXP9ORZxGK7ER3tsMiD2z05YpvC+mibESJxaY0qsuQu1y1Gu65QLPe5ocw405btJwqYn+b4YFpUd2GbLNhjtLzsc+OKrD0DWuEI0bxePQUYDga5wR6g4cdZaXU3ixDjee7sJbDeVJAuykGlfZ4A1k+fQIgPs3s9XMHaeG9AfDhFiZ/UoNdonzos1iSa/Y1TzHIXp1wnbHKT5HUWWPFNb5PzJxHEtHbm3jwOH4iK8VAq94ec16M2aqUAj7muiqcrTlYa5rs6jRlXo/TRymFnbQRdBT7eLmLNDQD35yR1Y+4mxHqKi+3189yG9RE+uwIlB+9HZFgNbioOApB+jarKC6M0qEgn0bHxkpJBP8JmNCA84U0ZUzyPvuMGsRbisAmKoUsU8C6cq59QDfBTcCTvKXK6r+6f23iRGieoGSbTxYQj46QkykpbWU0WstQDQsZL3L316uZecOVZmWKBRxPs= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc8d80ec384..9c00a2bf51a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ commit your changes, and [open a pull request](https://help.github.com/articles/ master branch. Pull requests must have 80% code coverage before beign considered for merge. -Additional details about the process can be found [here](./pr_review.md). +Additional details about the process can be found [here](./PR_REVIEW.md). ## Issues [prebid.org](http://prebid.org/) contains documentation that may help answer questions you have about using Prebid.js. @@ -55,7 +55,7 @@ When you are adding code to Prebid.js, or modifying code that isn't covered by a Prebid.js already has many tests. Read them to see how Prebid.js is tested, and for inspiration: - Look in `test/spec` and its subdirectories -- Tests for bidder adaptors are located in `test/spec/adapters` +- Tests for bidder adaptors are located in `test/spec/modules` A test module might have the following general structure: @@ -64,8 +64,8 @@ A test module might have the following general structure: import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' import adapter from 'src/adapters/'; -describe('', () => { - it('', () => { +describe('', function () { + it('', function () { // Arrange - set up preconditions and inputs // Act - call or act on the code under test // Assert - use chai to check that expected results have occurred diff --git a/PREBID_VERSIONING_DEPRECATION.md b/PREBID_VERSIONING_DEPRECATION.md new file mode 100644 index 00000000000..f006922259b --- /dev/null +++ b/PREBID_VERSIONING_DEPRECATION.md @@ -0,0 +1,25 @@ +# Prebid versioning and deprecation policy + +## Goals +Provide clear definitions and policy around versioning and breaking changes to APIs that are both publisher and demand partner facing. + + - Limit the number of breaking changes. + - Ensure significant time for updates for breaking changes so that publisher or demand partners do not break. + - Provide a path to deprecation and reduce technical debt and increase security. + - Major versions should not be changed more than once per 30 days. + +## Versioning + +Follow semantic versioning so that all breaking changes occur within a major release. A breaking change includes both demand partner internal APIs* and publisher facing APIs (global APIs). + +*Demand partner APIs may be excluded from breaking change policy at the core teams discretion if the changes are made so to be transparent to the bidders (such as internal refactoring). + +## Deprecation process + + - Open an issue with an "intent to implement" and "API impact" labels. + - Allow 2 weeks for discussion. + - Announce breaking change to the mailing list (TBD needs to be created). + - At least 2 core members needs to provide explicit approval for the deprecation. + - Open a PR against current master for console warning for possible breakage. + - Support the previous major version for a minimum of 30 days. + - Coordinate with the core team to ensure clean merging into feature branch if applicable (future major version branch). diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 012a2d8b501..4ad8b8ec372 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -14,10 +14,16 @@ For modules and core platform updates, the initial reviewer should request an ad - Review for obvious errors or bad coding practice / use best judgement here. - If the change is a new feature / change to core prebid.js - review the change with a Tech Lead on the project and make sure they agree with the nature of change. - If the change results in needing updates to docs (such as public API change, module interface etc), add a label for "needs docs" and inform the submitter they must submit a docs PR to update the appropriate area of Prebid.org **before the PR can merge**. Help them with finding where the docs are located on prebid.org if needed. + - Below are some examples of bidder specific updates that should require docs update (in their dev-docs/bidders/bidder.md file): + - Add support for GDPR consentManagement module > add `gdpr_supported: true` + - Add support for userId module > add `userId: pubCommon, digitrust, newProviderHere` + - Add support for video and/or native mediaTypes > add `media_types: video, native` + - Add support for COPPA > add `coppa_supported: true` - If all above is good, add a `LGTM` comment and request 1 additional core member to review. - Once there is 2 `LGTM` on the PR, merge to master - Ask the submitter to add a PR for documentation if applicable. - Add a line into the [draft release](https://github.com/prebid/Prebid.js/releases) notes for this submission. If no draft release is available, create one using [this template]( https://gist.github.com/mkendall07/c3af6f4691bed8a46738b3675cb5a479) +- Add the PR to the appropriate project board (I.E. 1.23.0 Release) for the week, [see](https://github.com/prebid/Prebid.js/projects) ### New Adapter or updates to adapter process - Follow steps above for general review process. In addition, please verify the following: @@ -39,9 +45,9 @@ For modules and core platform updates, the initial reviewer should request an ad ## Ticket Coordinator Each week, Prebid Org assigns one person to keep an eye on incoming issues and PRs. That person should: -- Review issues and PRs at least once per weekday for new items. +- Review issues and PRs at least once per weekday for new items. Encourage a 48 "SLA" on PRs/issues assigned. Aim for touchpoint once every 48/hours. - For PRs: assign PRs to individuals on the PR review list. Try to be equitable -- not all PRs are created equally. Use the "Assigned" field and add the "Needs Review" label. -- For Issues: try to address questions and troubleshooting requests on your own, assigning them to others as needed. +- For Issues: try to address questions and troubleshooting requests on your own, assigning them to others as needed. Please add labels as appropriate (I.E. bug, question, backlog etc). - Issues that are questions or troubleshooting requests may be closed if the originator doesn't respond within a week to requests for confirmation or details. - Issues that are bug reports should be left open and assigned to someone in PR rotation to confirm or deny the bug status. - It's polite to check with others before assigning them large tasks. diff --git a/README.md b/README.md index 946d6755774..be07a27ddc1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -[![Build Status](https://travis-ci.org/prebid/Prebid.js.svg?branch=master)](https://travis-ci.org/prebid/Prebid.js) +[![Build Status](https://circleci.com/gh/prebid/Prebid.js.svg?style=svg)](https://circleci.com/gh/prebid/Prebid.js) [![Percentage of issues still open](http://isitmaintained.com/badge/open/prebid/Prebid.js.svg)](http://isitmaintained.com/project/prebid/Prebid.js "Percentage of issues still open") [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/prebid/Prebid.js.svg)](http://isitmaintained.com/project/prebid/Prebid.js "Average time to resolve an issue") [![Code Climate](https://codeclimate.com/github/prebid/Prebid.js/badges/gpa.svg)](https://codeclimate.com/github/prebid/Prebid.js) [![Coverage Status](https://coveralls.io/repos/github/prebid/Prebid.js/badge.svg)](https://coveralls.io/github/prebid/Prebid.js) [![devDependencies Status](https://david-dm.org/prebid/Prebid.js/dev-status.svg)](https://david-dm.org/prebid/Prebid.js?type=dev) +[![Total Alerts](https://img.shields.io/lgtm/alerts/g/prebid/Prebid.js.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/prebid/Prebid.js/alerts/) # Prebid.js @@ -15,11 +16,94 @@ Working examples can be found in [the developer docs](http://prebid.org/dev-docs **Table of Contents** +- [Usage](#Usage) - [Install](#Install) - [Build](#Build) - [Run](#Run) - [Contribute](#Contribute) + + +## Usage (as a npm dependency) + +*Note:* Requires Prebid.js v1.38.0+ + +Prebid.js depends on Babel and some Babel Plugins in order to run correctly in the browser. Here are some examples for +configuring webpack to work with Prebid.js. + +With Babel 7: +```javascript +// webpack.conf.js +let path = require('path'); +module.exports = { + mode: 'production', + module: { + rules: [ + + // this rule can be excluded if you don't require babel-loader for your other application files + { + test: /\.m?js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + } + }, + + // this separate rule is required to make sure that the Prebid.js files are babel-ified. this rule will + // override the regular exclusion from above (for being inside node_modules). + { + test: /.js$/, + include: new RegExp(`\\${path.sep}prebid\.js`), + use: { + loader: 'babel-loader', + // presets and plugins for Prebid.js must be manually specified separate from your other babel rule. + // this can be accomplished by requiring prebid's .babelrc.js file (requires Babel 7 and Node v8.9.0+) + options: require('prebid.js/.babelrc.js') + } + } + ] + } +} +``` + +Or for Babel 6: +```javascript + // you must manually install and specify the presets and plugins yourself + options: { + plugins: [ + "transform-object-assign", // required (for IE support) and "babel-plugin-transform-object-assign" + // must be installed as part of your package. + require('prebid.js/plugins/pbjsGlobals.js') // required! + ], + presets: [ + ["env", { // you can use other presets if you wish. + "targets": { // this example is using "babel-presets-env", which must be installed if you + "browsers": [ // follow this example. + ... // your browser targets. they should probably match the targets you're using for the rest + // of your application + ] + } + }] + ] + } +``` + +Then you can use Prebid.js as any other npm depedendency + +```javascript +import prebid from 'prebid.js'; +import 'prebid.js/modules/rubiconBidAdapter'; // imported modules will register themselves automatically with prebid +import 'prebid.js/modules/appnexusBidAdapter'; +prebid.processQueue(); // required to process existing pbjs.queue blocks and setup any further pbjs.queue execution + +prebid.requestBids({ + ... +}) + +``` + + + ## Install @@ -28,7 +112,16 @@ Working examples can be found in [the developer docs](http://prebid.org/dev-docs $ cd Prebid.js $ npm install -*Note:* You need to have `NodeJS` 4.x or greater installed. +*Note:* You need to have `NodeJS` 8.9.x or greater installed. + +*Note:* In the 1.24.0 release of Prebid.js we have transitioned to using gulp 4.0 from using gulp 3.9.1. To comply with gulp's recommended setup for 4.0, you'll need to have `gulp-cli` installed globally prior to running the general `npm install`. This shouldn't impact any other projects you may work on that use an earlier version of gulp in its setup. + +If you have a previous version of `gulp` installed globally, you'll need to remove it before installing `gulp-cli`. You can check if this is installed by running `gulp -v` and seeing the version that's listed in the `CLI` field of the output. If you have the `gulp` package installed globally, it's likely the same version that you'll see in the `Local` field. If you already have `gulp-cli` installed, it should be a lower major version (it's at version `2.0.1` at the time of the transition). + +To remove the old package, you can use the command: `npm rm gulp -g` + +Once setup, run the following command to globally install the `gulp-cli` package: `npm install gulp-cli -g` + @@ -114,10 +207,20 @@ gulp test-coverage gulp view-coverage ``` -For end-to-end testing, edit the example file `./integrationExamples/gpt/pbjs_example_gpt.html`: +For Prebid.org members with access to BrowserStack, additional end-to-end testing can be done with: -1. Change `{id}` values appropriately to set up ad units and bidders -2. Set the path to Prebid.js in your example file as shown below (see `pbs.src`). +```bash +gulp e2e-test --host=test.localhost +``` + +To run these tests, the following items are required: +- setup an alias of localhost in your `hosts` file (eg `127.0.0.1 test.localhost`); note - you can use any alias. Use this alias in the command-line argument above. +- access to [BrowserStack](https://www.browserstack.com/) account. Assign the following variables in your bash_profile: +```bash +export BROWSERSTACK_USERNAME='YourUserNameHere' +export BROWSERSTACK_ACCESS_KEY='YourAccessKeyHere' +``` +You can get these BrowserStack values from your profile page. For development: @@ -167,7 +270,7 @@ Many SSPs, bidders, and publishers have contributed to this project. [60+ Bidder For guidelines, see [Contributing](./CONTRIBUTING.md). -Our PR review process can be found [here](https://github.com/prebid/Prebid.js/tree/master/pr_review.md). +Our PR review process can be found [here](https://github.com/prebid/Prebid.js/tree/master/PR_REVIEW.md). ### Add a Bidder Adapter @@ -207,7 +310,7 @@ For instructions on writing tests for Prebid.js, see [Testing Prebid.js](http:// ### Supported Browsers -Prebid.js is supported on IE10+ and modern browsers. +Prebid.js is supported on IE11 and modern browsers. ### Governance Review our governance model [here](https://github.com/prebid/Prebid.js/tree/master/governance.md). diff --git a/RELEASE_SCHEDULE.md b/RELEASE_SCHEDULE.md index efdc45f7f9f..7b2c6244bd7 100644 --- a/RELEASE_SCHEDULE.md +++ b/RELEASE_SCHEDULE.md @@ -1,11 +1,12 @@ **Table of Contents** - [Release Schedule](#release-schedule) - [Release Process](#release-process) +- [Beta Releases](#beta-releases) - [FAQs](#faqs) ## Release Schedule -We push a new release of Prebid.js every other week on Tuesday. During the adoption phase for 1.x, we are releasing updates for 1.x and 0.x at the same time. +We aim to push a new release of Prebid.js every week on Tuesday. While the releases will be available immediately for those using direct Git access, it will be about a week before the Prebid Org [Download Page](http://prebid.org/download.html) will be updated. @@ -16,13 +17,17 @@ Announcements regarding releases will be made to the #headerbidding-dev channel ## Release Process -1. Make Sure all browserstack tests are passing. On PR merge to master travis will run unit tests on browserstack. Checking the last travis build [here](https://travis-ci.org/prebid/Prebid.js/branches) for master branch will show you detailed results. +_Note: If `github.com/prebid/Prebid.js` is not configured as the git origin for your repo, all of the following git commands will have to be modified to reference the proper remote (e.g. `upstream`)_ + +1. Make Sure all browserstack tests are passing. On PR merge to master CircleCI will run unit tests on browserstack. Checking the last CircleCI build [here](https://circleci.com/gh/prebid/Prebid.js) for master branch will show you detailed results. In case of failure do following, - Try to fix the failing tests. - If you are not able to fix tests in time. Skip the test, create issue and tag contributor. #### How to run tests in browserstack + + _Note: the following browserstack information is only relevant for debugging purposes, if you will not be debugging then it can be skipped._ Set the environment variables. You may want to add these to your `~/.bashrc` for convenience. @@ -58,11 +63,12 @@ Announcements regarding releases will be made to the #headerbidding-dev channel Pull these changes locally by running command ``` git pull + git fetch --tags ``` and verify the tag. -5. Update coveralls +5. Update coveralls _(skip for legacy)_ We use https://coveralls.io/ to show parts of code covered by unit tests. @@ -74,7 +80,9 @@ Announcements regarding releases will be made to the #headerbidding-dev channel Run `gulp coveralls` to update code coverage history. -6. Distribute the code +6. Distribute the code + + _Note: do not go to step 7 until step 6 has been verified completed._ Reach out to any of the Appnexus folks to trigger the jenkins job. @@ -82,7 +90,7 @@ Announcements regarding releases will be made to the #headerbidding-dev channel Jenkins job is moving files to appnexus cdn, pushing prebid.js to npm, purging cache and sending notification to slack. Move all the files from Appnexus CDN to jsDelivr and create bash script to do above tasks. -7. Post Release Steps +7. Post Release Version Update the version Manually edit Prebid's package.json to become "1.x.x-pre" (using the values for the next release). Then commit your changes. @@ -90,11 +98,37 @@ Announcements regarding releases will be made to the #headerbidding-dev channel git commit -m "Increment pre version" git push ``` + +8. Create new release draft + + Go to [github releases](https://github.com/prebid/Prebid.js/releases) and add a new draft for the next version of Prebid.js with the following template: +``` +## 🚀New Features + +## 🛠Maintenance + +## 🐛Bug Fixes +``` + +## Beta Releases + +Prebid.js features may be released as Beta or as Generally Available (GA). + +Characteristics of a `Beta` release: +- May be a partial implementation (e.g. more work needed to flesh out the feature) +- May not be fully tested with other features +- Limited documentation, focused on technical aspects +- Few users + +Characteristics of a `GA` release: +- Complete set of functionality +- Significant user base with no major issues for at least a month +- Decent documentation that includes business need, use cases, and examples ## FAQs -**1. Is there flexibility in the 2-week schedule?** +**1. Is there flexibility in the schedule?** If a major bug is found in the current release, a maintenance patch will be done as soon as possible. diff --git a/allowedModules.js b/allowedModules.js new file mode 100644 index 00000000000..e66b8e24098 --- /dev/null +++ b/allowedModules.js @@ -0,0 +1,24 @@ + +const sharedWhiteList = [ + "core-js/library/fn/array/find", // no ie11 + "core-js/library/fn/array/includes", // no ie11 + "core-js/library/fn/set", // ie11 supports Set but not Set#values + "core-js/library/fn/string/includes", // no ie11 + "core-js/library/fn/number/is-integer", // no ie11, + "core-js/library/fn/array/from" // no ie11 +]; + +module.exports = { + 'modules': [ + ...sharedWhiteList, + 'jsencrypt', + 'crypto-js' + ], + 'src': [ + ...sharedWhiteList, + 'fun-hooks/no-eval', + 'just-clone', + 'dlv', + 'dset' + ] +}; diff --git a/browsers.json b/browsers.json index cb523addc7e..9042d7d0627 100644 --- a/browsers.json +++ b/browsers.json @@ -1,9 +1,17 @@ { - "bs_ie_14_windows_10": { + "bs_edge_17_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "edge", - "browser_version": "14.0", + "browser_version": "17.0", + "device": null, + "os": "Windows" + }, + "bs_edge_16_windows_10": { + "base": "BrowserStack", + "os_version": "10", + "browser": "edge", + "browser_version": "16.0", "device": null, "os": "Windows" }, @@ -15,68 +23,52 @@ "device": null, "os": "Windows" }, - "bs_chrome_62_windows_10": { + "bs_chrome_74_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "chrome", - "browser_version": "62.0", + "browser_version": "74.0", "device": null, "os": "Windows" }, - "bs_chrome_61_windows_10": { + "bs_chrome_75_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "chrome", - "browser_version": "61.0", + "browser_version": "75.0", "device": null, "os": "Windows" }, - "bs_firefox_58_windows_10": { + "bs_firefox_66_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "firefox", - "browser_version": "58.0", + "browser_version": "66.0", "device": null, "os": "Windows" }, - "bs_firefox_57_windows_10": { + "bs_firefox_67_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "firefox", - "browser_version": "57.0", + "browser_version": "67.0", "device": null, "os": "Windows" }, - "bs_safari_9.1_mac_elcapitan": { + "bs_safari_11_mac_high_sierra": { "base": "BrowserStack", - "os_version": "El Capitan", + "os_version": "High Sierra", "browser": "safari", - "browser_version": "9.1", + "browser_version": "11.1", "device": null, "os": "OS X" }, - "bs_safari_8_mac_yosemite": { + "bs_safari_12_mac_mojave": { "base": "BrowserStack", - "os_version": "Yosemite", + "os_version": "Mojave", "browser": "safari", - "browser_version": "8.0", + "browser_version": "12.0", "device": null, "os": "OS X" - }, - "bs_ios_9": { - "base": "BrowserStack", - "os": "ios", - "os_version": "9.1", - "browser": "iphone", - "device": "iPhone 6S", - "browser_version": null - }, - "bs_ios_8": { - "base": "BrowserStack", - "os": "ios", - "os_version": "8.3", - "browser": "iphone", - "device": "iPhone 6", - "browser_version": null } -} \ No newline at end of file +} diff --git a/governance.md b/governance.md index 9f8a1fe1e13..3d00f067194 100644 --- a/governance.md +++ b/governance.md @@ -2,10 +2,10 @@ This document describes the governance model for the Prebid project. The Prebid project’s stated mission is to facilitate fair, transparent, and effective header bidding across the industry, and is responsible for creating and maintaining such projects as [Prebid.js](https://github.com/prebid/Prebid.js). -1. A single Tech Lead oversees the technical direction of the project and appoints Core Team members +1. A single Tech Lead (PMC Chair) oversees the technical direction of the project and appoints Core Team members 2. The Core Team members maintain the project on an ongoing basis with direction from the Tech Lead. 3. In the event of any disagreements, the Tech Lead will make a final decision. -4. If there is no Tech Lead available to perform his/her duties, AppNexus Inc. will appoint one. +4. If there is no Tech Lead available to perform his/her duties, Prebid.org will appoint one. ### Roles and Responsibilities: - **User:** Any individual who consumes / uses the Prebid.js library. @@ -15,9 +15,9 @@ This document describes the governance model for the Prebid project. The Prebid ### Current Prebid.js Core Team - @mkendall07 (Tech Lead) -- @protonate +- @jsnellbaker - @matthewlane - @jaiminpanchal27 - @snapwich - @harpere -- @dbemiller +- @mike-chowla diff --git a/gulpHelpers.js b/gulpHelpers.js index d4078aa3dbc..84f01b4e966 100644 --- a/gulpHelpers.js +++ b/gulpHelpers.js @@ -3,15 +3,15 @@ const fs = require('fs.extra'); const path = require('path'); const argv = require('yargs').argv; const MANIFEST = 'package.json'; -const exec = require('child_process').exec; const through = require('through2'); const _ = require('lodash'); const gutil = require('gulp-util'); +const submodules = require('./modules/.submodules.json'); const MODULE_PATH = './modules'; const BUILD_PATH = './build/dist'; const DEV_PATH = './build/dev'; - +const ANALYTICS_PATH = '../analytics'; // get only subdirectories that contain package.json with 'main' property function isModuleDirectory(filePath) { @@ -21,8 +21,7 @@ function isModuleDirectory(filePath) { const module = require(manifestPath); return module && module.main; } - } - catch (error) {} + } catch (error) {} } module.exports = { @@ -37,11 +36,13 @@ module.exports = { jsonifyHTML: function (str) { console.log(arguments); return str.replace(/\n/g, '') - .replace(/<\//g, '<\\/') - .replace(/\/>/g, '\\/>'); + .replace(/<\//g, '<\\/') + .replace(/\/>/g, '\\/>'); }, getArgModules() { - var modules = (argv.modules || '').split(',').filter(module => !!module); + var modules = (argv.modules || '') + .split(',') + .filter(module => !!module); try { if (modules.length === 1 && path.extname(modules[0]).toLowerCase() === '.json') { @@ -51,13 +52,22 @@ module.exports = { fs.readFileSync(moduleFile, 'utf8') ); } - } catch(e) { + } catch (e) { throw new gutil.PluginError({ plugin: 'modules', message: 'failed reading: ' + argv.modules }); } + Object.keys(submodules).forEach(parentModule => { + if ( + !modules.includes(parentModule) && + modules.some(module => submodules[parentModule].includes(module)) + ) { + modules.unshift(parentModule); + } + }); + return modules; }, getModules: _.memoize(function(externalModules) { @@ -66,24 +76,27 @@ module.exports = { try { var absoluteModulePath = path.join(__dirname, MODULE_PATH); internalModules = fs.readdirSync(absoluteModulePath) - .filter(file => !(/(^|\/)\.[^\/\.]/g).test(file)) + .filter(file => (/^[^\.]+(\.js)?$/).test(file)) .reduce((memo, file) => { var moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; var modulePath = path.join(absoluteModulePath, file); if (fs.lstatSync(modulePath).isDirectory()) { - modulePath = path.join(modulePath, "index.js") + modulePath = path.join(modulePath, 'index.js') } memo[modulePath] = moduleName; return memo; }, {}); - } catch(err) { + } catch (err) { internalModules = {}; } return Object.assign(externalModules.reduce((memo, module) => { try { - var modulePath = require.resolve(module); + // prefer internal project modules before looking at project dependencies + var modulePath = require.resolve(module, {paths: ['./modules']}); + if (modulePath === '') modulePath = require.resolve(module); + memo[modulePath] = module; - } catch(err) { + } catch (err) { // do something } return memo; @@ -92,7 +105,7 @@ module.exports = { getBuiltModules: function(dev, externalModules) { var modules = this.getModuleNames(externalModules); - if(Array.isArray(externalModules)) { + if (Array.isArray(externalModules)) { modules = _.intersection(modules, externalModules); } return modules.map(name => path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, name + '.js')); @@ -126,72 +139,33 @@ module.exports = { * Invoke with gulp --analytics * Returns an array of source files for inclusion in build process */ - getAnalyticsSources: function(directory) { - if (!argv.analytics) {return [];} // empty arrays won't affect a standard build + getAnalyticsSources: function() { + if (!argv.analytics) { return []; } // empty arrays won't affect a standard build - const directoryContents = fs.readdirSync(directory); + const directoryContents = fs.readdirSync(ANALYTICS_PATH); return directoryContents - .filter(file => isModuleDirectory(path.join(directory, file))) + .filter(file => isModuleDirectory(path.join(ANALYTICS_PATH, file))) .map(moduleDirectory => { - const module = require(path.join(directory, moduleDirectory, MANIFEST)); - return path.join(directory, moduleDirectory, module.main); + const module = require(path.join(ANALYTICS_PATH, moduleDirectory, MANIFEST)); + return path.join(ANALYTICS_PATH, moduleDirectory, module.main); }); }, - createEnd2EndTestReport : function(targetDestinationDir) { - var browsers = require('./browsers.json'); - var env = []; - var input = 'bs'; - for(var key in browsers) { - if(key.substring(0, input.length) === input && browsers[key].browser !== 'iphone') { - env.push(key); + /* + * Returns the babel options object necessary for allowing analytics packages + * to have their own configs. Gets added to prebid's webpack config with the + * flag --analytics + */ + getAnalyticsOptions: function() { + let options; + + if (argv.analytics) { + // https://babeljs.io/docs/en/options#babelrcroots + options = { + babelrcRoots: ['.', ANALYTICS_PATH], } } - //create new directory structure - fs.rmrfSync(targetDestinationDir); - env.forEach(item => { - fs.mkdirpSync(targetDestinationDir + '/' + item); - }); - - //move xml files to newly created directory - var walker = fs.walk('./build/coverage/e2e/reports'); - walker.on("file", function (root, stat, next) { - env.forEach(item => { - if(stat.name.search(item) !== -1) { - var src = root + '/' + stat.name; - var dest = targetDestinationDir + '/' + item + '/' + stat.name; - fs.copy(src, dest, {replace: true}, function(err) { - if(err) { - throw err; - } - }); - } - }); - next(); - }); - - //run junit-viewer to read xml and create html - env.forEach(item => { - //junit-viewer --results="./custom-reports/chrome51" --save="./chrome.html" - var cmd = 'junit-viewer --results="' + targetDestinationDir + '/' + item + '" --save="' + targetDestinationDir + '/' + item +'.html"'; - exec(cmd); - }); - - //create e2e-results.html - var html = 'End to End Testing Result
Note: Refresh in 2-3 seconds if it says "Cannot get ....."
'; - var li = ''; - var tabs = ''; - env.forEach(function(item,i) { - i++; - li = li + '
  • '+item+'
  • '; - tabs = tabs + '
    '; - }); - html = html + '
      ' + li + '
    ' + tabs; - html = html + '
    '; - - var filepath = targetDestinationDir + '/results.html'; - fs.openSync(filepath, 'w+'); - fs.writeFileSync(filepath, html); + return options; } }; diff --git a/gulpfile.js b/gulpfile.js index d2955f7d777..24c628ef228 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,54 +5,147 @@ var argv = require('yargs').argv; var gulp = require('gulp'); var gutil = require('gulp-util'); var connect = require('gulp-connect'); -var path = require('path'); var webpack = require('webpack'); var webpackStream = require('webpack-stream'); var uglify = require('gulp-uglify'); -var clean = require('gulp-clean'); +var gulpClean = require('gulp-clean'); var KarmaServer = require('karma').Server; var karmaConfMaker = require('./karma.conf.maker'); -var opens = require('open'); +var opens = require('opn'); var webpackConfig = require('./webpack.conf'); var helpers = require('./gulpHelpers'); -var del = require('del'); -var gulpDocumentation = require('gulp-documentation'); var concat = require('gulp-concat'); var header = require('gulp-header'); var footer = require('gulp-footer'); var replace = require('gulp-replace'); var shell = require('gulp-shell'); -var optimizejs = require('gulp-optimize-js'); var eslint = require('gulp-eslint'); var gulpif = require('gulp-if'); var sourcemaps = require('gulp-sourcemaps'); var through = require('through2'); var fs = require('fs'); var jsEscape = require('gulp-js-escape'); +const path = require('path'); +const execa = require('execa'); var prebid = require('./package.json'); var dateString = 'Updated : ' + (new Date()).toISOString().substring(0, 10); var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + ' */\n'; -var analyticsDirectory = '../analytics'; var port = 9999; -// Tasks -gulp.task('default', ['webpack']); +// these modules must be explicitly listed in --modules to be included in the build, won't be part of "all" modules +var explicitModules = [ + 'pre1api' +]; + +// all the following functions are task functions +function bundleToStdout() { + nodeBundle().then(file => console.log(file)); +} +bundleToStdout.displayName = 'bundle-to-stdout'; -gulp.task('serve', ['lint', 'build-bundle-dev', 'watch', 'test']); +function clean() { + return gulp.src(['build'], { + read: false, + allowEmpty: true + }) + .pipe(gulpClean()); +} -gulp.task('serve-nw', ['lint', 'watch', 'e2etest']); +// Dependant task for building postbid. It escapes postbid-config file. +function escapePostbidConfig() { + gulp.src('./integrationExamples/postbid/oas/postbid-config.js') + .pipe(jsEscape()) + .pipe(gulp.dest('build/postbid/')); +}; +escapePostbidConfig.displayName = 'escape-postbid-config'; -gulp.task('run-tests', ['lint', 'test-coverage']); +function lint(done) { + if (argv.nolint) { + return done(); + } + const isFixed = function(file) { + return file.eslint != null && file.eslint.fixed; + } + return gulp.src(['src/**/*.js', 'modules/**/*.js', 'test/**/*.js'], {base: './'}) + .pipe(gulpif(argv.nolintfix, eslint(), eslint({fix: true}))) + .pipe(eslint.format('stylish')) + .pipe(eslint.failAfterError()) + .pipe(gulpif(isFixed, gulp.dest('./'))); +}; -gulp.task('build', ['build-bundle-prod']); +// View the code coverage report in the browser. +function viewCoverage(done) { + var coveragePort = 1999; + var mylocalhost = (argv.host) ? argv.host : 'localhost'; -gulp.task('clean', function () { - return gulp.src(['build'], { - read: false - }) - .pipe(clean()); -}); + connect.server({ + port: coveragePort, + root: 'build/coverage/karma_html', + livereload: false + }); + opens('http://' + mylocalhost + ':' + coveragePort); + done(); +}; + +viewCoverage.displayName = 'view-coverage'; + +// Watch Task with Live Reload +function watch(done) { + var mainWatcher = gulp.watch([ + 'src/**/*.js', + 'modules/**/*.js', + 'test/spec/**/*.js', + '!test/spec/loaders/**/*.js' + ]); + var loaderWatcher = gulp.watch([ + 'loaders/**/*.js', + 'test/spec/loaders/**/*.js' + ]); + + connect.server({ + https: argv.https, + port: port, + root: './', + livereload: true + }); + + mainWatcher.on('all', gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test))); + loaderWatcher.on('all', gulp.series(lint)); + done(); +}; + +function makeDevpackPkg() { + var cloned = _.cloneDeep(webpackConfig); + cloned.devtool = 'source-map'; + var externalModules = helpers.getArgModules(); + + const analyticsSources = helpers.getAnalyticsSources(); + const moduleSources = helpers.getModulePaths(externalModules); + + return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) + .pipe(helpers.nameModules(externalModules)) + .pipe(webpackStream(cloned, webpack)) + .pipe(gulp.dest('build/dev')) + .pipe(connect.reload()); +} + +function makeWebpackPkg() { + var cloned = _.cloneDeep(webpackConfig); + delete cloned.devtool; + + var externalModules = helpers.getArgModules(); + + const analyticsSources = helpers.getAnalyticsSources(); + const moduleSources = helpers.getModulePaths(externalModules); + + return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) + .pipe(helpers.nameModules(externalModules)) + .pipe(webpackStream(cloned, webpack)) + .pipe(uglify()) + .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid }))) + .pipe(gulp.dest('build/dist')); +} function gulpBundle(dev) { return bundle(dev).pipe(gulp.dest('build/' + (dev ? 'dev' : 'dist'))); @@ -71,20 +164,15 @@ function nodeBundle(modules) { }); } -// these modules must be explicitly listed in --modules to be included in the build, won't be part of "all" modules -var explicitModules = [ - 'pre1api' -]; - function bundle(dev, moduleArr) { - var modules = moduleArr || helpers.getArgModules(), - allModules = helpers.getModuleNames(modules); + var modules = moduleArr || helpers.getArgModules(); + var allModules = helpers.getModuleNames(modules); - if(modules.length === 0) { - modules = allModules.filter(module => !explicitModules.includes(module)); + if (modules.length === 0) { + modules = allModules.filter(module => explicitModules.indexOf(module) === -1); } else { var diff = _.difference(modules, allModules); - if(diff.length !== 0) { + if (diff.length !== 0) { throw new gutil.PluginError({ plugin: 'bundle', message: 'invalid modules: ' + diff.join(', ') @@ -106,78 +194,17 @@ function bundle(dev, moduleArr) { gutil.log('Generating bundle:', outputFileName); return gulp.src( - entries - ) + entries + ) .pipe(gulpif(dev, sourcemaps.init({loadMaps: true}))) .pipe(concat(outputFileName)) .pipe(gulpif(!argv.manualEnable, footer('\n<%= global %>.processQueue();', { - global: prebid.globalVarName - } + global: prebid.globalVarName + } ))) .pipe(gulpif(dev, sourcemaps.write('.'))); } -// Workaround for incompatibility between Karma & gulp callbacks. -// See https://github.com/karma-runner/gulp-karma/issues/18 for some related discussion. -function newKarmaCallback(done) { - return function (exitCode) { - if (exitCode) { - done(new Error('Karma tests failed with exit code ' + exitCode)); - } else { - if (argv.browserstack) { - process.exit(0); - } else { - done(); - } - } - } -} - -gulp.task('build-bundle-dev', ['devpack'], gulpBundle.bind(null, true)); -gulp.task('build-bundle-prod', ['webpack'], gulpBundle.bind(null, false)); -gulp.task('bundle', gulpBundle.bind(null, false)); // used for just concatenating pre-built files with no build step - -gulp.task('bundle-to-stdout', function() { - nodeBundle().then(file => console.log(file)); -}); - -gulp.task('devpack', ['clean'], function () { - var cloned = _.cloneDeep(webpackConfig); - cloned.devtool = 'source-map'; - var externalModules = helpers.getArgModules(); - - const analyticsSources = helpers.getAnalyticsSources(analyticsDirectory); - const moduleSources = helpers.getModulePaths(externalModules); - - return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) - .pipe(helpers.nameModules(externalModules)) - .pipe(webpackStream(cloned, webpack)) - .pipe(replace('$prebid.version$', prebid.version)) - .pipe(gulp.dest('build/dev')) - .pipe(connect.reload()); -}); - -gulp.task('webpack', ['clean'], function () { - var cloned = _.cloneDeep(webpackConfig); - - delete cloned.devtool; - - var externalModules = helpers.getArgModules(); - - const analyticsSources = helpers.getAnalyticsSources(analyticsDirectory); - const moduleSources = helpers.getModulePaths(externalModules); - - return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) - .pipe(helpers.nameModules(externalModules)) - .pipe(webpackStream(cloned, webpack)) - .pipe(replace('$prebid.version$', prebid.version)) - .pipe(uglify()) - .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid }))) - .pipe(optimizejs()) - .pipe(gulp.dest('build/dist')) - .pipe(connect.reload()); -}); - // Run the unit tests. // // By default, this runs in headless chrome. @@ -186,141 +213,111 @@ gulp.task('webpack', ['clean'], function () { // If --file "" is given, the task will only run tests in the specified file. // If --browserstack is given, it will run the full suite of currently supported browsers. // If --browsers is given, browsers can be chosen explicitly. e.g. --browsers=chrome,firefox,ie9 -gulp.task('test', ['clean'], function (done) { - var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file); +// If --notest is given, it will immediately skip the test task (useful for developing changes with `gulp serve --notest`) +function test(done) { + if (argv.notest) { + done(); + } else if (argv.e2e) { + let wdioCmd = path.join(__dirname, 'node_modules/.bin/wdio'); + let wdioConf = path.join(__dirname, 'wdio.conf.js'); + let wdioOpts = [ + wdioConf + ]; + return execa(wdioCmd, wdioOpts, { stdio: 'inherit' }); + } else { + var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file); - var browserOverride = helpers.parseBrowserArgs(argv).map(helpers.toCapitalCase); - if (browserOverride.length > 0) { - karmaConf.browsers = browserOverride; + var browserOverride = helpers.parseBrowserArgs(argv).map(helpers.toCapitalCase); + if (browserOverride.length > 0) { + karmaConf.browsers = browserOverride; + } + + new KarmaServer(karmaConf, newKarmaCallback(done)).start(); } +} - new KarmaServer(karmaConf, newKarmaCallback(done)).start(); -}); +function newKarmaCallback(done) { + return function(exitCode) { + if (exitCode) { + done(new Error('Karma tests failed with exit code ' + exitCode)); + if (argv.browserstack) { + process.exit(exitCode); + } + } else { + done(); + if (argv.browserstack) { + process.exit(exitCode); + } + } + } +} // If --file "" is given, the task will only run tests in the specified file. -gulp.task('test-coverage', ['clean'], function(done) { +function testCoverage(done) { new KarmaServer(karmaConfMaker(true, false, false, argv.file), newKarmaCallback(done)).start(); -}); - -// View the code coverage report in the browser. -gulp.task('view-coverage', function (done) { - var coveragePort = 1999; - - connect.server({ - port: coveragePort, - root: 'build/coverage/karma_html', - livereload: false - }); - opens('http://localhost:' + coveragePort); - done(); -}); +} -gulp.task('coveralls', ['test-coverage'], function() { // 2nd arg is a dependency: 'test' must be finished +function coveralls() { // 2nd arg is a dependency: 'test' must be finished // first send results of istanbul's test coverage to coveralls.io. return gulp.src('gulpfile.js', { read: false }) // You have to give it a file, but you don't // have to read it. .pipe(shell('cat build/coverage/lcov.info | node_modules/coveralls/bin/coveralls.js')); -}); +} -// Watch Task with Live Reload -gulp.task('watch', function () { - gulp.watch([ - 'src/**/*.js', - 'modules/**/*.js', - 'test/spec/**/*.js', - '!test/spec/loaders/**/*.js' - ], ['lint', 'build-bundle-dev', 'test']); - gulp.watch([ - 'loaders/**/*.js', - 'test/spec/loaders/**/*.js' - ], ['lint']); - connect.server({ - https: argv.https, - port: port, - root: './', - livereload: true - }); -}); +// This task creates postbid.js. Postbid setup is different from prebid.js +// More info can be found here http://prebid.org/overview/what-is-post-bid.html -gulp.task('lint', () => { - return gulp.src(['src/**/*.js', 'modules/**/*.js', 'test/**/*.js']) - .pipe(eslint()) - .pipe(eslint.format('stylish')) - .pipe(eslint.failAfterError()); -}); - -gulp.task('clean-docs', function () { - del(['docs']); -}); - -gulp.task('docs', ['clean-docs'], function () { - return gulp.src('src/prebid.js') - .pipe(gulpDocumentation('md')) - .on('error', function (err) { - gutil.log('`gulp-documentation` failed:', err.message); - }) - .pipe(gulp.dest('docs')); -}); - -gulp.task('e2etest', ['devpack', 'webpack'], function() { - var cmdQueue = []; - if(argv.browserstack) { - var browsers = require('./browsers.json'); - delete browsers['bs_ie_9_windows_7']; - - var cmdStr = ' --config nightwatch.conf.js'; - if (argv.group) { - cmdStr = cmdStr + ' --group ' + argv.group; - } - cmdStr = cmdStr + ' --reporter ./test/spec/e2e/custom-reporter/pbjs-html-reporter.js'; +function buildPostbid() { + var fileContent = fs.readFileSync('./build/postbid/postbid-config.js', 'utf8'); - var startWith = 'bs'; + return gulp.src('./integrationExamples/postbid/oas/postbid.js') + .pipe(replace('\[%%postbid%%\]', fileContent)) + .pipe(gulp.dest('build/postbid/')); +} - Object.keys(browsers).filter(function(v){ - return v.substring(0, startWith.length) === startWith && browsers[v].browser !== 'iphone'; - }).map(function(v,i,arr) { - var newArr = (i%2 === 0) ? arr.slice(i,i+2) : null; - if(newArr) { - var cmd = 'nightwatch --env ' + newArr.join(',') + cmdStr; - cmdQueue.push(cmd); - } +function setupE2e(done) { + if (!argv.host) { + throw new gutil.PluginError({ + plugin: 'E2E test', + message: gutil.colors.red('Host should be defined e.g. ap.localhost, anlocalhost. localhost cannot be used as safari browserstack is not able to connect to localhost') }); } + process.env.TEST_SERVER_HOST = argv.host; + if (argv.https) { + process.env.TEST_SERVER_PROTOCOL = argv.https; + } + argv.e2e = true; + done(); +} - return gulp.src('') - .pipe(shell(cmdQueue.join(';'))); -}); +// support tasks +gulp.task(lint); +gulp.task(watch); -gulp.task('e2etest-report', function() { - var reportPort = 9010; - var targetDestinationDir = './e2etest-report'; - helpers.createEnd2EndTestReport(targetDestinationDir); - connect.server({ - port: reportPort, - root: './', - livereload: true - }); +gulp.task(clean); - setTimeout(function() { - opens('http://localhost:' + reportPort + '/' + targetDestinationDir.slice(2) + '/results.html'); - }, 5000); -}); +gulp.task(escapePostbidConfig); -// This task creates postbid.js. Postbid setup is different from prebid.js -// More info can be found here http://prebid.org/overview/what-is-post-bid.html -gulp.task('build-postbid', ['escape-postbid-config'], function() { - var fileContent = fs.readFileSync('./build/postbid/postbid-config.js', 'utf8'); +gulp.task('build-bundle-dev', gulp.series(makeDevpackPkg, gulpBundle.bind(null, true))); +gulp.task('build-bundle-prod', gulp.series(makeWebpackPkg, gulpBundle.bind(null, false))); - return gulp.src('./integrationExamples/postbid/oas/postbid.js') - .pipe(replace('\[%%postbid%%\]', fileContent)) - .pipe(gulp.dest('build/postbid/')); -}); +// public tasks (dependencies are needed for each task since they can be ran on their own) +gulp.task('test', gulp.series(clean, lint, test)); -// Dependant task for building postbid. It escapes postbid-config file. -gulp.task('escape-postbid-config', function() { - gulp.src('./integrationExamples/postbid/oas/postbid-config.js') - .pipe(jsEscape()) - .pipe(gulp.dest('build/postbid/')); -}); +gulp.task('test-coverage', gulp.series(clean, testCoverage)); +gulp.task(viewCoverage); + +gulp.task('coveralls', gulp.series('test-coverage', coveralls)); + +gulp.task('build', gulp.series(clean, 'build-bundle-prod')); +gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); + +gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, test))); +gulp.task('default', gulp.series(clean, makeWebpackPkg)); + +gulp.task('e2e-test', gulp.series(clean, setupE2e, gulp.parallel('build-bundle-dev', watch), test)) +// other tasks +gulp.task(bundleToStdout); +gulp.task('bundle', gulpBundle.bind(null, false)); // used for just concatenating pre-built files with no build step module.exports = nodeBundle; diff --git a/integrationExamples/gpt/digitrust_Full.html b/integrationExamples/gpt/digitrust_Full.html new file mode 100644 index 00000000000..7ec268a619a --- /dev/null +++ b/integrationExamples/gpt/digitrust_Full.html @@ -0,0 +1,222 @@ + + + Full DigiTrust Prebid Sample + + + + + + + + + + + + + +

    DigiTrust Prebid Full Sample

    + + +

    + This sample shows the simplest integration path for using DigiTrust ID with Prebid. + You can use DigiTrust ID without integrating the entire DigiTrust suite. +

    + +
    + +
    + +
    + + + + diff --git a/integrationExamples/gpt/digitrust_Simple.html b/integrationExamples/gpt/digitrust_Simple.html new file mode 100644 index 00000000000..c9a8c1d2ad6 --- /dev/null +++ b/integrationExamples/gpt/digitrust_Simple.html @@ -0,0 +1,220 @@ + + + Simple DigiTrust Prebid - No Framework + + + + + + + + + + + + + + +

    DigiTrust Prebid Sample - No Framework

    + +

    + This sample shows the simplest integration path for using DigiTrust ID with Prebid. + You can use DigiTrust ID without integrating the entire DigiTrust suite. +

    +
    + +
    + +
    + + diff --git a/integrationExamples/gpt/gdpr_hello_world.html b/integrationExamples/gpt/gdpr_hello_world.html index 9f6194edb16..de0630178f1 100644 --- a/integrationExamples/gpt/gdpr_hello_world.html +++ b/integrationExamples/gpt/gdpr_hello_world.html @@ -1,20 +1,101 @@ - - + + + - - - + setTimeout(function() { + sendAdserverRequest(); + console.log('timeout in main pbjs fired'); + }, FAILSAFE_TIMEOUT); + - \ No newline at end of file + diff --git a/integrationExamples/gpt/gpt_aliasingBidder.html b/integrationExamples/gpt/gpt_aliasingBidder.html deleted file mode 100644 index 693be76e82e..00000000000 --- a/integrationExamples/gpt/gpt_aliasingBidder.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - - - - - - - -

    Prebid.js Test

    - -
    - -
    - - -
    - -
    - - - - - - - diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index a5949b87c56..337c762adc5 100644 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -1,98 +1,91 @@ - - - - - - - - - - - - - - - -

    Prebid.js Test

    -
    Div-1
    -
    - -
    - - \ No newline at end of file + + + + + + + + + + + + + + +

    Prebid.js Test

    +
    Div-1
    +
    + +
    + + diff --git a/integrationExamples/gpt/inskin_example.html b/integrationExamples/gpt/inskin_example.html new file mode 100644 index 00000000000..197a5b1ffe1 --- /dev/null +++ b/integrationExamples/gpt/inskin_example.html @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + +

    Prebid.js Test

    +
    Div-1
    +
    + +
    + + diff --git a/integrationExamples/gpt/load_pbjs_before_dfp_example.html b/integrationExamples/gpt/load_pbjs_before_dfp_example.html deleted file mode 100644 index cb17b8c3348..00000000000 --- a/integrationExamples/gpt/load_pbjs_before_dfp_example.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - -

    Prebid.js Test

    -
    - -
    -
    - -
    - - diff --git a/integrationExamples/gpt/load_pbjs_dfp_concurrently.html b/integrationExamples/gpt/load_pbjs_dfp_concurrently.html deleted file mode 100644 index 0d6270ba7a5..00000000000 --- a/integrationExamples/gpt/load_pbjs_dfp_concurrently.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - -

    Prebid.js Test

    -
    - -
    -
    - -
    - - diff --git a/integrationExamples/gpt/pbjs_example_gpt.html b/integrationExamples/gpt/pbjs_example_gpt.html deleted file mode 100644 index f1ec912fd26..00000000000 --- a/integrationExamples/gpt/pbjs_example_gpt.html +++ /dev/null @@ -1,587 +0,0 @@ - - - -Prebid.js integration example - - - - -

    Prebid.js Test

    - -
    - -
    - - -
    - -
    - - - - - - - diff --git a/integrationExamples/gpt/pbjs_innity_gpt.html b/integrationExamples/gpt/pbjs_innity_gpt.html deleted file mode 100644 index 7882d44791d..00000000000 --- a/integrationExamples/gpt/pbjs_innity_gpt.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -

    Prebid.js Test

    -
    Div-1
    -
    - -
    - - \ No newline at end of file diff --git a/integrationExamples/gpt/pbjs_partial_refresh_gpt.html b/integrationExamples/gpt/pbjs_partial_refresh_gpt.html deleted file mode 100644 index 09009a24d76..00000000000 --- a/integrationExamples/gpt/pbjs_partial_refresh_gpt.html +++ /dev/null @@ -1,296 +0,0 @@ - - - - - - - - - - - - - -

    Prebid.js Test

    - -
    Div-1, 300x250 or 300x600
    - - - -
    - -
    - - -
    Div-2, 728x90 or 970x90
    - - - - -
    - -
    - - - - - diff --git a/integrationExamples/gpt/pbjs_ucfunnel_gpt.html b/integrationExamples/gpt/pbjs_ucfunnel_gpt.html deleted file mode 100644 index cda2af03b18..00000000000 --- a/integrationExamples/gpt/pbjs_ucfunnel_gpt.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -

    Prebid.js Test

    -
    Div-1
    - - -
    - -
    - - - diff --git a/integrationExamples/gpt/pbjs_yieldbot_gpt.html b/integrationExamples/gpt/pbjs_yieldbot_gpt.html deleted file mode 100644 index 986eed8fc5e..00000000000 --- a/integrationExamples/gpt/pbjs_yieldbot_gpt.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - -

    Prebid.js Yieldbot Adapter Basic Example

    - Use the links below to enable and disable Yieldbot test bids.
    -
    - Note: -
    - The "Enable - Yieldbot Test Bids" link below will set a cookie to force Yieldbot bid requests to return static test creative: the cookie expires in 24 hrs. -
    - -
      -
    1. Enable - Yieldbot Test Bids
    2. -
    3. Disable - Yieldbot Test Bids
    4. -
    -
    Div-0, 728x90
    - -
    - -
    -
    Div-1, 300x250 or 300x600
    - -
    - -
    -
    Div-2, 300x250 or 300x600
    - The bid for the 300x250 | 300x600 slot is shown under Div-1 above. -
      -
    • Refresh this slot after initial page view and you should see the Yieldbot test creative.
    • -
    - -
    - -
    - - diff --git a/integrationExamples/gpt/pollux_example.html b/integrationExamples/gpt/pollux_example.html deleted file mode 100644 index 56eedbf2a9c..00000000000 --- a/integrationExamples/gpt/pollux_example.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - test - - - -
    - -
    - -
    -
    - -
    - -
    - - \ No newline at end of file diff --git a/integrationExamples/gpt/prebidServer_example.html b/integrationExamples/gpt/prebidServer_example.html index 2f91c2b966f..db61a6a46d6 100644 --- a/integrationExamples/gpt/prebidServer_example.html +++ b/integrationExamples/gpt/prebidServer_example.html @@ -4,7 +4,6 @@ var PREBID_TIMEOUT = 3000; var googletag = googletag || {}; - var sizes = [[728, 90],[300, 250], [300,600]]; googletag.cmd = googletag.cmd || []; function initAdserver() { @@ -37,12 +36,12 @@ pbjs.que.push(function() { var adUnits = [{ code: 'div-gpt-ad-1460505748561-0', - sizes: [[300, 250], [300,600]], + sizes: [[300, 250]], bids: [ { bidder: 'appnexus', params: { - placementId: '10433394' + placementId: 13144370 } } ] @@ -56,7 +55,7 @@ bidders : ['appnexus'], timeout : 1000, //default value is 1000 adapter : 'prebidServer', //if we have any other s2s adapter, default value is s2s - endpoint : 'https://prebid.adnxs.com/pbs/v1/auction?url_override=http%3A%2F%2Fwww.nytimes.com' + endpoint : 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' } }); @@ -72,7 +71,7 @@ + + + + + + + + + + + +

    Prebid Native

    +
    +

    No response

    + +
    + +
    +
    + +
    +

    No response

    + +
    + + + + diff --git a/integrationExamples/gpt/unruly_example.html b/integrationExamples/gpt/unruly_example.html deleted file mode 100644 index 77a9b02b3dd..00000000000 --- a/integrationExamples/gpt/unruly_example.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - test - - - -
    - -
    - - - diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html new file mode 100644 index 00000000000..9e1cd01f8fa --- /dev/null +++ b/integrationExamples/gpt/userId_example.html @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + +

    Rubicon Project Prebid

    + +
    + +
    + + diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html index 3b0058f2ee8..a6981706227 100644 --- a/integrationExamples/gpt/x-domain/creative.html +++ b/integrationExamples/gpt/x-domain/creative.html @@ -2,10 +2,11 @@ // this script can be returned by an ad server delivering a cross domain iframe, into which the // creative will be rendered, e.g. DFP delivering a SafeFrame +let windowLocation = window.location; var urlParser = document.createElement('a'); urlParser.href = '%%PATTERN:url%%'; var publisherDomain = urlParser.protocol + '//' + urlParser.hostname; -var adServerDomain = urlParser.protocol + '//tpc.googlesyndication.com'; +var adServerDomain = windowLocation.protocol + '//tpc.googlesyndication.com'; function renderAd(ev) { var key = ev.message ? 'message' : 'data'; diff --git a/integrationExamples/longform/basic_w_custom_adserver_translation.html b/integrationExamples/longform/basic_w_custom_adserver_translation.html new file mode 100644 index 00000000000..995ea822da4 --- /dev/null +++ b/integrationExamples/longform/basic_w_custom_adserver_translation.html @@ -0,0 +1,132 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Integration Demo

    +

    custom adserver translation file

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/basic_w_requireExactDuration.html b/integrationExamples/longform/basic_w_requireExactDuration.html new file mode 100644 index 00000000000..016b51d09c4 --- /dev/null +++ b/integrationExamples/longform/basic_w_requireExactDuration.html @@ -0,0 +1,130 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Test Page

    +

    requireExactDuration = true

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/basic_wo_brandCategoryExclusion.html b/integrationExamples/longform/basic_wo_brandCategoryExclusion.html new file mode 100644 index 00000000000..c8af5801406 --- /dev/null +++ b/integrationExamples/longform/basic_wo_brandCategoryExclusion.html @@ -0,0 +1,130 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Test Page

    +

    brandCategoryExclusion = false

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/basic_wo_requireExactDuration.html b/integrationExamples/longform/basic_wo_requireExactDuration.html new file mode 100644 index 00000000000..215138479cc --- /dev/null +++ b/integrationExamples/longform/basic_wo_requireExactDuration.html @@ -0,0 +1,131 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Test Page

    +

    requireExactDuration = false

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/custom_adserver_translation.json b/integrationExamples/longform/custom_adserver_translation.json new file mode 100644 index 00000000000..377fe9cdeda --- /dev/null +++ b/integrationExamples/longform/custom_adserver_translation.json @@ -0,0 +1,1180 @@ +{ + "mapping":{ + "IAB1-1":{ + "id":404, + "name":"Publishing" + }, + "IAB1-2":{ + "id":392, + "name":"Entertainment" + }, + "IAB1-5":{ + "id":419, + "name":"Filmed Entertainment" + }, + "IAB1-6":{ + "id":392, + "name":"Entertainment" + }, + "IAB1-7":{ + "id":392, + "name":"Entertainment" + }, + "IAB2-1":{ + "id":399, + "name":"Automotive" + }, + "IAB2-2":{ + "id":399, + "name":"Automotive" + }, + "IAB2-3":{ + "id":399, + "name":"Automotive" + }, + "IAB2-4":{ + "id":399, + "name":"Automotive" + }, + "IAB2-5":{ + "id":399, + "name":"Automotive" + }, + "IAB2-6":{ + "id":399, + "name":"Automotive" + }, + "IAB2-7":{ + "id":399, + "name":"Automotive" + }, + "IAB2-8":{ + "id":399, + "name":"Automotive" + }, + "IAB2-9":{ + "id":399, + "name":"Automotive" + }, + "IAB2-10":{ + "id":399, + "name":"Automotive" + }, + "IAB2-11":{ + "id":399, + "name":"Automotive" + }, + "IAB2-12":{ + "id":399, + "name":"Automotive" + }, + "IAB2-13":{ + "id":399, + "name":"Automotive" + }, + "IAB2-14":{ + "id":399, + "name":"Automotive" + }, + "IAB2-15":{ + "id":399, + "name":"Automotive" + }, + "IAB2-16":{ + "id":399, + "name":"Automotive" + }, + "IAB2-17":{ + "id":399, + "name":"Automotive" + }, + "IAB2-18":{ + "id":399, + "name":"Automotive" + }, + "IAB2-19":{ + "id":399, + "name":"Automotive" + }, + "IAB2-20":{ + "id":399, + "name":"Automotive" + }, + "IAB2-21":{ + "id":399, + "name":"Automotive" + }, + "IAB2-22":{ + "id":399, + "name":"Automotive" + }, + "IAB2-23":{ + "id":399, + "name":"Automotive" + }, + "IAB3-1":{ + "id":393, + "name":"Business Services" + }, + "IAB3-2":{ + "id":393, + "name":"Business Services" + }, + "IAB3-3":{ + "id":393, + "name":"Business Services" + }, + "IAB3-4":{ + "id":409, + "name":"Computing Product" + }, + "IAB3-5":{ + "id":393, + "name":"Business Services" + }, + "IAB3-6":{ + "id":393, + "name":"Business Services" + }, + "IAB3-7":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB3-8":{ + "id":393, + "name":"Business Services" + }, + "IAB3-9":{ + "id":393, + "name":"Business Services" + }, + "IAB3-10":{ + "id":393, + "name":"Business Services" + }, + "IAB3-11":{ + "id":393, + "name":"Business Services" + }, + "IAB3-12":{ + "id":393, + "name":"Business Services" + }, + "IAB4-1":{ + "id":393, + "name":"Business Services" + }, + "IAB4-2":{ + "id":405, + "name":"Educational Services" + }, + "IAB4-3":{ + "id":405, + "name":"Educational Services" + }, + "IAB4-4":{ + "id":393, + "name":"Business Services" + }, + "IAB4-5":{ + "id":393, + "name":"Business Services" + }, + "IAB4-6":{ + "id":393, + "name":"Business Services" + }, + "IAB4-7":{ + "id":406, + "name":"Health Care Services" + }, + "IAB4-8":{ + "id":405, + "name":"Educational Services" + }, + "IAB4-9":{ + "id":417, + "name":"Telecommunications" + }, + "IAB4-10":{ + "id":429, + "name":"Military" + }, + "IAB4-11":{ + "id":393, + "name":"Business Services" + }, + "IAB5-1":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-2":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-3":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-4":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-5":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-6":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-7":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-8":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-9":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-10":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-11":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-12":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-13":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-14":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-15":{ + "id":405, + "name":"Educational Services" + }, + "IAB7-1":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-2":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-3":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-4":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-5":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-6":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-7":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-8":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-9":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-10":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-11":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-12":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-13":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-14":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-15":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-16":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-17":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-18":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-19":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-20":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-21":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-22":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-23":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-24":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-25":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-26":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-27":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-28":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-29":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-30":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-31":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-32":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-33":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-34":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-35":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-36":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-37":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-38":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-39":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-40":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-41":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-42":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-43":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-44":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-45":{ + "id":406, + "name":"Health Care Services" + }, + "IAB8-1":{ + "id":394, + "name":"Food" + }, + "IAB8-2":{ + "id":394, + "name":"Food" + }, + "IAB8-3":{ + "id":394, + "name":"Food" + }, + "IAB8-4":{ + "id":394, + "name":"Food" + }, + "IAB8-5":{ + "id":400, + "name":"Beer/Wine/Liquor" + }, + "IAB8-6":{ + "id":401, + "name":"Beverages" + }, + "IAB8-7":{ + "id":394, + "name":"Food" + }, + "IAB8-8":{ + "id":394, + "name":"Food" + }, + "IAB8-9":{ + "id":407, + "name":"Restaurant/Fast Food" + }, + "IAB8-10":{ + "id":394, + "name":"Food" + }, + "IAB8-11":{ + "id":394, + "name":"Food" + }, + "IAB8-12":{ + "id":394, + "name":"Food" + }, + "IAB8-13":{ + "id":394, + "name":"Food" + }, + "IAB8-14":{ + "id":394, + "name":"Food" + }, + "IAB8-15":{ + "id":394, + "name":"Food" + }, + "IAB8-16":{ + "id":394, + "name":"Food" + }, + "IAB8-17":{ + "id":394, + "name":"Food" + }, + "IAB8-18":{ + "id":400, + "name":"Beer/Wine/Liquor" + }, + "IAB9-1":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-3":{ + "id":418, + "name":"Jewelry" + }, + "IAB9-5":{ + "id":413, + "name":"Gaming" + }, + "IAB9-6":{ + "id":412, + "name":"Household Products" + }, + "IAB9-9":{ + "id":426, + "name":"Tobacco" + }, + "IAB9-11":{ + "id":404, + "name":"Publishing" + }, + "IAB9-15":{ + "id":404, + "name":"Publishing" + }, + "IAB9-16":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-18":{ + "id":393, + "name":"Business Services" + }, + "IAB9-19":{ + "id":418, + "name":"Jewelry" + }, + "IAB9-23":{ + "id":424, + "name":"Photographic Equipment" + }, + "IAB9-24":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-25":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-30":{ + "id":392, + "name":"Entertainment" + }, + "IAB10-1":{ + "id":415, + "name":"Appliances" + }, + "IAB10-5":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB10-6":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB10-7":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB10-8":{ + "id":393, + "name":"Business Services" + }, + "IAB10-9":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB11-1":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-2":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-3":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-4":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-5":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB12-1":{ + "id":438, + "name":"News" + }, + "IAB12-2":{ + "id":438, + "name":"News" + }, + "IAB12-3":{ + "id":438, + "name":"News" + }, + "IAB13-1":{ + "id":393, + "name":"Business Services" + }, + "IAB13-2":{ + "id":393, + "name":"Business Services" + }, + "IAB13-3":{ + "id":438, + "name":"News" + }, + "IAB13-4":{ + "id":391, + "name":"Financial Services" + }, + "IAB13-5":{ + "id":393, + "name":"Business Services" + }, + "IAB13-6":{ + "id":436, + "name":"Insurance" + }, + "IAB13-7":{ + "id":393, + "name":"Business Services" + }, + "IAB13-8":{ + "id":393, + "name":"Business Services" + }, + "IAB13-9":{ + "id":393, + "name":"Business Services" + }, + "IAB13-10":{ + "id":393, + "name":"Business Services" + }, + "IAB13-11":{ + "id":393, + "name":"Business Services" + }, + "IAB13-12":{ + "id":393, + "name":"Business Services" + }, + "IAB16-1":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-2":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-3":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-4":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-5":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-6":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-7":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB17-1":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-2":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-3":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-4":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-5":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-6":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-7":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-8":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-9":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-10":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-11":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-12":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-13":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-14":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-15":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-16":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-17":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-18":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-19":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-20":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-21":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-22":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-23":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-24":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-25":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-26":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-27":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-28":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-29":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-30":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-31":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-32":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-33":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-34":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-35":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-36":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-37":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-38":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-39":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-40":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-41":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-42":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-43":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-44":{ + "id":425, + "name":"Professional Sports" + }, + "IAB18-1":{ + "id":411, + "name":"Cosmetics/Toiletries" + }, + "IAB18-2":{ + "id":397, + "name":"Apparel" + }, + "IAB18-3":{ + "id":397, + "name":"Apparel" + }, + "IAB18-4":{ + "id":418, + "name":"Jewelry" + }, + "IAB18-5":{ + "id":397, + "name":"Apparel" + }, + "IAB18-6":{ + "id":397, + "name":"Apparel" + }, + "IAB19-2":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-3":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-4":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-5":{ + "id":424, + "name":"Photographic Equipment" + }, + "IAB19-6":{ + "id":417, + "name":"Telecommunications" + }, + "IAB19-7":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-8":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-9":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-10":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-11":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-12":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-13":{ + "id":404, + "name":"Publishing" + }, + "IAB19-14":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-15":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-16":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-17":{ + "id":419, + "name":"Filmed Entertainment" + }, + "IAB19-18":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-19":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-20":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-21":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-22":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-23":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-24":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-25":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-26":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-27":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-28":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-29":{ + "id":392, + "name":"Entertainment" + }, + "IAB19-30":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-31":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-32":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-33":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-34":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-35":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-36":{ + "id":409, + "name":"Computing Product" + }, + "IAB20-1":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-2":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-3":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-4":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-5":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-6":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-7":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-8":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-9":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-10":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-11":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-12":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-13":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-14":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-15":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-16":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-17":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-18":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-19":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-20":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-21":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-22":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-23":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-24":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-25":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-26":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-27":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB21-1":{ + "id":416, + "name":"Real Estate" + }, + "IAB21-2":{ + "id":416, + "name":"Real Estate" + }, + "IAB21-3":{ + "id":416, + "name":"Real Estate" + }, + "IAB22-1":{ + "id":403, + "name":"Retail Stores/Chains" + }, + "IAB22-2":{ + "id":403, + "name":"Retail Stores/Chains" + }, + "IAB22-3":{ + "id":403, + "name":"Retail Stores/Chains" + } + } +} \ No newline at end of file diff --git a/integrationExamples/longform/longformTestUtils.js b/integrationExamples/longform/longformTestUtils.js new file mode 100644 index 00000000000..096be91bb77 --- /dev/null +++ b/integrationExamples/longform/longformTestUtils.js @@ -0,0 +1,66 @@ +var prebidTestUtils = prebidTestUtils || {}; + +function getIndustry(id) { + var mapping = window.localStorage.getItem('iabToFwMappingkey'); + if (mapping) { + try { + mapping = JSON.parse(mapping); + } catch (error) { + // + } + var industry; + mapping = mapping['mapping']; + for (var v in mapping) { + if (mapping[v]['id'] == id) { + industry = mapping[v]['name']; + break; + } + } + return industry; + } +} + +prebidTestUtils.loadKv = function (targetingArr) { + var div = document.getElementById('collapseThree').children[0]; + var html = ''; + Object.keys(targetingArr).forEach(function(adUnitCode) { + targetingArr[adUnitCode].forEach(function (targeting) { + Object.keys(targeting).forEach(function (key) { + html += '' + }); + }); + }); + html += '
    ' + key + '' + targeting[key] + '
    '; + div.innerHTML = html; +} + +prebidTestUtils.loadBids = function (targetingArr, brandCatExclusion) { + var div = document.getElementById('collapseTwo').children[0]; + var html = ''; + var index = 1; + Object.keys(targetingArr).forEach(function(adUnitCode) { + targetingArr[adUnitCode].forEach(function (targeting) { + Object.keys(targeting).forEach(function (key) { + if (key !== 'hb_cache_id') { + var result = targeting[key].split('_'); + html += ''; + html += ''; + html += ''; + if (brandCatExclusion) { + html += ''; + html += ''; + } else { + html += ''; + html += ''; + } + html += ''; + html += ''; + html += ''; + index++; + } + }); + }); + }); + html += '
    #CPMIndustryDurationStatusComm Break #
    ' + index + '' + result[0] + '' + getIndustry(result[1]) + '' + result[2] + '' + result[1] + '
    '; + div.innerHTML = html; +} diff --git a/integrationExamples/longform/longform_testpages_style.css b/integrationExamples/longform/longform_testpages_style.css new file mode 100644 index 00000000000..fe8105edcfa --- /dev/null +++ b/integrationExamples/longform/longform_testpages_style.css @@ -0,0 +1,34 @@ +#videoPlayer { + height: 480px; + width: 100%; + background-color: #000; +} + +.outer { + position: relative; +} + +#skip { + position: absolute; + right: 20px; + bottom: 20px; + display: none; + color: white; + background-color: black; + border: 1px solid gray; +} + +#adCount { + color: white; + position: absolute; + left: 20px; + bottom: 20px; + display: none; +} + +#showad { + color: white; + position: absolute; + left: 20px; + top: 20px; +} \ No newline at end of file diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 5d075b6929c..9aa416375b5 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -20,7 +20,10 @@ function newWebpackConfig(codeCoverage) { webpackConfig.module.rules.push({ enforce: 'post', exclude: /(node_modules)|(test)|(integrationExamples)|(build)|polyfill.js|(src\/adapters\/analytics\/ga.js)/, - loader: 'istanbul-instrumenter-loader', + use: { + loader: 'istanbul-instrumenter-loader', + options: { esModules: true } + }, test: /\.js$/ }) } @@ -34,7 +37,6 @@ function newPluginsArray(browserstack) { 'karma-es5-shim', 'karma-mocha', 'karma-chai', - 'karma-requirejs', 'karma-sinon', 'karma-sourcemap-loader', 'karma-spec-reporter', @@ -43,12 +45,12 @@ function newPluginsArray(browserstack) { ]; if (browserstack) { plugins.push('karma-browserstack-launcher'); - plugins.push('karma-firefox-launcher'); - plugins.push('karma-opera-launcher'); - plugins.push('karma-safari-launcher'); - plugins.push('karma-script-launcher'); - plugins.push('karma-ie-launcher'); } + plugins.push('karma-firefox-launcher'); + plugins.push('karma-opera-launcher'); + plugins.push('karma-safari-launcher'); + plugins.push('karma-script-launcher'); + plugins.push('karma-ie-launcher'); return plugins; } @@ -83,7 +85,8 @@ function setBrowsers(karmaConf, browserstack) { if (browserstack) { karmaConf.browserStack = { username: process.env.BROWSERSTACK_USERNAME, - accessKey: process.env.BROWSERSTACK_ACCESS_KEY + accessKey: process.env.BROWSERSTACK_ACCESS_KEY, + build: 'Prebidjs Unit Tests ' + new Date().toLocaleString() } if (process.env.TRAVIS) { karmaConf.browserStack.startTunnel = false; @@ -92,7 +95,19 @@ function setBrowsers(karmaConf, browserstack) { karmaConf.customLaunchers = require('./browsers.json') karmaConf.browsers = Object.keys(karmaConf.customLaunchers); } else { - karmaConf.browsers = ['ChromeHeadless']; + var isDocker = require('is-docker')(); + if (isDocker) { + karmaConf.customLaunchers = karmaConf.customLaunchers || {}; + karmaConf.customLaunchers.ChromeCustom = { + base: 'ChromeHeadless', + // We must disable the Chrome sandbox when running Chrome inside Docker (Chrome's sandbox needs + // more permissions than Docker allows by default) + flags: ['--no-sandbox'] + } + karmaConf.browsers = ['ChromeCustom']; + } else { + karmaConf.browsers = ['ChromeHeadless']; + } } } @@ -113,6 +128,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { webpack: webpackConfig, webpackMiddleware: { + stats: 'errors-only', noInfo: true }, // frameworks to use diff --git a/modules/.submodules.json b/modules/.submodules.json new file mode 100644 index 00000000000..c0e30037660 --- /dev/null +++ b/modules/.submodules.json @@ -0,0 +1,11 @@ +{ + "userId": [ + "digiTrustIdSystem", + "id5IdSystem", + "criteortusIdSystem" + ], + "adpod": [ + "freeWheelAdserverVideo", + "dfpAdServerVideo" + ] +} diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 15792240fdd..f53bf8b9d17 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -1,11 +1,15 @@ -import { userSync } from 'src/userSync' -const { registerBidder } = require('../src/adapters/bidderFactory'); -const { config } = require('../src/config'); +import { registerBidder } from '../src/adapters/bidderFactory'; +import { config } from '../src/config'; +import * as utils from '../src/utils'; const BIDDER_CODE = '33across'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; const SYNC_ENDPOINT = 'https://de.tynt.com/deb/v2?m=xch&rt=html'; +const adapterState = {}; + +const NON_MEASURABLE = 'nm'; + // All this assumes that only one bid is ever returned by ttx function _createBidResponse(response) { return { @@ -22,10 +26,56 @@ function _createBidResponse(response) { } } -// infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request -function _createServerRequest(bidRequest) { +function _isViewabilityMeasurable(element) { + return !_isIframe() && element !== null; +} + +function _getViewability(element, topWin, { w, h } = {}) { + return topWin.document.visibilityState === 'visible' + ? _getPercentInView(element, topWin, { w, h }) + : 0; +} + +function _mapAdUnitPathToElementId(adUnitCode) { + if (utils.isGptPubadsDefined()) { + const adSlots = googletag.pubads().getSlots(); + const isMatchingAdSlot = utils.isSlotMatchingAdUnitCode(adUnitCode); + + for (let i = 0; i < adSlots.length; i++) { + if (isMatchingAdSlot(adSlots[i])) { + const id = adSlots[i].getSlotElementId(); + + utils.logInfo(`[33Across Adapter] Map ad unit path to HTML element id: '${adUnitCode}' -> ${id}`); + + return id; + } + } + } + + utils.logWarn(`[33Across Adapter] Unable to locate element for ad unit code: '${adUnitCode}'`); + + return null; +} + +function _getAdSlotHTMLElement(adUnitCode) { + return document.getElementById(adUnitCode) || + document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); +} + +// Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request +// NOTE: At this point, TTX only accepts request for a single impression +function _createServerRequest(bidRequest, gdprConsent = {}) { const ttxRequest = {}; const params = bidRequest.params; + const element = _getAdSlotHTMLElement(bidRequest.adUnitCode); + const sizes = _transformSizes(bidRequest.sizes); + const minSize = _getMinSize(sizes); + + const viewabilityAmount = _isViewabilityMeasurable(element) + ? _getViewability(element, utils.getWindowTop(), minSize) + : NON_MEASURABLE; + + const contributeViewability = ViewabilityContributor(viewabilityAmount); /* * Infer data for the request payload @@ -33,28 +83,50 @@ function _createServerRequest(bidRequest) { ttxRequest.imp = []; ttxRequest.imp[0] = { banner: { - format: bidRequest.sizes.map(_getFormatSize) + format: sizes.map(size => Object.assign(size, {ext: {}})) }, ext: { ttx: { prod: params.productId } } - } + }; ttxRequest.site = { id: params.siteId }; + // Go ahead send the bidId in request to 33exchange so it's kept track of in the bid response and // therefore in ad targetting process ttxRequest.id = bidRequest.bidId; + + // Set GDPR related fields + ttxRequest.user = { + ext: { + consent: gdprConsent.consentString + } + }; + ttxRequest.regs = { + ext: { + gdpr: (gdprConsent.gdprApplies === true) ? 1 : 0 + } + }; + ttxRequest.ext = { + ttx: { + caller: [{ + 'name': 'prebidjs', + 'version': '$prebid.version$' + }] + } + }; + // Finally, set the openRTB 'test' param if this is to be a test bid if (params.test === 1) { ttxRequest.test = 1; } /* - * Now construt the full server request + * Now construct the full server request */ const options = { - contentType: 'application/json', + contentType: 'text/plain', withCredentials: true }; // Allow the ability to configure the HB endpoint for testing purposes. @@ -65,35 +137,143 @@ function _createServerRequest(bidRequest) { return { 'method': 'POST', 'url': url, - 'data': JSON.stringify(ttxRequest), + 'data': JSON.stringify(contributeViewability(ttxRequest)), 'options': options } } -function _getFormatSize(sizeArr) { +// Sync object will always be of type iframe for TTX +function _createSync({siteId, gdprConsent = {}}) { + const ttxSettings = config.getConfig('ttxSettings'); + const syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT; + + const {consentString, gdprApplies} = gdprConsent; + + const sync = { + type: 'iframe', + url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}` + }; + + if (typeof gdprApplies === 'boolean') { + sync.url += `&gdpr=${Number(gdprApplies)}`; + } + + return sync; +} + +function _getSize(size) { return { - w: sizeArr[0], - h: sizeArr[1], - ext: {} + w: parseInt(size[0], 10), + h: parseInt(size[1], 10) } } -// Register one sync per bid since each ad unit may potenitally be linked to a uniqe guid -// Sync type will always be 'iframe' for 33Across -function _registerUserSyncs(requestData) { - let ttxRequest; - try { - ttxRequest = JSON.parse(requestData); - } catch (err) { - // No point in trying to register sync since the requisite data cannot be parsed. - return; +function _getMinSize(sizes) { + return sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min); +} + +function _getBoundingBox(element, { w, h } = {}) { + let { width, height, left, top, right, bottom } = element.getBoundingClientRect(); + + if ((width === 0 || height === 0) && w && h) { + width = w; + height = h; + right = left + w; + bottom = top + h; } - const ttxSettings = config.getConfig('ttxSettings'); - let syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT; + return { width, height, left, top, right, bottom }; +} + +function _transformSizes(sizes) { + if (utils.isArray(sizes) && sizes.length === 2 && !utils.isArray(sizes[0])) { + return [_getSize(sizes)]; + } - syncUrl = `${syncUrl}&id=${ttxRequest.site.id}`; - userSync.registerSync('iframe', BIDDER_CODE, syncUrl); + return sizes.map(_getSize); +} + +function _getIntersectionOfRects(rects) { + const bbox = { + left: rects[0].left, + right: rects[0].right, + top: rects[0].top, + bottom: rects[0].bottom + }; + + for (let i = 1; i < rects.length; ++i) { + bbox.left = Math.max(bbox.left, rects[i].left); + bbox.right = Math.min(bbox.right, rects[i].right); + + if (bbox.left >= bbox.right) { + return null; + } + + bbox.top = Math.max(bbox.top, rects[i].top); + bbox.bottom = Math.min(bbox.bottom, rects[i].bottom); + + if (bbox.top >= bbox.bottom) { + return null; + } + } + + bbox.width = bbox.right - bbox.left; + bbox.height = bbox.bottom - bbox.top; + + return bbox; +} + +function _getPercentInView(element, topWin, { w, h } = {}) { + const elementBoundingBox = _getBoundingBox(element, { w, h }); + + // Obtain the intersection of the element and the viewport + const elementInViewBoundingBox = _getIntersectionOfRects([ { + left: 0, + top: 0, + right: topWin.innerWidth, + bottom: topWin.innerHeight + }, elementBoundingBox ]); + + let elementInViewArea, elementTotalArea; + + if (elementInViewBoundingBox !== null) { + // Some or all of the element is in view + elementInViewArea = elementInViewBoundingBox.width * elementInViewBoundingBox.height; + elementTotalArea = elementBoundingBox.width * elementBoundingBox.height; + + return ((elementInViewArea / elementTotalArea) * 100); + } + + // No overlap between element and the viewport; therefore, the element + // lies completely out of view + return 0; +} + +/** + * Viewability contribution to request.. + */ +function ViewabilityContributor(viewabilityAmount) { + function contributeViewability(ttxRequest) { + const req = Object.assign({}, ttxRequest); + const imp = req.imp = req.imp.map(impItem => Object.assign({}, impItem)); + const banner = imp[0].banner = Object.assign({}, imp[0].banner); + const ext = banner.ext = Object.assign({}, banner.ext); + const ttx = ext.ttx = Object.assign({}, ext.ttx); + + ttx.viewability = { amount: isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount) }; + + return req; + } + + return contributeViewability; +} + +function _isIframe() { + try { + return utils.getWindowSelf() !== utils.getWindowTop(); + } catch (e) { + return true; + } } function isBidRequestValid(bid) { @@ -108,18 +288,21 @@ function isBidRequestValid(bid) { return true; } -// NOTE: At this point, 33exchange only accepts request for a single impression -function buildRequests(bidRequests) { - return bidRequests.map(_createServerRequest); +// NOTE: With regards to gdrp consent data, +// - the server independently infers gdpr applicability therefore, setting the default value to false +function buildRequests(bidRequests, bidderRequest) { + const gdprConsent = Object.assign({ + consentString: undefined, + gdprApplies: false + }, bidderRequest && bidderRequest.gdprConsent); + + adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(utils.uniques); + + return bidRequests.map(req => _createServerRequest(req, gdprConsent)); } // NOTE: At this point, the response from 33exchange will only ever contain one bid i.e. the highest bid function interpretResponse(serverResponse, bidRequest) { - // Register user sync first - if (bidRequest && bidRequest.data) { - _registerUserSyncs(bidRequest.data); - } - const bidResponses = []; // If there are bids, look at the first bid of the first seatbid (see NOTE above for assumption about ttx) @@ -130,13 +313,23 @@ function interpretResponse(serverResponse, bidRequest) { return bidResponses; } -const spec = { +// Register one sync per unique guid so long as iframe is enable +// Else no syncs +// For logic on how we handle gdpr data see _createSyncs and module's unit tests +// '33acrossBidAdapter#getUserSyncs' +function getUserSyncs(syncOptions, responses, gdprConsent) { + return (syncOptions.iframeEnabled) ? adapterState.uniqueSiteIds.map((siteId) => _createSync({gdprConsent, siteId})) : ([]); +} + +export const spec = { + NON_MEASURABLE, + code: BIDDER_CODE, + isBidRequestValid, buildRequests, - interpretResponse -} + interpretResponse, + getUserSyncs, +}; registerBidder(spec); - -module.exports = spec; diff --git a/modules/33acrossBidAdapter.md b/modules/33acrossBidAdapter.md index c4eb319c157..c313f3b6e0b 100644 --- a/modules/33acrossBidAdapter.md +++ b/modules/33acrossBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: 33Across Bid Adapter Module Type: Bidder Adapter -Maintainer: aparna.hegde@33across.com +Maintainer: headerbidding@33across.com ``` # Description @@ -16,7 +16,7 @@ Connects to 33Across's exchange for bids. ``` var adUnits = [ { - code: '33across-hb-ad-123456-1', + code: '33across-hb-ad-123456-1', // ad slot HTML element ID sizes: [ [300, 250], [728, 90] @@ -24,72 +24,9 @@ var adUnits = [ bids: [{ bidder: '33across', params: { - siteId: 'pub1234', - productId: 'infeed' + siteId: 'cxBE0qjUir6iopaKkGJozW', + productId: 'siab' } }] } ``` - -# Ad Unit and Setup: For Testing -In order to receive bids please map localhost to (any) test domain. - -``` -<--! Prebid Config section > - - + `; -}; +} /** * Returns iframe document in a browser agnostic way - * @param {object} iframe reference - * @return {object} iframe `document` reference + * @param {Object} iframe reference + * @return {Object} iframe `document` reference */ -exports.getIframeDocument = function (iframe) { +export function getIframeDocument(iframe) { if (!iframe) { return; } @@ -658,24 +740,24 @@ exports.getIframeDocument = function (iframe) { doc = iframe.contentDocument; } } catch (e) { - this.logError('Cannot get iframe document', e); + internal.logError('Cannot get iframe document', e); } return doc; -}; +} -exports.getValueString = function(param, val, defaultValue) { +export function getValueString(param, val, defaultValue) { if (val === undefined || val === null) { return defaultValue; } - if (this.isStr(val)) { + if (isStr(val)) { return val; } - if (this.isNumber(val)) { + if (isNumber(val)) { return val.toString(); } - this.logWarn('Unsuported type for param: ' + param + ' required type: String'); -}; + internal.logWarn('Unsuported type for param: ' + param + ' required type: String'); +} export function uniques(value, index, arry) { return arry.indexOf(value) === index; @@ -685,8 +767,19 @@ export function flatten(a, b) { return a.concat(b); } -export function getBidRequest(id, bidsRequested) { - return find(bidsRequested.map(bidSet => find(bidSet.bids, bid => bid.bidId === id)), bid => bid); +export function getBidRequest(id, bidderRequests) { + if (!id) { + return; + } + let bidRequest; + bidderRequests.some(bidderRequest => { + let result = find(bidderRequest.bids, bid => ['bidId', 'adId', 'bid_id'].some(type => bid[type] === id)); + if (result) { + bidRequest = result; + } + return result; + }); + return bidRequest; } export function getKeys(obj) { @@ -697,6 +790,19 @@ export function getValue(obj, key) { return obj[key]; } +/** + * Get the key of an object for a given value + */ +export function getKeyByValue(obj, value) { + for (let prop in obj) { + if (obj.hasOwnProperty(prop)) { + if (obj[prop] === value) { + return prop; + } + } + } +} + export function getBidderCodes(adUnits = $$PREBID_GLOBAL$$.adUnits) { // this could memoize adUnits return adUnits.map(unit => unit.bids.map(bid => bid.bidder) @@ -704,17 +810,29 @@ export function getBidderCodes(adUnits = $$PREBID_GLOBAL$$.adUnits) { } export function isGptPubadsDefined() { - if (window.googletag && exports.isFn(window.googletag.pubads) && exports.isFn(window.googletag.pubads().getSlots)) { + if (window.googletag && isFn(window.googletag.pubads) && isFn(window.googletag.pubads().getSlots)) { return true; } } -export function getHighestCpm(previous, current) { - if (previous.cpm === current.cpm) { - return previous.timeToRespond > current.timeToRespond ? current : previous; - } +// This function will get highest cpm value bid, in case of tie it will return the bid with lowest timeToRespond +export const getHighestCpm = getHighestCpmCallback('timeToRespond', (previous, current) => previous > current); + +// This function will get the oldest hightest cpm value bid, in case of tie it will return the bid which came in first +// Use case for tie: https://github.com/prebid/Prebid.js/issues/2448 +export const getOldestHighestCpmBid = getHighestCpmCallback('responseTimestamp', (previous, current) => previous > current); - return previous.cpm < current.cpm ? current : previous; +// This function will get the latest hightest cpm value bid, in case of tie it will return the bid which came in last +// Use case for tie: https://github.com/prebid/Prebid.js/issues/2539 +export const getLatestHighestCpmBid = getHighestCpmCallback('responseTimestamp', (previous, current) => previous < current); + +function getHighestCpmCallback(useTieBreakerProperty, tieBreakerCallback) { + return (previous, current) => { + if (previous.cpm === current.cpm) { + return tieBreakerCallback(previous[useTieBreakerProperty], current[useTieBreakerProperty]) ? current : previous; + } + return previous.cpm < current.cpm ? current : previous; + } } /** @@ -763,7 +881,7 @@ export function deepClone(obj) { export function inIframe() { try { - return exports.getWindowSelf() !== exports.getWindowTop(); + return internal.getWindowSelf() !== internal.getWindowTop(); } catch (e) { return true; } @@ -788,13 +906,34 @@ export function checkCookieSupport() { } } export function cookiesAreEnabled() { - if (exports.checkCookieSupport()) { + if (internal.checkCookieSupport()) { return true; } window.document.cookie = 'prebid.cookieTest'; return window.document.cookie.indexOf('prebid.cookieTest') != -1; } +export function getCookie(name) { + let m = window.document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]*)\\s*(;|$)'); + return m ? decodeURIComponent(m[2]) : null; +} + +export function setCookie(key, value, expires, sameSite) { + document.cookie = `${key}=${encodeURIComponent(value)}${(expires !== '') ? `; expires=${expires}` : ''}; path=/${sameSite ? `; SameSite=${sameSite}` : ''}`; +} + +/** + * @returns {boolean} + */ +export function localStorageIsEnabled () { + try { + localStorage.setItem('prebid.cookieTest', '1'); + return localStorage.getItem('prebid.cookieTest') === '1'; + } catch (error) { + return false; + } +} + /** * Given a function, return a function which only executes the original after * it's been called numRequiredCalls times. @@ -825,7 +964,7 @@ export function delayExecution(func, numRequiredCalls) { * @export * @param {array} xs * @param {string} key - * @returns {${key_value}: ${groupByArray}, key_value: {groupByArray}} + * @returns {Object} {${key_value}: ${groupByArray}, key_value: {groupByArray}} */ export function groupBy(xs, key) { return xs.reduce(function(rv, x) { @@ -834,29 +973,9 @@ export function groupBy(xs, key) { }, {}); } -/** - * deepAccess utility function useful for doing safe access (will not throw exceptions) of deep object paths. - * @param {object} obj The object containing the values you would like to access. - * @param {string|number} path Object path to the value you would like to access. Non-strings are coerced to strings. - * @returns {*} The value found at the specified object path, or undefined if path is not found. - */ -export function deepAccess(obj, path) { - if (!obj) { - return; - } - path = String(path).split('.'); - for (let i = 0; i < path.length; i++) { - obj = obj[path[i]]; - if (typeof obj === 'undefined') { - return; - } - } - return obj; -} - /** * Returns content for a friendly iframe to execute a URL in script tag - * @param {url} URL to be executed in a script tag in a friendly iframe + * @param {string} url URL to be executed in a script tag in a friendly iframe * and are macros left to be replaced if required */ export function createContentToExecuteExtScriptInFriendlyFrame(url) { @@ -870,9 +989,9 @@ export function createContentToExecuteExtScriptInFriendlyFrame(url) { /** * Build an object consisting of only defined parameters to avoid creating an * object with defined keys and undefined values. - * @param {object} object The object to pick defined params out of + * @param {Object} object The object to pick defined params out of * @param {string[]} params An array of strings representing properties to look for in the object - * @returns {object} An object containing all the specified values that are defined + * @returns {Object} An object containing all the specified values that are defined */ export function getDefinedParams(object, params) { return params @@ -894,7 +1013,7 @@ export function getDefinedParams(object, params) { */ export function isValidMediaTypes(mediaTypes) { const SUPPORTED_MEDIA_TYPES = ['banner', 'native', 'video']; - const SUPPORTED_STREAM_TYPES = ['instream', 'outstream']; + const SUPPORTED_STREAM_TYPES = ['instream', 'outstream', 'adpod']; const types = Object.keys(mediaTypes); @@ -917,8 +1036,8 @@ export function getBidderRequest(bidRequests, bidder, adUnitCode) { } /** * Returns user configured bidder params from adunit - * @param {object} adunits - * @param {string} adunit code + * @param {Object} adUnits + * @param {string} adUnitCode code * @param {string} bidder code * @return {Array} user configured param for the given bidder adunit configuration */ @@ -953,7 +1072,7 @@ const compareCodeAndSlot = (slot, adUnitCode) => slot.getAdUnitPath() === adUnit /** * Returns filter function to match adUnitCode in slot - * @param {object} slot GoogleTag slot + * @param {Object} slot GoogleTag slot * @return {function} filter function */ export function isAdUnitCodeMatchingSlot(slot) { @@ -992,18 +1111,9 @@ export function unsupportedBidderMessage(adUnit, bidder) { * @return {Object} object */ export function deletePropertyFromObject(object, prop) { - let result = Object.assign({}, object) + let result = Object.assign({}, object); delete result[prop]; - return result -} - -/** - * Delete requestId from external bid object. - * @param {Object} bid - * @return {Object} bid - */ -export function removeRequestId(bid) { - return exports.deletePropertyFromObject(bid, 'requestId'); + return result; } /** @@ -1026,3 +1136,201 @@ export function isInteger(value) { export function convertCamelToUnderscore(value) { return value.replace(/(?:^|\.?)([A-Z])/g, function (x, y) { return '_' + y.toLowerCase() }).replace(/^_/, ''); } + +/** + * Returns a new object with undefined properties removed from given object + * @param obj the object to clean + */ +export function cleanObj(obj) { + return Object.keys(obj).reduce((newObj, key) => { + if (typeof obj[key] !== 'undefined') { + newObj[key] = obj[key]; + } + return newObj; + }, {}) +} + +/** + * Create a new object with selected properties. Also allows property renaming and transform functions. + * @param obj the original object + * @param properties An array of desired properties + */ +export function pick(obj, properties) { + if (typeof obj !== 'object') { + return {}; + } + return properties.reduce((newObj, prop, i) => { + if (typeof prop === 'function') { + return newObj; + } + + let newProp = prop; + let match = prop.match(/^(.+?)\sas\s(.+?)$/i); + + if (match) { + prop = match[1]; + newProp = match[2]; + } + + let value = obj[prop]; + if (typeof properties[i + 1] === 'function') { + value = properties[i + 1](value, newObj); + } + if (typeof value !== 'undefined') { + newObj[newProp] = value; + } + + return newObj; + }, {}); +} + +/** + * Converts an object of arrays (either strings or numbers) into an array of objects containing key and value properties + * normally read from bidder params + * eg { foo: ['bar', 'baz'], fizz: ['buzz'] } + * becomes [{ key: 'foo', value: ['bar', 'baz']}, {key: 'fizz', value: ['buzz']}] + * @param {Object} keywords object of arrays representing keyvalue pairs + * @param {string} paramName name of parent object (eg 'keywords') containing keyword data, used in error handling + */ +export function transformBidderParamKeywords(keywords, paramName = 'keywords') { + let arrs = []; + + _each(keywords, (v, k) => { + if (isArray(v)) { + let values = []; + _each(v, (val) => { + val = getValueString(paramName + '.' + k, val); + if (val || val === '') { values.push(val); } + }); + v = values; + } else { + v = getValueString(paramName + '.' + k, v); + if (isStr(v)) { + v = [v]; + } else { + return; + } // unsuported types - don't send a key + } + arrs.push({key: k, value: v}); + }); + + return arrs; +} + +/** + * Try to convert a value to a type. + * If it can't be done, the value will be returned. + * + * @param {string} typeToConvert The target type. e.g. "string", "number", etc. + * @param {*} value The value to be converted into typeToConvert. + */ +function tryConvertType(typeToConvert, value) { + if (typeToConvert === 'string') { + return value && value.toString(); + } else if (typeToConvert === 'number') { + return Number(value); + } else { + return value; + } +} + +export function convertTypes(types, params) { + Object.keys(types).forEach(key => { + if (params[key]) { + if (isFn(types[key])) { + params[key] = types[key](params[key]); + } else { + params[key] = tryConvertType(types[key], params[key]); + } + + // don't send invalid values + if (isNaN(params[key])) { + delete params.key; + } + } + }); + return params; +} + +export function setDataInLocalStorage(key, value) { + if (hasLocalStorage()) { + window.localStorage.setItem(key, value); + } +} + +export function getDataFromLocalStorage(key) { + if (hasLocalStorage()) { + return window.localStorage.getItem(key); + } +} + +export function hasLocalStorage() { + try { + return !!window.localStorage; + } catch (e) { + logError('Local storage api disabled'); + } +} + +export function isArrayOfNums(val, size) { + return (isArray(val)) && ((size) ? val.length === size : true) && (val.every(v => isInteger(v))); +} + +/** + * Creates an array of n length and fills each item with the given value + */ +export function fill(value, length) { + let newArray = []; + + for (let i = 0; i < length; i++) { + let valueToPush = isPlainObject(value) ? deepClone(value) : value; + newArray.push(valueToPush); + } + + return newArray; +} + +/** + * http://npm.im/chunk + * Returns an array with *size* chunks from given array + * + * Example: + * ['a', 'b', 'c', 'd', 'e'] chunked by 2 => + * [['a', 'b'], ['c', 'd'], ['e']] + */ +export function chunk(array, size) { + let newArray = []; + + for (let i = 0; i < Math.ceil(array.length / size); i++) { + let start = i * size; + let end = start + size; + newArray.push(array.slice(start, end)); + } + + return newArray; +} + +export function getMinValueFromArray(array) { + return Math.min(...array); +} + +export function getMaxValueFromArray(array) { + return Math.max(...array); +} + +/** + * This function will create compare function to sort on object property + * @param {string} property + * @returns {function} compare function to be used in sorting + */ +export function compareOn(property) { + return function compare(a, b) { + if (a[property] < b[property]) { + return 1; + } + if (a[property] > b[property]) { + return -1; + } + return 0; + } +} diff --git a/src/video.js b/src/video.js index 8e0775a6d62..57f44a76764 100644 --- a/src/video.js +++ b/src/video.js @@ -1,10 +1,12 @@ -import { videoAdapters } from './adaptermanager'; +import adapterManager from './adapterManager'; import { getBidRequest, deepAccess, logError } from './utils'; import { config } from '../src/config'; import includes from 'core-js/library/fn/array/includes'; +import { hook } from './hook'; const VIDEO_MEDIA_TYPE = 'video'; -const OUTSTREAM = 'outstream'; +export const OUTSTREAM = 'outstream'; +export const INSTREAM = 'instream'; /** * Helper functions for working with video-enabled adUnits @@ -14,7 +16,7 @@ export const videoAdUnit = adUnit => { const mediaTypes = deepAccess(adUnit, 'mediaTypes.video'); return mediaType || mediaTypes; }; -export const videoBidder = bid => includes(videoAdapters, bid.bidder); +export const videoBidder = bid => includes(adapterManager.videoAdapters, bid.bidder); export const hasNonVideoBidder = adUnit => adUnit.bids.filter(bid => !videoBidder(bid)).length; @@ -30,7 +32,7 @@ export const hasNonVideoBidder = adUnit => * @return {Boolean} If object is valid */ export function isValidVideoBid(bid, bidRequests) { - const bidRequest = getBidRequest(bid.adId, bidRequests); + const bidRequest = getBidRequest(bid.requestId, bidRequests); const videoMediaType = bidRequest && deepAccess(bidRequest, 'mediaTypes.video'); @@ -38,12 +40,16 @@ export function isValidVideoBid(bid, bidRequests) { // if context not defined assume default 'instream' for video bids // instream bids require a vast url or vast xml content + return checkVideoBidSetup(bid, bidRequest, videoMediaType, context); +} + +export const checkVideoBidSetup = hook('sync', function(bid, bidRequest, videoMediaType, context) { if (!bidRequest || (videoMediaType && context !== OUTSTREAM)) { // xml-only video bids require a prebid cache url if (!config.getConfig('cache.url') && bid.vastXml && !bid.vastUrl) { logError(` This bid contains only vastXml and will not work when a prebid cache url is not specified. - Try enabling prebid cache with pbjs.setConfig({ cache: {url: "..."} }); + Try enabling prebid cache with $$PREBID_GLOBAL$$.setConfig({ cache: {url: "..."} }); `); return false; } @@ -57,4 +63,4 @@ export function isValidVideoBid(bid, bidRequests) { } return true; -} +}, 'checkVideoBidSetup'); diff --git a/src/videoCache.js b/src/videoCache.js index cec2a3ec864..06847012a6e 100644 --- a/src/videoCache.js +++ b/src/videoCache.js @@ -60,10 +60,17 @@ function wrapURI(uri, impUrl) { */ function toStorageRequest(bid) { const vastValue = bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastImpUrl); - return { + let payload = { type: 'xml', - value: vastValue + value: vastValue, + ttlseconds: Number(bid.ttl) }; + + if (typeof bid.customCacheKey === 'string' && bid.customCacheKey !== '') { + payload.key = bid.customCacheKey; + } + + return payload; } /** @@ -113,7 +120,7 @@ function shimStorageCallback(done) { * * @param {CacheableBid[]} bids A list of bid objects which should be cached. * @param {videoCacheStoreCallback} [done] An optional callback which should be executed after - * the data has been stored in the cache. + * the data has been stored in the cache. */ export function store(bids, done) { const requestData = { diff --git a/test/fixtures/allAdapters.js b/test/fixtures/allAdapters.js deleted file mode 100644 index e2baa12cd10..00000000000 --- a/test/fixtures/allAdapters.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.getAllAdaptersString = function () { - return `var AardvarkAdapter = require(\'./adapters/aardvark.js\');\n exports.registerBidAdapter(new AardvarkAdapter(), \'aardvark\');\nvar AdbladeAdapter = require(\'./adapters/adblade.js\');\n exports.registerBidAdapter(new AdbladeAdapter(), \'adblade\');\nvar AdbutlerAdapter = require(\'./adapters/adbutler.js\');\n exports.registerBidAdapter(new AdbutlerAdapter(), \'adbutler\');\nvar AdequantAdapter = require(\'./adapters/adequant.js\');\n exports.registerBidAdapter(new AdequantAdapter(), \'adequant\');\nvar AdformAdapter = require(\'./adapters/adform.js\');\n exports.registerBidAdapter(new AdformAdapter(), \'adform\');\nvar AdmediaAdapter = require(\'./adapters/admedia.js\');\n exports.registerBidAdapter(new AdmediaAdapter(), \'admedia\');\nvar AolAdapter = require(\'./adapters/aol.js\');\n exports.registerBidAdapter(new AolAdapter(), \'aol\');\nvar AppnexusAdapter = require(\'./adapters/appnexus.js\');\n exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\nvar AppnexusAstAdapter = require(\'./adapters/appnexusAst.js\');\n exports.registerBidAdapter(new AppnexusAstAdapter(), \'appnexusAst\');\nvar GetintentAdapter = require(\'./adapters/getintent.js\');\n exports.registerBidAdapter(new GetintentAdapter(), \'getintent\');\nvar HiromediaAdapter = require(\'./adapters/hiromedia.js\');\n exports.registerBidAdapter(new HiromediaAdapter(), \'hiromedia\');\nvar IndexExchangeAdapter = require(\'./adapters/indexExchange.js\');\n exports.registerBidAdapter(new IndexExchangeAdapter(), \'indexExchange\');\nvar KruxlinkAdapter = require(\'./adapters/kruxlink.js\');\n exports.registerBidAdapter(new KruxlinkAdapter(), \'kruxlink\');\nvar KomoonaAdapter = require(\'./adapters/komoona.js\');\n exports.registerBidAdapter(new KomoonaAdapter(), \'komoona\');\nvar OpenxAdapter = require(\'./adapters/openx.js\');\n exports.registerBidAdapter(new OpenxAdapter(), \'openx\');\nvar PiximediaAdapter = require(\'./adapters/piximedia.js\');\n exports.registerBidAdapter(new PiximediaAdapter(), \'piximedia\');\nvar PubmaticAdapter = require(\'./adapters/pubmatic.js\');\n exports.registerBidAdapter(new PubmaticAdapter(), \'pubmatic\');\nvar PulsepointAdapter = require(\'./adapters/pulsepoint.js\');\n exports.registerBidAdapter(new PulsepointAdapter(), \'pulsepoint\');\nvar RubiconAdapter = require(\'./adapters/rubicon.js\');\n exports.registerBidAdapter(new RubiconAdapter(), \'rubicon\');\nvar SonobiAdapter = require(\'./adapters/sonobi.js\');\n exports.registerBidAdapter(new SonobiAdapter(), \'sonobi\');\nvar SovrnAdapter = require(\'./adapters/sovrn.js\');\n exports.registerBidAdapter(new SovrnAdapter(), \'sovrn\');\nvar SpringserveAdapter = require(\'./adapters/springserve.js\');\n exports.registerBidAdapter(new SpringserveAdapter(), \'springserve\');\nvar ThoughtleadrAdapter = require('./adapters/thoughtleadr.js');\n exports.registerBidAdapter(new ThoughtleadrAdapter(), 'thoughtleadr');\nvar TripleliftAdapter = require(\'./adapters/triplelift.js\');\n exports.registerBidAdapter(new TripleliftAdapter(), \'triplelift\');\nvar TwengaAdapter = require(\'./adapters/twenga.js\');\n exports.registerBidAdapter(new TwengaAdapter(), \'twenga\');\nvar YieldbotAdapter = require(\'./adapters/yieldbot.js\');\n exports.registerBidAdapter(new YieldbotAdapter(), \'yieldbot\');\nvar NginadAdapter = require(\'./adapters/nginad.js\');\n exports.registerBidAdapter(new NginadAdapter(), \'nginad\');\nvar BrightcomAdapter = require(\'./adapters/brightcom.js\');\n exports.registerBidAdapter(new BrightcomAdapter(), \'brightcom\');\nvar WideorbitAdapter = require(\'./adapters/wideorbit.js\');\n exports.registerBidAdapter(new WideorbitAdapter(), \'wideorbit\');\nvar JcmAdapter = require(\'./adapters/jcm.js\');\n exports.registerBidAdapter(new JcmAdapter(), \'jcm\');\nvar UnderdogmediaAdapter = require(\'./adapters/underdogmedia.js\');\n exports.registerBidAdapter(new UnderdogmediaAdapter(), \'underdogmedia\');\nvar MemeglobalAdapter = require(\'./adapters/memeglobal.js\');\n exports.registerBidAdapter(new MemeglobalAdapter(), \'memeglobal\');\nvar CentroAdapter = require(\'./adapters/centro.js\');\n exports.registerBidAdapter(new CentroAdapter(), \'centro\');\nvar RoxotAdapter = require(\'./adapters/roxot.js\');\n exports.registerBidAdapter(new RoxotAdapter(), \'roxot\');\nexports.aliasBidAdapter(\'appnexus\',\'brealtime\');\nexports.aliasBidAdapter(\'appnexus\',\'pagescience\');\nexports.aliasBidAdapter(\'appnexus\',\'defymedia\');\nexports.videoAdapters = ["appnexusAst"];`; -}; diff --git a/test/fixtures/cpmInputsOutputs.json b/test/fixtures/cpmInputsOutputs.json index b24b0c00c8d..7569eef6e97 100644 --- a/test/fixtures/cpmInputsOutputs.json +++ b/test/fixtures/cpmInputsOutputs.json @@ -1,4 +1,4 @@ { - "cpmInputs": ["17.638", "19.836", "11.501", "14.384", "23.224", "21.279", "8.886", "16.555", "10.579", "1.331", "1.998", "14.988", "14.864", "10.369", "0.262", "5.269", "6.874", "5.598", "7.191", "15.218", "10.958", "4.420", "17.749", "23.808", "12.353", "21.726", "1.562", "18.085", "1.184", "15.470", "13.841", "17.966", "22.150", "9.088", "13.613", "18.384", "13.690", "23.639", "5.085", "5.779", "11.456", "0.315", "18.557", "20.813", "18.813", "10.202", "10.143", "2.483", "16.147", "2.909", "0.652"], - "priceStringOutputs": [{"low":"5.00","med":"17.60","high":"17.63","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"19.80","high":"19.83","auto":"19.50","dense":"19.50","custom":""},{"low":"5.00","med":"11.50","high":"11.50","auto":"11.50","dense":"11.50","custom":""},{"low":"5.00","med":"14.30","high":"14.38","auto":"14.00","dense":"14.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"8.80","high":"8.88","auto":"8.80","dense":"8.50","custom":""},{"low":"5.00","med":"16.50","high":"16.55","auto":"16.50","dense":"16.50","custom":""},{"low":"5.00","med":"10.50","high":"10.57","auto":"10.50","dense":"10.50","custom":""},{"low":"1.00","med":"1.30","high":"1.33","auto":"1.30","dense":"1.33","custom":""},{"low":"1.50","med":"1.90","high":"1.99","auto":"1.95","dense":"1.99","custom":""},{"low":"5.00","med":"14.90","high":"14.98","auto":"14.50","dense":"14.50","custom":""},{"low":"5.00","med":"14.80","high":"14.86","auto":"14.50","dense":"14.50","custom":""},{"low":"5.00","med":"10.30","high":"10.36","auto":"10.00","dense":"10.00","custom":""},{"low":"0.00","med":"0.20","high":"0.26","auto":"0.25","dense":"0.26","custom":""},{"low":"5.00","med":"5.20","high":"5.26","auto":"5.20","dense":"5.25","custom":""},{"low":"5.00","med":"6.80","high":"6.87","auto":"6.80","dense":"6.85","custom":""},{"low":"5.00","med":"5.50","high":"5.59","auto":"5.50","dense":"5.55","custom":""},{"low":"5.00","med":"7.10","high":"7.19","auto":"7.10","dense":"7.15","custom":""},{"low":"5.00","med":"15.20","high":"15.21","auto":"15.00","dense":"15.00","custom":""},{"low":"5.00","med":"10.90","high":"10.95","auto":"10.50","dense":"10.50","custom":""},{"low":"4.00","med":"4.40","high":"4.42","auto":"4.40","dense":"4.40","custom":""},{"low":"5.00","med":"17.70","high":"17.74","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"12.30","high":"12.35","auto":"12.00","dense":"12.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"1.50","med":"1.50","high":"1.56","auto":"1.55","dense":"1.56","custom":""},{"low":"5.00","med":"18.00","high":"18.08","auto":"18.00","dense":"18.00","custom":""},{"low":"1.00","med":"1.10","high":"1.18","auto":"1.15","dense":"1.18","custom":""},{"low":"5.00","med":"15.40","high":"15.47","auto":"15.00","dense":"15.00","custom":""},{"low":"5.00","med":"13.80","high":"13.84","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"17.90","high":"17.96","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"9.00","high":"9.08","auto":"9.00","dense":"9.00","custom":""},{"low":"5.00","med":"13.60","high":"13.61","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"18.30","high":"18.38","auto":"18.00","dense":"18.00","custom":""},{"low":"5.00","med":"13.60","high":"13.69","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"5.00","high":"5.08","auto":"5.00","dense":"5.05","custom":""},{"low":"5.00","med":"5.70","high":"5.77","auto":"5.70","dense":"5.75","custom":""},{"low":"5.00","med":"11.40","high":"11.45","auto":"11.00","dense":"11.00","custom":""},{"low":"0.00","med":"0.30","high":"0.31","auto":"0.30","dense":"0.31","custom":""},{"low":"5.00","med":"18.50","high":"18.55","auto":"18.50","dense":"18.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"18.80","high":"18.81","auto":"18.50","dense":"18.50","custom":""},{"low":"5.00","med":"10.20","high":"10.20","auto":"10.00","dense":"10.00","custom":""},{"low":"5.00","med":"10.10","high":"10.14","auto":"10.00","dense":"10.00","custom":""},{"low":"2.00","med":"2.40","high":"2.48","auto":"2.45","dense":"2.48","custom":""},{"low":"5.00","med":"16.10","high":"16.14","auto":"16.00","dense":"16.00","custom":""},{"low":"2.50","med":"2.90","high":"2.90","auto":"2.90","dense":"2.90","custom":""},{"low":"0.50","med":"0.60","high":"0.65","auto":"0.65","dense":"0.65","custom":""}] + "cpmInputs": ["17.638", "19.836", "11.501", "14.384", "23.224", "21.279", "8.886", "16.555", "10.579", "1.331", "1.998", "14.988", "14.864", "10.369", "0.262", "5.269", "6.874", "5.598", "7.191", "15.218", "10.958", "4.420", "17.749", "23.808", "12.353", "21.726", "1.562", "18.085", "1.184", "15.470", "13.841", "17.966", "22.150", "9.088", "13.613", "18.384", "13.690", "23.639", "5.085", "5.779", "11.456", "0.315", "18.557", "20.813", "18.813", "10.202", "10.143", "2.483", "16.147", "2.909", "0.652","2.21","3.15","4.89","2.98","2.99","4.01","4.68","4.69"], + "priceStringOutputs": [{"low":"5.00","med":"17.60","high":"17.63","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"19.80","high":"19.83","auto":"19.50","dense":"19.50","custom":""},{"low":"5.00","med":"11.50","high":"11.50","auto":"11.50","dense":"11.50","custom":""},{"low":"5.00","med":"14.30","high":"14.38","auto":"14.00","dense":"14.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"8.80","high":"8.88","auto":"8.80","dense":"8.50","custom":""},{"low":"5.00","med":"16.50","high":"16.55","auto":"16.50","dense":"16.50","custom":""},{"low":"5.00","med":"10.50","high":"10.57","auto":"10.50","dense":"10.50","custom":""},{"low":"1.00","med":"1.30","high":"1.33","auto":"1.30","dense":"1.33","custom":""},{"low":"1.50","med":"1.90","high":"1.99","auto":"1.95","dense":"1.99","custom":""},{"low":"5.00","med":"14.90","high":"14.98","auto":"14.50","dense":"14.50","custom":""},{"low":"5.00","med":"14.80","high":"14.86","auto":"14.50","dense":"14.50","custom":""},{"low":"5.00","med":"10.30","high":"10.36","auto":"10.00","dense":"10.00","custom":""},{"low":"0.00","med":"0.20","high":"0.26","auto":"0.25","dense":"0.26","custom":""},{"low":"5.00","med":"5.20","high":"5.26","auto":"5.20","dense":"5.25","custom":""},{"low":"5.00","med":"6.80","high":"6.87","auto":"6.80","dense":"6.85","custom":""},{"low":"5.00","med":"5.50","high":"5.59","auto":"5.50","dense":"5.55","custom":""},{"low":"5.00","med":"7.10","high":"7.19","auto":"7.10","dense":"7.15","custom":""},{"low":"5.00","med":"15.20","high":"15.21","auto":"15.00","dense":"15.00","custom":""},{"low":"5.00","med":"10.90","high":"10.95","auto":"10.50","dense":"10.50","custom":""},{"low":"4.00","med":"4.40","high":"4.42","auto":"4.40","dense":"4.40","custom":""},{"low":"5.00","med":"17.70","high":"17.74","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"12.30","high":"12.35","auto":"12.00","dense":"12.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"1.50","med":"1.50","high":"1.56","auto":"1.55","dense":"1.56","custom":""},{"low":"5.00","med":"18.00","high":"18.08","auto":"18.00","dense":"18.00","custom":""},{"low":"1.00","med":"1.10","high":"1.18","auto":"1.15","dense":"1.18","custom":""},{"low":"5.00","med":"15.40","high":"15.47","auto":"15.00","dense":"15.00","custom":""},{"low":"5.00","med":"13.80","high":"13.84","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"17.90","high":"17.96","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"9.00","high":"9.08","auto":"9.00","dense":"9.00","custom":""},{"low":"5.00","med":"13.60","high":"13.61","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"18.30","high":"18.38","auto":"18.00","dense":"18.00","custom":""},{"low":"5.00","med":"13.60","high":"13.69","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"5.00","high":"5.08","auto":"5.00","dense":"5.05","custom":""},{"low":"5.00","med":"5.70","high":"5.77","auto":"5.70","dense":"5.75","custom":""},{"low":"5.00","med":"11.40","high":"11.45","auto":"11.00","dense":"11.00","custom":""},{"low":"0.00","med":"0.30","high":"0.31","auto":"0.30","dense":"0.31","custom":""},{"low":"5.00","med":"18.50","high":"18.55","auto":"18.50","dense":"18.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"18.80","high":"18.81","auto":"18.50","dense":"18.50","custom":""},{"low":"5.00","med":"10.20","high":"10.20","auto":"10.00","dense":"10.00","custom":""},{"low":"5.00","med":"10.10","high":"10.14","auto":"10.00","dense":"10.00","custom":""},{"low":"2.00","med":"2.40","high":"2.48","auto":"2.45","dense":"2.48","custom":""},{"low":"5.00","med":"16.10","high":"16.14","auto":"16.00","dense":"16.00","custom":""},{"low":"2.50","med":"2.90","high":"2.90","auto":"2.90","dense":"2.90","custom":""},{"low":"0.50","med":"0.60","high":"0.65","auto":"0.65","dense":"0.65","custom":""},{"low":"2.00","med":"2.20","high":"2.21","auto":"2.20","dense":"2.21","custom":""},{"low":"3.00","med":"3.10","high":"3.15","auto":"3.15","dense":"3.15","custom":""},{"low":"4.50","med":"4.80","high":"4.89","auto":"4.85","dense":"4.85","custom":""},{"low":"2.50","med":"2.90","high":"2.98","auto":"2.95","dense":"2.98","custom":""},{"low":"2.50","med":"2.90","high":"2.99","auto":"2.95","dense":"2.99","custom":""},{"low":"4.00","med":"4.00","high":"4.01","auto":"4.00","dense":"4.00","custom":""},{"low":"4.50","med":"4.60","high":"4.68","auto":"4.65","dense":"4.65","custom":""},{"low":"4.50","med":"4.60","high":"4.69","auto":"4.65","dense":"4.65","custom":""}] } diff --git a/test/fixtures/fixtures.js b/test/fixtures/fixtures.js index fc59d7eeab3..2a0a7638fc4 100644 --- a/test/fixtures/fixtures.js +++ b/test/fixtures/fixtures.js @@ -1,4 +1,33 @@ // jscs:disable +import CONSTANTS from 'src/constants.json'; +const utils = require('src/utils.js'); + +function convertTargetingsFromOldToNew(targetings) { + var mapOfOldToNew = { + 'hb_bidder': CONSTANTS.TARGETING_KEYS.BIDDER, + 'hb_adid': CONSTANTS.TARGETING_KEYS.AD_ID, + 'hb_pb': CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, + 'hb_size': CONSTANTS.TARGETING_KEYS.SIZE, + 'hb_deal': CONSTANTS.TARGETING_KEYS.DEAL, + 'hb_source': CONSTANTS.TARGETING_KEYS.SOURCE, + 'hb_format': CONSTANTS.TARGETING_KEYS.FORMAT + }; + var newTargetings = {}; + utils._each(targetings, function(value, currentKey) { + var replaced = false; + utils._each(mapOfOldToNew, function(newKey, oldKey) { + if (currentKey.indexOf(oldKey) === 0 && oldKey !== newKey) { + var updatedKey = currentKey.replace(oldKey, newKey); + newTargetings[updatedKey] = targetings[currentKey]; + replaced = true; + } + }); + if (!replaced) { + newTargetings[currentKey] = targetings[currentKey]; + } + }) + return newTargetings; +} export function getBidRequests() { return [ @@ -13,7 +42,7 @@ export function getBidRequests() { 'placementId': '4799418', 'test': 'me' }, - 'placementCode': '/19968336/header-bid-tag1', + 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, @@ -24,11 +53,14 @@ export function getBidRequests() { 90 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90], [970, 90]] + } + }, 'bidId': '392b5a6b05d648', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', - 'startTime': 1462918897462, - 'status': 1, 'transactionId': 'fsafsa' }, { @@ -36,7 +68,7 @@ export function getBidRequests() { 'params': { 'placementId': '4799418' }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -47,11 +79,14 @@ export function getBidRequests() { 600 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90], [970, 90]] + } + }, 'bidId': '4dccdc37746135', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', - 'startTime': 1462918897463, - 'status': 1, 'transactionId': 'fsafsa' } ], @@ -68,7 +103,7 @@ export function getBidRequests() { 'publisherId': 39741, 'adSlot': '39620189@300x250' }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -79,6 +114,11 @@ export function getBidRequests() { 600 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '6d11aa2d5b3659', 'bidderRequestId': '5e1525bae3eb11', 'auctionId': '1863e370099523', @@ -117,7 +157,7 @@ export function getBidRequests() { 10 ], }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -128,6 +168,11 @@ export function getBidRequests() { 600 ] ], + 'mediaType': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '96aff279720d39', 'bidderRequestId': '8778750ee15a77', 'auctionId': '1863e370099523', @@ -146,7 +191,7 @@ export function getBidRequests() { 'params': { 'inventoryCode': 'sortable_all_right_sports' }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -157,10 +202,14 @@ export function getBidRequests() { 600 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '1144e2f0de84363', 'bidderRequestId': '107f5e6e98dcf09', 'auctionId': '1863e370099523', - 'startTime': 1462918897477, 'transactionId': 'fsafsa' } ], @@ -176,7 +225,7 @@ export function getBidRequests() { 'params': { 'tagId': 16577 }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -187,10 +236,14 @@ export function getBidRequests() { 600 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '135e89c039705da', 'bidderRequestId': '12eeded736650b4', 'auctionId': '1863e370099523', - 'status': 1, 'transactionId': 'fsafsa' } ], @@ -206,7 +259,7 @@ export function getBidRequests() { 'params': { 'placementId': '4799418' }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -217,11 +270,14 @@ export function getBidRequests() { 600 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '17dd1d869bed44e', 'bidderRequestId': '167c4d79b615948', 'auctionId': '1863e370099523', - 'startTime': 1462918897480, - 'status': 1, 'transactionId': 'fsafsa' } ], @@ -237,7 +293,7 @@ export function getBidRequests() { 'params': { 'placementId': '4799418' }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -248,11 +304,14 @@ export function getBidRequests() { 600 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '192c8c1df0f5d1d', 'bidderRequestId': '18bed198c172a69', 'auctionId': '1863e370099523', - 'startTime': 1462918897481, - 'status': 1, 'transactionId': 'fsafsa' } ], @@ -268,7 +327,7 @@ export function getBidRequests() { 'params': { 'aId': 3080 }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -279,6 +338,11 @@ export function getBidRequests() { 600 ] ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '21ae8131ec04f6e', 'bidderRequestId': '20d0d30333715a7', 'auctionId': '1863e370099523', @@ -294,6 +358,7 @@ export function getBidResponses() { return [ { 'bidderCode': 'triplelift', + 'mediaType': 'banner', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', @@ -311,19 +376,21 @@ export function getBidResponses() { 'pbAg': '0.10', 'size': '0x0', 'auctionId': 123456, - 'adserverTargeting': { + 'requestId': '1144e2f0de84363', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'triplelift', 'hb_adid': '222bb26f9e8bd', 'hb_pb': '10.00', 'hb_size': '0x0', 'foobar': '0x0' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }, { 'bidderCode': 'appnexus', + 'mediaType': 'banner', 'width': 300, 'height': 250, 'statusMessage': 'Bid available', @@ -343,19 +410,21 @@ export function getBidResponses() { 'size': '300x250', 'alwaysUseBid': true, 'auctionId': 123456, - 'adserverTargeting': { + 'requestId': '4dccdc37746135', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'appnexus', 'hb_adid': '233bcbee889d46d', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }, { 'bidderCode': 'appnexus', + 'mediaType': 'banner', 'width': 728, 'height': 90, 'statusMessage': 'Bid available', @@ -375,19 +444,21 @@ export function getBidResponses() { 'size': '728x90', 'alwaysUseBid': true, 'auctionId': 123456, - 'adserverTargeting': { + 'requestId': '392b5a6b05d648', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'appnexus', 'hb_adid': '24bd938435ec3fc', 'hb_pb': '10.00', 'hb_size': '728x90', 'foobar': '728x90' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }, { 'bidderCode': 'pagescience', + 'mediaType': 'banner', 'width': 300, 'height': 250, 'statusMessage': 'Bid available', @@ -406,19 +477,21 @@ export function getBidResponses() { 'pbAg': '0.50', 'size': '300x250', 'auctionId': 123456, - 'adserverTargeting': { + 'requestId': '192c8c1df0f5d1d', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'pagescience', 'hb_adid': '25bedd4813632d7', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }, { 'bidderCode': 'brightcom', + 'mediaType': 'banner', 'width': 300, 'height': 250, 'statusMessage': 'Bid available', @@ -436,19 +509,21 @@ export function getBidResponses() { 'pbAg': '0.15', 'size': '300x250', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '135e89c039705da', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'brightcom', 'hb_adid': '26e0795ab963896', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }, { 'bidderCode': 'brealtime', + 'mediaType': 'banner', 'width': 300, 'height': 250, 'statusMessage': 'Bid available', @@ -467,19 +542,21 @@ export function getBidResponses() { 'pbAg': '0.50', 'size': '300x250', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '17dd1d869bed44e', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'brealtime', 'hb_adid': '275bd666f5a5a5d', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }, { 'bidderCode': 'pubmatic', + 'mediaType': 'banner', 'width': '300', 'height': '250', 'statusMessage': 'Bid available', @@ -499,19 +576,21 @@ export function getBidResponses() { 'pbAg': '5.90', 'size': '300x250', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '6d11aa2d5b3659', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'pubmatic', 'hb_adid': '28f4039c636b6a7', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }, { 'bidderCode': 'rubicon', + 'mediaType': 'banner', 'width': 300, 'height': 600, 'statusMessage': 'Bid available', @@ -529,13 +608,14 @@ export function getBidResponses() { 'pbAg': '2.70', 'size': '300x600', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '96aff279720d39', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'rubicon', 'hb_adid': '29019e2ab586a5a', 'hb_pb': '10.00', 'hb_size': '300x600', 'foobar': '300x600' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 @@ -546,26 +626,26 @@ export function getBidResponses() { export function getSlotTargeting() { return { '/19968336/header-bid-tag-0': [ - { + convertTargetingsFromOldToNew({ 'hb_bidder': [ 'appnexus' ] - }, - { + }), + convertTargetingsFromOldToNew({ 'hb_adid': [ '233bcbee889d46d' ] - }, - { + }), + convertTargetingsFromOldToNew({ 'hb_pb': [ '10.00' ] - }, - { + }), + convertTargetingsFromOldToNew({ 'hb_size': [ '300x250' ] - }, + }), { 'foobar': [ '300x250' @@ -579,138 +659,54 @@ export function getAdUnits() { return [ { 'code': '/19968336/header-bid-tag1', - 'sizes': [ - [ - 728, - 90 - ], - [ - 970, - 90 - ] - ], + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90], [970, 90]] + }, + }, 'bids': [ { 'bidder': 'adequant', 'params': { 'publisher_id': '1234567', 'bidfloor': 0.01 - }, - 'placementCode': '/19968336/header-bid-tag1', - 'sizes': [ - [ - 728, - 90 - ], - [ - 970, - 90 - ] - ], - 'bidId': '3692954f816efc', - 'bidderRequestId': '2b1a75d5e826c4', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'appnexus', 'params': { 'placementId': '543221', 'test': 'me' - }, - 'placementCode': '/19968336/header-bid-tag1', - 'sizes': [ - [ - 728, - 90 - ], - [ - 970, - 90 - ] - ], - 'bidId': '68136e1c47023d', - 'bidderRequestId': '55e24a66bed717', - 'auctionId': '1ff753bd4ae5cb', - 'startTime': 1463510220995, - 'status': 1 + } } ] }, { 'code': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '5324321' - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '7e5d6af25ed188', - 'bidderRequestId': '55e24a66bed717', - 'auctionId': '1ff753bd4ae5cb', - 'startTime': 1463510220996 + } }, { 'bidder': 'adequant', 'params': { 'publisher_id': '12353433', 'bidfloor': 0.01 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '4448d80ac1374e', - 'bidderRequestId': '2b1a75d5e826c4', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'triplelift', 'params': { 'inventoryCode': 'inv_code_here' - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '9514d586c52abf', - 'bidderRequestId': '8c4f03b838d7ee', - 'auctionId': '1ff753bd4ae5cb', - 'startTime': 1463510220997 + } }, { 'bidder': 'springserve', @@ -718,21 +714,7 @@ export function getAdUnits() { 'impId': 1234, 'supplyPartnerId': 1, 'test': true - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '113079fed03f58c', - 'bidderRequestId': '1048e0df882e965', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'rubicon', @@ -758,105 +740,33 @@ export function getAdUnits() { 15, 10 ] - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '13c2c2a79d155ea', - 'bidderRequestId': '129e383ac549e5d', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'openx', 'params': { 'jstag_url': 'http://servedbyopenx.com/w/1.0/jstag?nc=account_key', 'unit': 2345677 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '154f9cbf82df565', - 'bidderRequestId': '1448569c2453b84', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'pubmatic', 'params': { 'publisherId': 1234567, 'adSlot': '1234567@300x250' - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '17f8c3a8fb13308', - 'bidderRequestId': '16095445eeb05e4', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'pagescience', 'params': { 'placementId': '1234567' - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '2074d5757675542', - 'bidderRequestId': '19883380ef5453a', - 'auctionId': '1ff753bd4ae5cb', - 'startTime': 1463510221014 + } }, { 'bidder': 'brealtime', 'params': { 'placementId': '1234567' - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '222b6ad5a9b835d', - 'bidderRequestId': '2163409fdf6f333', - 'auctionId': '1ff753bd4ae5cb', - 'startTime': 1463510221015 + } }, { 'bidder': 'indexExchange', @@ -864,21 +774,7 @@ export function getAdUnits() { 'id': '1', 'siteID': 123456, 'timeout': 10000 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '2499961ab3f937a', - 'bidderRequestId': '23b57a2de4ae50b', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'adform', @@ -886,82 +782,26 @@ export function getAdUnits() { 'adxDomain': 'adx.adform.net', 'mid': 123456, 'test': 1 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '26605265bf5e9c5', - 'bidderRequestId': '25a0902299c17d3', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'amazon', 'params': { 'aId': 3080 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '2935d8f6764fe45', - 'bidderRequestId': '28afa21ca9246c1', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'aol', 'params': { 'network': '112345.45', 'placement': 12345 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '31d1489681dc539', - 'bidderRequestId': '30bf32da9080fdd', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'sovrn', 'params': { 'tagid': '123556' - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '33c1a8028d91563', - 'bidderRequestId': '324bcb47cfcf034', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'pulsepoint', @@ -969,41 +809,13 @@ export function getAdUnits() { 'cf': '300X250', 'cp': 1233456, 'ct': 12357 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '379219f0506a26f', - 'bidderRequestId': '360ec66bbb0719c', - 'auctionId': '1ff753bd4ae5cb' + } }, { 'bidder': 'brightcom', 'params': { 'tagId': 75423 - }, - 'placementCode': '/19968336/header-bid-tag-0', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '395cfcf496e7d6d', - 'bidderRequestId': '38a776c7f001ea', - 'auctionId': '1ff753bd4ae5cb' + } } ] } @@ -1018,6 +830,7 @@ export function getBidResponsesFromAPI() { 'bidderCode': 'brightcom', 'width': 300, 'height': 250, + 'mediaType': 'banner', 'statusMessage': 'Bid available', 'adId': '26e0795ab963896', 'cpm': 0.17, @@ -1033,13 +846,14 @@ export function getBidResponsesFromAPI() { 'pbAg': '0.15', 'size': '300x250', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '135e89c039705da', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'brightcom', 'hb_adid': '26e0795ab963896', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 @@ -1048,6 +862,7 @@ export function getBidResponsesFromAPI() { 'bidderCode': 'brealtime', 'width': 300, 'height': 250, + 'mediaType': 'banner', 'statusMessage': 'Bid available', 'adId': '275bd666f5a5a5d', 'creative_id': 29681110, @@ -1064,13 +879,14 @@ export function getBidResponsesFromAPI() { 'pbAg': '0.50', 'size': '300x250', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '17dd1d869bed44e', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'brealtime', 'hb_adid': '275bd666f5a5a5d', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 @@ -1079,6 +895,7 @@ export function getBidResponsesFromAPI() { 'bidderCode': 'pubmatic', 'width': '300', 'height': '250', + 'mediaType': 'banner', 'statusMessage': 'Bid available', 'adId': '28f4039c636b6a7', 'adSlot': '39620189@300x250', @@ -1096,13 +913,14 @@ export function getBidResponsesFromAPI() { 'pbAg': '5.90', 'size': '300x250', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '6d11aa2d5b3659', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'pubmatic', 'hb_adid': '28f4039c636b6a7', 'hb_pb': '10.00', 'hb_size': '300x250', 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 @@ -1111,6 +929,7 @@ export function getBidResponsesFromAPI() { 'bidderCode': 'rubicon', 'width': 300, 'height': 600, + 'mediaType': 'banner', 'statusMessage': 'Bid available', 'adId': '29019e2ab586a5a', 'cpm': 2.74, @@ -1126,13 +945,14 @@ export function getBidResponsesFromAPI() { 'pbAg': '2.70', 'size': '300x600', 'auctionId': 654321, - 'adserverTargeting': { + 'requestId': '96aff279720d39', + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': 'rubicon', 'hb_adid': '29019e2ab586a5a', 'hb_pb': '10.00', 'hb_size': '300x600', 'foobar': '300x600' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': 300 @@ -1145,7 +965,7 @@ export function getBidResponsesFromAPI() { // Ad server targeting when `setConfig({ enableSendAllBids: true })` is set. export function getAdServerTargeting() { return { - '/19968336/header-bid-tag-0': { + '/19968336/header-bid-tag-0': convertTargetingsFromOldToNew({ 'foobar': '0x0,300x250,300x600', 'hb_size': '300x250', 'hb_pb': '10.00', @@ -1179,8 +999,8 @@ export function getAdServerTargeting() { 'hb_pb_rubicon': '10.00', 'hb_adid_rubicon': '29019e2ab586a5a', 'hb_bidder_rubicon': 'rubicon' - }, - '/19968336/header-bid-tag1': { + }), + '/19968336/header-bid-tag1': convertTargetingsFromOldToNew({ 'foobar': '728x90', 'hb_size': '728x90', 'hb_pb': '10.00', @@ -1190,7 +1010,7 @@ export function getAdServerTargeting() { 'hb_pb_appnexus': '10.00', 'hb_adid_appnexus': '24bd938435ec3fc', 'hb_bidder_appnexus': 'appnexus' - } + }) }; } @@ -1198,19 +1018,19 @@ export function getAdServerTargeting() { export function getTargetingKeys() { return [ [ - 'hb_bidder', + CONSTANTS.TARGETING_KEYS.BIDDER, 'appnexus' ], [ - 'hb_adid', + CONSTANTS.TARGETING_KEYS.AD_ID, '233bcbee889d46d' ], [ - 'hb_pb', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, '10.00' ], [ - 'hb_size', + CONSTANTS.TARGETING_KEYS.SIZE, '300x250' ], [ @@ -1225,19 +1045,19 @@ export function getTargetingKeys() { export function getTargetingKeysBidLandscape() { return [ [ - 'hb_bidder', + CONSTANTS.TARGETING_KEYS.BIDDER, 'appnexus' ], [ - 'hb_adid_appnexus', + CONSTANTS.TARGETING_KEYS.AD_ID + '_appnexus', '233bcbee889d46d' ], [ - 'hb_pb', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, '10.00' ], [ - 'hb_size', + CONSTANTS.TARGETING_KEYS.SIZE, '300x250' ], [ @@ -1245,111 +1065,111 @@ export function getTargetingKeysBidLandscape() { ['0x0', '300x250', '300x600'] ], [ - 'hb_bidder_triplelift', + CONSTANTS.TARGETING_KEYS.BIDDER + '_triplelift', 'triplelift' ], [ - 'hb_adid_triplelift', + CONSTANTS.TARGETING_KEYS.AD_ID + '_triplelift', '222bb26f9e8bd' ], [ - 'hb_pb_triplelift', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_triplelift', '10.00' ], [ - 'hb_size_triplelift', + CONSTANTS.TARGETING_KEYS.SIZE + '_triplelift', '0x0' ], [ - 'hb_bidder_appnexus', + CONSTANTS.TARGETING_KEYS.BIDDER + '_appnexus', 'appnexus' ], [ - 'hb_pb_appnexus', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_appnexus', '10.00' ], [ - 'hb_size_appnexus', + CONSTANTS.TARGETING_KEYS.SIZE + '_appnexus', '300x250' ], [ - 'hb_bidder_pagescienc', + CONSTANTS.TARGETING_KEYS.BIDDER + '_pagescienc', 'pagescience' ], [ - 'hb_adid_pagescience', + CONSTANTS.TARGETING_KEYS.AD_ID + '_pagescience', '25bedd4813632d7' ], [ - 'hb_pb_pagescience', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_pagescience', '10.00' ], [ - 'hb_size_pagescience', + CONSTANTS.TARGETING_KEYS.SIZE + '_pagescience', '300x250' ], [ - 'hb_bidder_brightcom', + CONSTANTS.TARGETING_KEYS.BIDDER + '_brightcom', 'brightcom' ], [ - 'hb_adid_brightcom', + CONSTANTS.TARGETING_KEYS.AD_ID + '_brightcom', '26e0795ab963896' ], [ - 'hb_pb_brightcom', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_brightcom', '10.00' ], [ - 'hb_size_brightcom', + CONSTANTS.TARGETING_KEYS.SIZE + '_brightcom', '300x250' ], [ - 'hb_bidder_brealtime', + CONSTANTS.TARGETING_KEYS.BIDDER + '_brealtime', 'brealtime' ], [ - 'hb_adid_brealtime', + CONSTANTS.TARGETING_KEYS.AD_ID + '_brealtime', '275bd666f5a5a5d' ], [ - 'hb_pb_brealtime', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_brealtime', '10.00' ], [ - 'hb_size_brealtime', + CONSTANTS.TARGETING_KEYS.SIZE + '_brealtime', '300x250' ], [ - 'hb_bidder_pubmatic', + CONSTANTS.TARGETING_KEYS.BIDDER + '_pubmatic', 'pubmatic' ], [ - 'hb_adid_pubmatic', + CONSTANTS.TARGETING_KEYS.AD_ID + '_pubmatic', '28f4039c636b6a7' ], [ - 'hb_pb_pubmatic', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_pubmatic', '10.00' ], [ - 'hb_size_pubmatic', + CONSTANTS.TARGETING_KEYS.SIZE + '_pubmatic', '300x250' ], [ - 'hb_bidder_rubicon', + CONSTANTS.TARGETING_KEYS.BIDDER + '_rubicon', 'rubicon' ], [ - 'hb_adid_rubicon', + CONSTANTS.TARGETING_KEYS.AD_ID + '_rubicon', '29019e2ab586a5a' ], [ - 'hb_pb_rubicon', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_rubicon', '10.00' ], [ - 'hb_size_rubicon', + CONSTANTS.TARGETING_KEYS.SIZE + '_rubicon', '300x600' ] ]; @@ -1367,7 +1187,7 @@ export function getBidRequestedPayload() { 'publisher_id': '5000563', 'bidfloor': 0.01 }, - 'placementCode': '/19968336/header-bid-tag-1', + 'adUnitCode': '/19968336/header-bid-tag-1', 'sizes': [ [ 300, @@ -1405,7 +1225,7 @@ export function getCurrencyRates() { }; } -export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl}) { +export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId}) { let bid = { 'bidderCode': bidder, 'width': '300', @@ -1419,6 +1239,7 @@ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, ad 'requestTimestamp': 1454535718610, 'responseTimestamp': responseTimestamp, 'auctionId': auctionId, + 'requestId': requestId, 'timeToRespond': 123, 'pbLg': '0.50', 'pbMg': '0.50', @@ -1426,12 +1247,12 @@ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, ad 'adUnitCode': adUnitCode, 'bidder': bidder, 'size': '300x250', - 'adserverTargeting': { + 'adserverTargeting': convertTargetingsFromOldToNew({ 'hb_bidder': bidder, 'hb_adid': adId, 'hb_pb': cpm, 'foobar': '300x250' - }, + }), 'netRevenue': true, 'currency': 'USD', 'ttl': (!ttl) ? 300 : ttl @@ -1442,3 +1263,64 @@ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, ad } return bid; } + +export function getServerTestingsAds() { + return [ + { + code: 'test_div_1', + sizes: [[728, 90]], + bids: [ + { + 'bidSource': { 'client': 0, 'server': 100 }, + 'bidder': 'rubicon' + }, + { + 'bidSource': { 'client': 100, 'server': 0 }, + 'bidder': 'appnexus' + } + ] + }, + { + code: 'test_div_2', + sizes: [[300, 250]], + bids: [ + { + 'bidSource': { 'client': 100, 'server': 0 }, + 'bidder': 'rubicon' + }, + { + 'bidSource': { 'client': 100, 'server': 0 }, + 'bidder': 'appnexus' + } + ] + }, + { + code: 'test_div_3', + sizes: [[300, 250]], + bids: [{ bidder: 'adequant' }] + }, + { + code: 'test_div_4', + sizes: [[300, 250]], + bids: [{ bidder: 'openx' }] + } + ]; +}; + +export const getServerTestingConfig = (config, override = {}) => + Object.assign({}, config, { + enabled: true, + testing: true, + testServerOnly: true, + bidders: ['appnexus', 'rubicon', 'openx'], + bidderControl: { + rubicon: { + bidSource: { server: 100, client: 0 }, + includeSourceKvp: true + }, + appnexus: { + bidSource: { server: 0, client: 100 }, + includeSourceKvp: true + } + } + }, override); diff --git a/test/helpers/testing-utils.js b/test/helpers/testing-utils.js new file mode 100644 index 00000000000..21d5992873e --- /dev/null +++ b/test/helpers/testing-utils.js @@ -0,0 +1,4 @@ +module.exports = { + host: (process.env.TEST_SERVER_HOST) ? process.env.TEST_SERVER_HOST : 'localhost', + protocol: (process.env.TEST_SERVER_PROTOCOL) ? 'https' : 'http' +} diff --git a/test/mocks/adloaderStub.js b/test/mocks/adloaderStub.js new file mode 100644 index 00000000000..9b9e62a4c3b --- /dev/null +++ b/test/mocks/adloaderStub.js @@ -0,0 +1,23 @@ + +import * as adloader from 'src/adloader'; + +let sandbox; + +export let loadScript; +export let loadExternalScript; +export let loadScriptStub; +export let loadExternalScriptStub; + +beforeEach(function() { + sandbox = sinon.sandbox.create(); + loadScript = adloader.loadScript; + loadExternalScript = adloader.loadExternalScript; + loadScriptStub = sandbox.stub(adloader, 'loadScript').callsFake((...args) => { + args[1](); + }); + loadExternalScriptStub = sandbox.stub(adloader, 'loadExternalScript'); +}); + +afterEach(function() { + sandbox.restore(); +}); diff --git a/test/mocks/videoCacheStub.js b/test/mocks/videoCacheStub.js index 4a1d8bca343..39f5d67b6a9 100644 --- a/test/mocks/videoCacheStub.js +++ b/test/mocks/videoCacheStub.js @@ -12,7 +12,7 @@ import * as videoCache from 'src/videoCache'; export default function useVideoCacheStub(responses) { let storeStub; - beforeEach(() => { + beforeEach(function () { storeStub = sinon.stub(videoCache, 'store'); if (responses.store instanceof Error) { @@ -22,7 +22,7 @@ export default function useVideoCacheStub(responses) { } }); - afterEach(() => { + afterEach(function () { videoCache.store.restore(); }); diff --git a/test/pages/banner.html b/test/pages/banner.html new file mode 100644 index 00000000000..03f3b9b9e54 --- /dev/null +++ b/test/pages/banner.html @@ -0,0 +1,94 @@ + + + + + + + Prebid.js Banner Example + + + + + + + + + + + + + + + +

    Prebid.js Banner Ad Unit Test

    +
    + +
    +
    + + + diff --git a/test/pages/native.html b/test/pages/native.html new file mode 100644 index 00000000000..0823f486848 --- /dev/null +++ b/test/pages/native.html @@ -0,0 +1,123 @@ + + + + + + + Prebid.js Native Example + + + + + + + + + + + + + + + +

    Prebid.js Native Ad Unit Test

    +
    +

    No response

    + +
    + + + diff --git a/test/pages/outstream.html b/test/pages/outstream.html new file mode 100644 index 00000000000..56e443a519b --- /dev/null +++ b/test/pages/outstream.html @@ -0,0 +1,168 @@ + + + + + + + Prebid.js Video Outstream Example + + + + + + + + + + + + +
    +

    + In scelerisque sem sed tortor posuere sagittis. Fusce scelerisque odio at tincidunt ultricies. Fusce egestas, erat + non finibus dictum, nulla arcu viverra nibh, at bibendum ligula nisi egestas magna. Nulla eu finibus nulla. + Pellentesque at mi eget turpis + consequat scelerisque. Sed lacinia, nisi sit amet egestas vestibulum, elit odio iaculis leo, et lacinia risus enim + non lacus. Cras nec neque eget nunc gravida maximus. Ut hendrerit convallis sollicitudin. Donec cursus erat vel + metus gravida, + et pretium justo iaculis. Curabitur condimentum blandit augue, quis interdum leo. Vivamus dapibus est nec dui + efficitur, eu imperdiet nulla sollicitudin. Suspendisse laoreet velit vitae arcu mollis, ac interdum lorem + venenatis. Aenean + nec purus varius, accumsan ex at, luctus arcu. Quisque consectetur tortor eros, placerat lacinia eros aliquam a. + Proin non porttitor libero. +

    +

    + Proin eget vulputate est. Nunc sit amet neque a tortor ullamcorper suscipit non eu neque. Quisque at massa in + metus feugiat rutrum. Nulla et orci orci. Aliquam erat volutpat. Cras tincidunt metus lectus, sed suscipit augue + mollis vitae. Sed quis condimentum + tortor, sit amet consectetur erat. Nulla pellentesque turpis lacus, eu venenatis massa fringilla at. Duis sed + pharetra turpis. Maecenas vel porttitor neque. Praesent quis felis sapien. Donec suscipit euismod dui, vitae + fermentum nisi ornare + in. +

    +

    + Suspendisse tempor felis accumsan orci finibus, imperdiet mollis arcu imperdiet. In eu dolor condimentum, pulvinar + nisl a, sollicitudin nunc. Ut vel lectus libero. Praesent rhoncus leo tortor, at mollis nulla sagittis eget. + Quisque tempus tempor augue + sed rutrum. Sed vitae volutpat quam. Proin vestibulum eros metus, a luctus erat condimentum eu. Vivamus + ullamcorper ultricies dui, ac malesuada leo finibus semper. Cras diam augue, imperdiet sed efficitur id, aliquam + sed purus. Praesent + eget turpis quis sapien interdum sagittis. Vivamus placerat nunc a tempus fermentum. Praesent laoreet leo at + tellus porta, ut viverra tortor pharetra. Quisque elit velit, eleifend eget imperdiet vel, suscipit ac nisi. + Aliquam egestas mauris + ut massa fringilla laoreet. +

    +
    +

    Prebid Outstream Video Ad

    + +
    +

    + Quisque ac luctus nisi, vitae ornare arcu. Proin fermentum sapien vitae odio vestibulum porta. Suspendisse + faucibus sapien enim, et faucibus urna tempus et. Integer porttitor justo sed faucibus blandit. Morbi semper + lectus vitae semper facilisis. Quisque + molestie accumsan arcu, eget bibendum dui euismod et. Sed in mattis lacus, nec lacinia sem. Fusce sed tortor + posuere, iaculis justo varius, elementum est. +

    +

    + Etiam condimentum, eros commodo semper tristique, lorem leo pharetra massa, eget cursus justo enim id urna. Sed + imperdiet mauris vitae ante bibendum elementum. Etiam eu dui porttitor leo imperdiet cursus. Maecenas consequat, + neque a dapibus viverra, nunc + velit volutpat nibh, ut cursus sem tortor ac arcu. Praesent convallis lacus vel nisi aliquam, in posuere libero + scelerisque. Curabitur et lacinia nisl. Nunc id ligula neque. Phasellus non eros et leo ultrices ultricies. Nulla + facilisi. + Donec ut augue urna. Suspendisse sodales nisi at ex faucibus, et tempus magna fermentum. Proin non arcu interdum, + pulvinar est at, vehicula odio. Morbi nec maximus sem. Ut eu tristique urna. +

    +

    + Pellentesque eget quam sem. Nam interdum eleifend leo, mattis sagittis metus ornare tristique. Cras pretium odio + lectus, vitae viverra massa consequat eget. Suspendisse porttitor pretium lectus in scelerisque. Phasellus euismod + porta lectus eget pharetra. + Ut et viverra mi, ut imperdiet lacus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere + cubilia Curae; Nunc tempus sapien sit amet tortor rhoncus dignissim. Sed at augue et sem lacinia feugiat. Nulla + vitae convallis + urna. Morbi scelerisque erat quis nibh pretium, non elementum elit consectetur. Proin in feugiat nisl. +

    +

    + Morbi et ipsum purus. Integer ut pulvinar metus. Fusce maximus ex nec purus sollicitudin gravida. Vivamus dapibus + volutpat erat nec tristique. Aliquam mi dolor, pretium non elementum quis, viverra non est. Pellentesque egestas, + lectus a posuere imperdiet, + nisi sem elementum neque, eu volutpat arcu turpis venenatis magna. Curabitur non neque consectetur, vulputate urna + sed, vestibulum lacus. Aenean mollis, risus non pulvinar egestas, lectus lectus finibus dui, sit amet pretium + metus mauris + vitae nibh. In non ultricies odio. +

    +
    + + + diff --git a/test/pages/video.html b/test/pages/video.html index c6a72b6e26b..e040b65fe23 100644 --- a/test/pages/video.html +++ b/test/pages/video.html @@ -32,7 +32,7 @@ code: 'video1', sizes: [640,480], mediaTypes: { - video: {context: 'instream'} + video: {context: 'instream', playerSize: [640, 480]} }, bids: [ { diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index d6b227e9aac..59138b03e61 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -1,142 +1,149 @@ -import { assert } from 'chai'; +import { expect } from 'chai'; import events from 'src/events'; import CONSTANTS from 'src/constants.json'; +const REQUEST_BIDS = CONSTANTS.EVENTS.REQUEST_BIDS; const BID_REQUESTED = CONSTANTS.EVENTS.BID_REQUESTED; const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; const BID_WON = CONSTANTS.EVENTS.BID_WON; const BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; const AD_RENDER_FAILED = CONSTANTS.EVENTS.AD_RENDER_FAILED; +const ADD_AD_UNITS = CONSTANTS.EVENTS.ADD_AD_UNITS; const AnalyticsAdapter = require('src/AnalyticsAdapter').default; const config = { - url: 'http://localhost:9999/src/adapters/analytics/libraries/example.js', - analyticsType: 'library', - global: 'ExampleAnalyticsGlobalObject', - handler: 'on' + url: 'http://localhost:9999/endpoint', + analyticsType: 'endpoint' }; -window[config.global] = () => {}; - describe(` FEATURE: Analytics Adapters API SCENARIO: A publisher enables analytics - GIVEN a global object \`window['testGlobal']\` AND an \`example\` instance of \`AnalyticsAdapter\`\n`, () => { - describe(`WHEN an event occurs that is to be tracked\n`, () => { + let xhr; + let requests; + let adapter; + + beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = (request) => requests.push(request); + adapter = new AnalyticsAdapter(config); + }); + + afterEach(function () { + xhr.restore(); + adapter.disableAnalytics(); + }); + + it(`SHOULD call the endpoint WHEN an event occurs that is to be tracked`, function () { const eventType = BID_REQUESTED; const args = { some: 'data' }; - const adapter = new AnalyticsAdapter(config); - var spyTestGlobal = sinon.spy(window, config.global); adapter.track({ eventType, args }); - it(`THEN should call \`window.${config.global}\` function\n`, () => { - assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); - assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); - }); - window[config.global].restore(); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {some: 'data'}, eventType: 'bidRequested'}); }); - describe(`WHEN an event occurs before tracking library is available\n`, () => { + it(`SHOULD queue the event first and then track it WHEN an event occurs before tracking library is available`, function () { const eventType = BID_RESPONSE; const args = { wat: 'wot' }; - const adapter = new AnalyticsAdapter(config); - - window[config.global] = null; - events.emit(BID_RESPONSE, args); - - describe(`AND the adapter is then enabled\n`, () => { - window[config.global] = () => {}; - var spyTestGlobal = sinon.spy(window, config.global); + events.emit(eventType, args); + adapter.enableAnalytics(); - adapter.enableAnalytics(); - - it(`THEN should queue the event first and then track it\n`, () => { - assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); - assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); - }); - - adapter.disableAnalytics(); - window[config.global].restore(); - }); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {wat: 'wot'}, eventType: 'bidResponse'}); }); - describe(`WHEN an event occurs after enable analytics\n`, () => { - var spyTestGlobal, - adapter; - - beforeEach(() => { - adapter = new AnalyticsAdapter(config); - spyTestGlobal = sinon.spy(window, config.global); - + describe(`WHEN an event occurs after enable analytics\n`, function () { + beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); // these tests shouldn't be affected by previous tests }); - afterEach(() => { - adapter.disableAnalytics(); - window[config.global].restore(); - + afterEach(function () { events.getEvents.restore(); }); - it('SHOULD call global when a bidWon event occurs', () => { + it('SHOULD call global when a bidWon event occurs', function () { const eventType = BID_WON; const args = { more: 'info' }; adapter.enableAnalytics(); events.emit(eventType, args); - assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); - assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {more: 'info'}, eventType: 'bidWon'}); }); - it('SHOULD call global when a adRenderFailed event occurs', () => { + it('SHOULD call global when a adRenderFailed event occurs', function () { const eventType = AD_RENDER_FAILED; const args = { call: 'adRenderFailed' }; adapter.enableAnalytics(); events.emit(eventType, args); - assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); - assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {call: 'adRenderFailed'}, eventType: 'adRenderFailed'}); + }); + + it('SHOULD call global when an addAdUnits event occurs', function () { + const eventType = ADD_AD_UNITS; + const args = { call: 'addAdUnits' }; + + adapter.enableAnalytics(); + events.emit(eventType, args); + + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {call: 'addAdUnits'}, eventType: 'addAdUnits'}); + }); + + it('SHOULD call global when a requestBids event occurs', function () { + const eventType = REQUEST_BIDS; + const args = { call: 'request' }; + + adapter.enableAnalytics(); + events.emit(eventType, args); + + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {call: 'request'}, eventType: 'requestBids'}); }); - it('SHOULD call global when a bidRequest event occurs', () => { + it('SHOULD call global when a bidRequest event occurs', function () { const eventType = BID_REQUESTED; const args = { call: 'request' }; adapter.enableAnalytics(); events.emit(eventType, args); - assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); - assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {call: 'request'}, eventType: 'bidRequested'}); }); - it('SHOULD call global when a bidResponse event occurs', () => { + it('SHOULD call global when a bidResponse event occurs', function () { const eventType = BID_RESPONSE; const args = { call: 'response' }; adapter.enableAnalytics(); events.emit(eventType, args); - assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); - assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {call: 'response'}, eventType: 'bidResponse'}); }); - it('SHOULD call global when a bidTimeout event occurs', () => { + it('SHOULD call global when a bidTimeout event occurs', function () { const eventType = BID_TIMEOUT; const args = { call: 'timeout' }; adapter.enableAnalytics(); events.emit(eventType, args); - assert.ok(spyTestGlobal.args[0][1] === eventType, `with expected event type\n`); - assert.deepEqual(spyTestGlobal.args[0][2], args, `with expected event data\n`); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {call: 'timeout'}, eventType: 'bidTimeout'}); }); - it('SHOULD NOT call global again when adapter.enableAnalytics is called with previous timeout', () => { + it('SHOULD NOT call global again when adapter.enableAnalytics is called with previous timeout', function () { const eventType = BID_TIMEOUT; const args = { call: 'timeout' }; @@ -144,22 +151,22 @@ FEATURE: Analytics Adapters API adapter.enableAnalytics(); events.emit(eventType, args); - assert(spyTestGlobal.calledOnce === true); + expect(requests.length).to.equal(1); }); - describe(`AND sampling is enabled\n`, () => { + describe(`AND sampling is enabled\n`, function () { const eventType = BID_WON; const args = { more: 'info' }; - beforeEach(() => { + beforeEach(function () { sinon.stub(Math, 'random').returns(0.5); }); - afterEach(() => { + afterEach(function () { Math.random.restore(); }); - it(`THEN should enable analytics when random number is in sample range`, () => { + it(`THEN should enable analytics when random number is in sample range`, function () { adapter.enableAnalytics({ options: { sampling: 0.75 @@ -167,10 +174,12 @@ FEATURE: Analytics Adapters API }); events.emit(eventType, args); - assert(spyTestGlobal.called === true); + expect(requests.length).to.equal(1); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({args: {more: 'info'}, eventType: 'bidWon'}); }); - it(`THEN should disable analytics when random number is outside sample range`, () => { + it(`THEN should disable analytics when random number is outside sample range`, function () { adapter.enableAnalytics({ options: { sampling: 0.25 @@ -178,7 +187,7 @@ FEATURE: Analytics Adapters API }); events.emit(eventType, args); - assert(spyTestGlobal.called === false); + expect(requests.length).to.equal(0); }); }); }); diff --git a/test/spec/adUnits_spec.js b/test/spec/adUnits_spec.js index f15ba41eb23..7dd48a13208 100644 --- a/test/spec/adUnits_spec.js +++ b/test/spec/adUnits_spec.js @@ -82,13 +82,6 @@ describe('Publisher API _ AdUnits', function () { assert.strictEqual(bids2[1].params.placementId, '827326', 'adUnit2 bids2 params.placementId'); }); - it('both add unit should contains a transactionId', function() { - assert.isString(adUnit1.transactionId); - assert.isString(adUnit2.transactionId); - - assert.strictEqual(false, adUnit1.transactionId === adUnit2.transactionId); - }); - it('the second adUnits value should be same with the adUnits that is added by $$PREBID_GLOBAL$$.addAdUnits();', function () { assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); assert.deepEqual(adUnit2.sizes, [[468, 60]], 'adUnit2 sizes'); diff --git a/test/spec/adloader_spec.js b/test/spec/adloader_spec.js index 55224cb0aab..27f574c9c42 100644 --- a/test/spec/adloader_spec.js +++ b/test/spec/adloader_spec.js @@ -1,28 +1,28 @@ import * as utils from 'src/utils'; -import * as adLoader from 'src/adloader'; +import * as adLoader from 'test/mocks/adloaderStub'; describe('adLoader', function () { let utilsinsertElementStub; let utilsLogErrorStub; - beforeEach(() => { + beforeEach(function () { utilsinsertElementStub = sinon.stub(utils, 'insertElement'); utilsLogErrorStub = sinon.stub(utils, 'logError'); }); - afterEach(() => { + afterEach(function () { utilsinsertElementStub.restore(); utilsLogErrorStub.restore(); }); - describe('loadExternalScript', () => { - it('requires moduleCode to be included on the request', () => { + describe('loadExternalScript', function () { + it('requires moduleCode to be included on the request', function () { adLoader.loadExternalScript('someURL'); expect(utilsLogErrorStub.called).to.be.true; expect(utilsinsertElementStub.called).to.be.false; }); - it('only allows whitelisted vendors to load scripts', () => { + it('only allows whitelisted vendors to load scripts', function () { adLoader.loadExternalScript('someURL', 'criteo'); expect(utilsLogErrorStub.called).to.be.false; expect(utilsinsertElementStub.called).to.be.true; diff --git a/test/spec/api_spec.js b/test/spec/api_spec.js index 41fafb080ad..a8987d3ed07 100755 --- a/test/spec/api_spec.js +++ b/test/spec/api_spec.js @@ -39,6 +39,10 @@ describe('Publisher API', function () { assert.isFunction($$PREBID_GLOBAL$$.getBidResponses); }); + it('should have function $$PREBID_GLOBAL$$.getBidResponses', function () { + assert.isFunction($$PREBID_GLOBAL$$.getNoBids); + }); + it('should have function $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode', function () { assert.isFunction($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode); }); diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 6fbc48b3cdc..577b1bec333 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -1,30 +1,24 @@ -import { auctionManager, newAuctionManager } from 'src/auctionManager'; -import { getKeyValueTargetingPairs } from 'src/auction'; +import { getKeyValueTargetingPairs, auctionCallbacks, AUCTION_COMPLETED } from 'src/auction'; import CONSTANTS from 'src/constants.json'; import { adjustBids } from 'src/auction'; import * as auctionModule from 'src/auction'; -import { newBidder, registerBidder } from 'src/adapters/bidderFactory'; +import { registerBidder } from 'src/adapters/bidderFactory'; +import { createBid } from 'src/bidfactory'; import { config } from 'src/config'; import * as store from 'src/videoCache'; import * as ajaxLib from 'src/ajax'; import find from 'core-js/library/fn/array/find'; -const adloader = require('../../src/adloader'); var assert = require('assert'); /* use this method to test individual files instead of the whole prebid.js project */ // TODO refactor to use the spec files var utils = require('../../src/utils'); -var bidfactory = require('../../src/bidfactory'); var fixtures = require('../fixtures/fixtures'); -var adaptermanager = require('src/adaptermanager'); +var adapterManager = require('src/adapterManager').default; var events = require('src/events'); -function timestamp() { - return new Date().getTime(); -} - const BIDDER_CODE = 'sampleBidder'; const BIDDER_CODE1 = 'sampleBidder1'; @@ -44,7 +38,8 @@ function mockBid(opts) { 'creativeId': 'id', 'currency': 'USD', 'netRevenue': true, - 'ttl': 360 + 'ttl': 360, + getSize: () => '300x250' }; } @@ -54,6 +49,12 @@ function mockBidRequest(bid, opts) { } let bidderCode = opts && opts.bidderCode; let adUnitCode = opts && opts.adUnitCode; + let defaultMediaType = { + banner: { + sizes: [[300, 250], [300, 600]] + } + } + let mediaType = (opts && opts.mediaType) ? opts.mediaType : defaultMediaType; let requestId = utils.getUniqueIdentifierStr(); @@ -71,7 +72,8 @@ function mockBidRequest(bid, opts) { 'sizes': [[300, 250], [300, 600]], 'bidId': bid.requestId, 'bidderRequestId': requestId, - 'auctionId': '20882439e3238c' + 'auctionId': '20882439e3238c', + 'mediaTypes': mediaType } ], 'auctionStart': 1505250713622, @@ -107,16 +109,6 @@ function mockAjaxBuilder() { } describe('auctionmanager.js', function () { - let xhr; - - before(() => { - xhr = sinon.useFakeXMLHttpRequest(); - }); - - after(() => { - xhr.restore(); - }); - describe('getKeyValueTargetingPairs', function () { const DEFAULT_BID = { cpm: 5.578, @@ -140,15 +132,18 @@ describe('auctionmanager.js', function () { /* return the expected response for a given bid, filter by keys if given */ function getDefaultExpected(bid, keys) { - var expected = { - 'hb_bidder': bid.bidderCode, - 'hb_adid': bid.adId, - 'hb_pb': bid.pbMg, - 'hb_size': bid.getSize(), - 'hb_source': bid.source, - 'hb_format': bid.mediaType, - }; - + var expected = {}; + expected[ CONSTANTS.TARGETING_KEYS.BIDDER ] = bid.bidderCode; + expected[ CONSTANTS.TARGETING_KEYS.AD_ID ] = bid.adId; + expected[ CONSTANTS.TARGETING_KEYS.PRICE_BUCKET ] = bid.pbMg; + expected[ CONSTANTS.TARGETING_KEYS.SIZE ] = bid.getSize(); + expected[ CONSTANTS.TARGETING_KEYS.SOURCE ] = bid.source; + expected[ CONSTANTS.TARGETING_KEYS.FORMAT ] = bid.mediaType; + if (bid.mediaType === 'video') { + expected[ CONSTANTS.TARGETING_KEYS.UUID ] = bid.videoCacheKey; + expected[ CONSTANTS.TARGETING_KEYS.CACHE_ID ] = bid.videoCacheKey; + expected[ CONSTANTS.TARGETING_KEYS.CACHE_HOST ] = 'prebid.adnxs.com'; + } if (!keys) { return expected; } @@ -166,8 +161,27 @@ describe('auctionmanager.js', function () { }); it('No bidder level configuration defined - default', function () { - var expected = getDefaultExpected(bid); - var response = getKeyValueTargetingPairs(bid.bidderCode, bid, CONSTANTS.GRANULARITY_OPTIONS.MEDIUM); + $$PREBID_GLOBAL$$.bidderSettings = {}; + let expected = getDefaultExpected(bid); + // remove hb_cache_host from expected + delete expected.hb_cache_host; + let response = getKeyValueTargetingPairs(bid.bidderCode, bid); + assert.deepEqual(response, expected); + }); + + it('No bidder level configuration defined - default for video', function () { + config.setConfig({ + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } + }); + $$PREBID_GLOBAL$$.bidderSettings = {}; + let videoBid = utils.deepClone(bid); + videoBid.mediaType = 'video'; + videoBid.videoCacheKey = 'abc123def'; + + let expected = getDefaultExpected(videoBid); + let response = getKeyValueTargetingPairs(videoBid.bidderCode, videoBid); assert.deepEqual(response, expected); }); @@ -177,35 +191,35 @@ describe('auctionmanager.js', function () { standard: { adserverTargeting: [ { - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function (bidResponse) { return bidResponse.bidderCode; } }, { - key: 'hb_adid', + key: CONSTANTS.TARGETING_KEYS.AD_ID, val: function (bidResponse) { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function (bidResponse) { // change default here return bidResponse.pbHg; } }, { - key: 'hb_size', + key: CONSTANTS.TARGETING_KEYS.SIZE, val: function (bidResponse) { return bidResponse.size; } }, { - key: 'hb_source', + key: CONSTANTS.TARGETING_KEYS.SOURCE, val: function (bidResponse) { return bidResponse.source; } }, { - key: 'hb_format', + key: CONSTANTS.TARGETING_KEYS.FORMAT, val: function (bidResponse) { return bidResponse.mediaType; } @@ -216,35 +230,105 @@ describe('auctionmanager.js', function () { }; var expected = getDefaultExpected(bid); - expected.hb_pb = bid.pbHg; + expected[CONSTANTS.TARGETING_KEYS.PRICE_BUCKET] = bid.pbHg; var response = getKeyValueTargetingPairs(bid.bidderCode, bid); assert.deepEqual(response, expected); }); + it('Custom configuration for all bidders with video bid', function () { + config.setConfig({ + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } + }); + let videoBid = utils.deepClone(bid); + videoBid.mediaType = 'video'; + videoBid.videoCacheKey = 'abc123def'; + + $$PREBID_GLOBAL$$.bidderSettings = + { + standard: { + adserverTargeting: [ + { + key: CONSTANTS.TARGETING_KEYS.BIDDER, + val: function (bidResponse) { + return bidResponse.bidderCode; + } + }, { + key: CONSTANTS.TARGETING_KEYS.AD_ID, + val: function (bidResponse) { + return bidResponse.adId; + } + }, { + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, + val: function (bidResponse) { + return bidResponse.pbMg; + } + }, { + key: CONSTANTS.TARGETING_KEYS.SIZE, + val: function (bidResponse) { + return bidResponse.size; + } + }, + { + key: CONSTANTS.TARGETING_KEYS.SOURCE, + val: function (bidResponse) { + return bidResponse.source; + } + }, + { + key: CONSTANTS.TARGETING_KEYS.FORMAT, + val: function (bidResponse) { + return bidResponse.mediaType; + } + }, + { + key: CONSTANTS.TARGETING_KEYS.UUID, + val: function (bidResponse) { + return bidResponse.videoCacheKey; + } + }, + { + key: CONSTANTS.TARGETING_KEYS.CACHE_ID, + val: function (bidResponse) { + return bidResponse.videoCacheKey; + } + } + ] + + } + }; + + let expected = getDefaultExpected(videoBid); + + let response = getKeyValueTargetingPairs(videoBid.bidderCode, videoBid); + assert.deepEqual(response, expected); + }); + it('Custom configuration for one bidder', function () { $$PREBID_GLOBAL$$.bidderSettings = { appnexus: { adserverTargeting: [ { - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function (bidResponse) { return bidResponse.bidderCode; } }, { - key: 'hb_adid', + key: CONSTANTS.TARGETING_KEYS.AD_ID, val: function (bidResponse) { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function (bidResponse) { // change default here return bidResponse.pbHg; } }, { - key: 'hb_size', + key: CONSTANTS.TARGETING_KEYS.SIZE, val: function (bidResponse) { return bidResponse.size; } @@ -255,7 +339,7 @@ describe('auctionmanager.js', function () { }; var expected = getDefaultExpected(bid); - expected.hb_pb = bid.pbHg; + expected[CONSTANTS.TARGETING_KEYS.PRICE_BUCKET] = bid.pbHg; var response = getKeyValueTargetingPairs(bid.bidderCode, bid); assert.deepEqual(response, expected); @@ -267,23 +351,23 @@ describe('auctionmanager.js', function () { nonExistentBidder: { adserverTargeting: [ { - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function (bidResponse) { return bidResponse.bidderCode; } }, { - key: 'hb_adid', + key: CONSTANTS.TARGETING_KEYS.AD_ID, val: function (bidResponse) { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function (bidResponse) { // change default here return bidResponse.pbHg; } }, { - key: 'hb_size', + key: CONSTANTS.TARGETING_KEYS.SIZE, val: function (bidResponse) { return bidResponse.size; } @@ -312,17 +396,17 @@ describe('auctionmanager.js', function () { }, adserverTargeting: [ { - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function (bidResponse) { return bidResponse.bidderCode; } }, { - key: 'hb_adid', + key: CONSTANTS.TARGETING_KEYS.AD_ID, val: function (bidResponse) { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function (bidResponse) { // change default here return 10.00; @@ -332,8 +416,8 @@ describe('auctionmanager.js', function () { } }; - var expected = getDefaultExpected(bid, ['hb_bidder', 'hb_adid']); - expected.hb_pb = 10.0; + var expected = getDefaultExpected(bid, [CONSTANTS.TARGETING_KEYS.BIDDER, CONSTANTS.TARGETING_KEYS.AD_ID]); + expected[CONSTANTS.TARGETING_KEYS.PRICE_BUCKET] = 10.0; var response = getKeyValueTargetingPairs(bid.bidderCode, bid); assert.deepEqual(response, expected); @@ -341,7 +425,7 @@ describe('auctionmanager.js', function () { it('Standard bidCpmAdjustment changes the bid of any bidder', function () { const bid = Object.assign({}, - bidfactory.createBid(2), + createBid(2), fixtures.getBidResponses()[5] ); @@ -369,17 +453,17 @@ describe('auctionmanager.js', function () { }, adserverTargeting: [ { - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function (bidResponse) { return bidResponse.bidderCode; } }, { - key: 'hb_adid', + key: CONSTANTS.TARGETING_KEYS.AD_ID, val: function (bidResponse) { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function (bidResponse) { // change default here return 15.00; @@ -390,24 +474,24 @@ describe('auctionmanager.js', function () { standard: { adserverTargeting: [ { - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function (bidResponse) { return bidResponse.bidderCode; } }, { - key: 'hb_adid', + key: CONSTANTS.TARGETING_KEYS.AD_ID, val: function (bidResponse) { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function (bidResponse) { // change default here return 10.00; }, }, { - key: 'hb_size', + key: CONSTANTS.TARGETING_KEYS.SIZE, val: function (bidResponse) { return bidResponse.size; } @@ -416,8 +500,8 @@ describe('auctionmanager.js', function () { } }; - var expected = getDefaultExpected(bid, ['hb_bidder', 'hb_adid', 'hb_size']); - expected.hb_pb = 15.0; + var expected = getDefaultExpected(bid, [CONSTANTS.TARGETING_KEYS.BIDDER, CONSTANTS.TARGETING_KEYS.AD_ID, CONSTANTS.TARGETING_KEYS.SIZE]); + expected[CONSTANTS.TARGETING_KEYS.PRICE_BUCKET] = 15.0; var response = getKeyValueTargetingPairs(bid.bidderCode, bid); assert.deepEqual(response, expected); @@ -430,17 +514,17 @@ describe('auctionmanager.js', function () { sendStandardTargeting: false, adserverTargeting: [ { - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function (bidResponse) { return bidResponse.bidderCode; } }, { - key: 'hb_adid', + key: CONSTANTS.TARGETING_KEYS.AD_ID, val: function (bidResponse) { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function (bidResponse) { return bidResponse.pbHg; } @@ -449,7 +533,7 @@ describe('auctionmanager.js', function () { } }; var expected = getDefaultExpected(bid); - expected.hb_pb = 5.57; + expected[CONSTANTS.TARGETING_KEYS.PRICE_BUCKET] = 5.57; var response = getKeyValueTargetingPairs(bid.bidderCode, bid); assert.deepEqual(response, expected); @@ -483,10 +567,10 @@ describe('auctionmanager.js', function () { }); }); - describe('adjustBids', () => { - it('should adjust bids if greater than zero and pass copy of bid object', () => { + describe('adjustBids', function () { + it('should adjust bids if greater than zero and pass copy of bid object', function () { const bid = Object.assign({}, - bidfactory.createBid(2), + createBid(2), fixtures.getBidResponses()[5] ); @@ -532,7 +616,7 @@ describe('auctionmanager.js', function () { }); }); - describe('addBidResponse', () => { + describe('addBidResponse', function () { let createAuctionStub; let adUnits; let adUnitCodes; @@ -542,23 +626,20 @@ describe('auctionmanager.js', function () { let bids = TEST_BIDS; let makeRequestsStub; - before(() => { - makeRequestsStub = sinon.stub(adaptermanager, 'makeBidRequests'); - - ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockAjaxBuilder); + before(function () { + makeRequestsStub = sinon.stub(adapterManager, 'makeBidRequests'); }); - after(() => { - ajaxStub.restore(); - adaptermanager.makeBidRequests.restore(); + after(function () { + adapterManager.makeBidRequests.restore(); }); - describe('when auction timeout is 3000', () => { - let loadScriptStub; - before(() => { + describe('when auction timeout is 3000', function () { + before(function () { + ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockAjaxBuilder); makeRequestsStub.returns(TEST_BID_REQS); }); - beforeEach(() => { + beforeEach(function () { adUnits = [{ code: ADUNIT_CODE, bids: [ @@ -570,17 +651,16 @@ describe('auctionmanager.js', function () { createAuctionStub = sinon.stub(auctionModule, 'newAuction'); createAuctionStub.returns(auction); - loadScriptStub = sinon.stub(adloader, 'loadScript').callsFake((...args) => { - args[1](); - }); - spec = mockBidder(BIDDER_CODE, bids); registerBidder(spec); }); - afterEach(() => { + afterEach(function () { auctionModule.newAuction.restore(); - loadScriptStub.restore(); + }); + + after(function () { + ajaxStub.restore(); }); function checkPbDg(cpm, expected, msg) { @@ -605,25 +685,25 @@ describe('auctionmanager.js', function () { it('should return proper price bucket increments for dense mode when cpm is 20+', checkPbDg('73.07', '20.00', '20+ caps at 20.00')); - it('should place dealIds in adserver targeting', () => { + it('should place dealIds in adserver targeting', function () { bids[0].dealId = 'test deal'; auction.callBids(); let registeredBid = auction.getBidsReceived().pop(); - assert.equal(registeredBid.adserverTargeting[`hb_deal`], 'test deal', 'dealId placed in adserverTargeting'); + assert.equal(registeredBid.adserverTargeting[CONSTANTS.TARGETING_KEYS.DEAL], 'test deal', 'dealId placed in adserverTargeting'); }); - it('should pass through default adserverTargeting sent from adapter', () => { + it('should pass through default adserverTargeting sent from adapter', function () { bids[0].adserverTargeting = {}; bids[0].adserverTargeting.extra = 'stuff'; auction.callBids(); let registeredBid = auction.getBidsReceived().pop(); - assert.equal(registeredBid.adserverTargeting.hb_bidder, BIDDER_CODE); + assert.equal(registeredBid.adserverTargeting[CONSTANTS.TARGETING_KEYS.BIDDER], BIDDER_CODE); assert.equal(registeredBid.adserverTargeting.extra, 'stuff'); }); - it('installs publisher-defined renderers on bids', () => { + it('installs publisher-defined renderers on bids', function () { let renderer = { url: 'renderer.js', render: (bid) => bid @@ -675,19 +755,20 @@ describe('auctionmanager.js', function () { }); }); - describe('when auction timeout is 20', () => { - let loadScriptStub; + describe('when auction timeout is 20', function () { let eventsEmitSpy; - let getBidderRequestStub; + let server; - before(() => { + before(function () { bids = [mockBid(), mockBid({ bidderCode: BIDDER_CODE1 })]; let bidRequests = bids.map(bid => mockBidRequest(bid)); makeRequestsStub.returns(bidRequests); }); - beforeEach(() => { + beforeEach(function () { + server = sinon.createFakeServer(); + adUnits = [{ code: ADUNIT_CODE, bids: [ @@ -696,47 +777,84 @@ describe('auctionmanager.js', function () { }]; adUnitCodes = [ADUNIT_CODE]; - auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: 20}); - createAuctionStub = sinon.stub(auctionModule, 'newAuction'); - createAuctionStub.returns(auction); + eventsEmitSpy = sinon.spy(events, 'emit'); + }); + afterEach(function () { + server.restore(); + events.emit.restore(); + }); - loadScriptStub = sinon.stub(adloader, 'loadScript').callsFake((...args) => { - args[1](); - }); + it('should emit BID_TIMEOUT and AUCTION_END for timed out bids', function (done) { + const spec1 = mockBidder(BIDDER_CODE, [bids[0]]); + registerBidder(spec1); + const spec2 = mockBidder(BIDDER_CODE1, [bids[1]]); + registerBidder(spec2); - spec = mockBidder(BIDDER_CODE, [bids[0]]); - registerBidder(spec); + function respondToRequest(requestIndex) { + server.requests[requestIndex].respond(200, {}, 'response body'); + } + function auctionCallback() { + const bidTimeoutCall = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.BID_TIMEOUT).getCalls()[0]; + const timedOutBids = bidTimeoutCall.args[1]; + assert.equal(timedOutBids.length, 1); + assert.equal(timedOutBids[0].bidder, BIDDER_CODE1); + + const auctionEndCall = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.AUCTION_END).getCalls()[0]; + const auctionProps = auctionEndCall.args[1]; + assert.equal(auctionProps.adUnits, adUnits); + assert.equal(auctionProps.timeout, 20); + assert.equal(auctionProps.auctionStatus, AUCTION_COMPLETED) + done(); + } + auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: auctionCallback, cbTimeout: 20}); - // Timeout is checked when bid is received. If that bid is the only one - // auction is waiting for, timeout is not emitted, so we need to add a - // second bidder to get timeout event. - let spec1 = mockBidder(BIDDER_CODE1, [bids[1]]); + auction.callBids(); + respondToRequest(0); + }); + it('should NOT emit BID_TIMEOUT when all bidders responded in time', function (done) { + const spec1 = mockBidder(BIDDER_CODE, [bids[0]]); registerBidder(spec1); + const spec2 = mockBidder(BIDDER_CODE1, [bids[1]]); + registerBidder(spec2); - eventsEmitSpy = sinon.spy(events, 'emit'); + function respondToRequest(requestIndex) { + server.requests[requestIndex].respond(200, {}, 'response body'); + } + function auctionCallback() { + assert.ok(eventsEmitSpy.withArgs(CONSTANTS.EVENTS.BID_TIMEOUT).notCalled, 'did not emit event BID_TIMEOUT'); + done(); + } + auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: auctionCallback, cbTimeout: 20}); - let origGBR = utils.getBidderRequest; - getBidderRequestStub = sinon.stub(utils, 'getBidderRequest'); - getBidderRequestStub.callsFake((bidRequests, bidder, adUnitCode) => { - let req = origGBR(bidRequests, bidder, adUnitCode); - req.start = 1000; - return req; - }); - }); - afterEach(() => { - auctionModule.newAuction.restore(); - loadScriptStub.restore(); - events.emit.restore(); - getBidderRequestStub.restore(); + auction.callBids(); + respondToRequest(0); + respondToRequest(1); }); - it('should emit BID_TIMEOUT for timed out bids', () => { + it('should NOT emit BID_TIMEOUT for bidders which responded in time but with an empty bid', function (done) { + const spec1 = mockBidder(BIDDER_CODE, []); + registerBidder(spec1); + const spec2 = mockBidder(BIDDER_CODE1, []); + registerBidder(spec2); + + function respondToRequest(requestIndex) { + server.requests[requestIndex].respond(200, {}, 'response body'); + } + function auctionCallback() { + const bidTimeoutCall = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.BID_TIMEOUT).getCalls()[0]; + const timedOutBids = bidTimeoutCall.args[1]; + assert.equal(timedOutBids.length, 1); + assert.equal(timedOutBids[0].bidder, BIDDER_CODE1); + done(); + } + auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: auctionCallback, cbTimeout: 20}); + auction.callBids(); - assert.ok(eventsEmitSpy.calledWith(CONSTANTS.EVENTS.BID_TIMEOUT), 'emitted events BID_TIMEOUT'); + respondToRequest(0); }); }); }); - describe('addBidResponse', () => { + describe('addBidResponse', function () { let createAuctionStub; let adUnits; let adUnitCodes; @@ -748,23 +866,23 @@ describe('auctionmanager.js', function () { let bids = TEST_BIDS; let bids1 = [mockBid({ bidderCode: BIDDER_CODE1 })]; - before(() => { + before(function () { let bidRequests = [ mockBidRequest(bids[0]), mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }) ]; - let makeRequestsStub = sinon.stub(adaptermanager, 'makeBidRequests'); + let makeRequestsStub = sinon.stub(adapterManager, 'makeBidRequests'); makeRequestsStub.returns(bidRequests); ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockAjaxBuilder); }); - after(() => { + after(function () { ajaxStub.restore(); - adaptermanager.makeBidRequests.restore(); + adapterManager.makeBidRequests.restore(); }); - beforeEach(() => { + beforeEach(function () { adUnits = [{ code: ADUNIT_CODE, bids: [ @@ -788,20 +906,20 @@ describe('auctionmanager.js', function () { registerBidder(spec1); }); - afterEach(() => { + afterEach(function () { auctionModule.newAuction.restore(); }); - it('should not alter bid adID', () => { + it('should not alter bid requestID', function () { auction.callBids(); const addedBid2 = auction.getBidsReceived().pop(); - assert.equal(addedBid2.adId, bids1[0].requestId); + assert.equal(addedBid2.requestId, bids1[0].requestId); const addedBid1 = auction.getBidsReceived().pop(); - assert.equal(addedBid1.adId, bids[0].requestId); + assert.equal(addedBid1.requestId, bids[0].requestId); }); - it('should not add banner bids that have no width or height', () => { + it('should not add banner bids that have no width or height', function () { bids1[0].width = undefined; bids1[0].height = undefined; @@ -813,7 +931,7 @@ describe('auctionmanager.js', function () { assert.equal(length, 1); }); - it('should run auction after video bids have been cached', () => { + it('should run auction after video bids have been cached', function () { sinon.stub(store, 'store').callsArgWith(1, null, [{ uuid: 123 }]); sinon.stub(config, 'getConfig').withArgs('cache.url').returns('cache-url'); @@ -832,7 +950,7 @@ describe('auctionmanager.js', function () { store.store.restore(); }); - it('runs auction after video responses with multiple bid objects have been cached', () => { + it('runs auction after video responses with multiple bid objects have been cached', function () { sinon.stub(store, 'store').callsArgWith(1, null, [{ uuid: 123 }]); sinon.stub(config, 'getConfig').withArgs('cache.url').returns('cache-url'); @@ -857,4 +975,86 @@ describe('auctionmanager.js', function () { store.store.restore(); }); }); + + describe('auctionCallbacks', function() { + let bids = TEST_BIDS; + let bidRequests; + let xhr; + let requests; + let doneSpy; + let auction = { + getBidRequests: () => bidRequests, + getAuctionId: () => '1', + addBidReceived: () => true, + getTimeout: () => 1000 + } + + beforeEach(() => { + doneSpy = sinon.spy(); + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = (request) => requests.push(request); + config.setConfig({ + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } + }) + }); + + afterEach(() => { + doneSpy.resetHistory(); + xhr.restore(); + config.resetConfig(); + }); + + it('should call auction done after bid is added to auction for mediaType banner', function () { + let ADUNIT_CODE2 = 'adUnitCode2'; + let BIDDER_CODE2 = 'sampleBidder2'; + + let bids1 = [mockBid({ bidderCode: BIDDER_CODE1 })]; + let bids2 = [mockBid({ bidderCode: BIDDER_CODE2 })]; + bidRequests = [ + mockBidRequest(bids[0]), + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE2 }) + ]; + let cbs = auctionCallbacks(doneSpy, auction); + cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE, bids[0]); + cbs.adapterDone.call(bidRequests[0]); + cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids1[0]); + cbs.adapterDone.call(bidRequests[1]); + cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE2, bids2[0]); + cbs.adapterDone.call(bidRequests[2]); + assert.equal(doneSpy.callCount, 1); + }); + + it('should call auction done after prebid cache is complete for mediaType video', function() { + bids[0].mediaType = 'video'; + let bids1 = [mockBid({ bidderCode: BIDDER_CODE1 })]; + + let opts = { + mediaType: { + video: { + context: 'instream', + playerSize: [640, 480], + }, + } + }; + bidRequests = [ + mockBidRequest(bids[0], opts), + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + ]; + + let cbs = auctionCallbacks(doneSpy, auction); + cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE, bids[0]); + cbs.adapterDone.call(bidRequests[0]); + cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids1[0]); + cbs.adapterDone.call(bidRequests[1]); + assert.equal(doneSpy.callCount, 0); + const uuid = 'c488b101-af3e-4a99-b538-00423e5a3371'; + const responseBody = `{"responses":[{"uuid":"${uuid}"}]}`; + requests[0].respond(200, { 'Content-Type': 'application/json' }, responseBody); + assert.equal(doneSpy.callCount, 1); + }) + }); }); diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 6b26d7da76a..196e167420a 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -8,10 +8,10 @@ let getConfig; let setConfig; let setDefaults; -describe('config API', () => { +describe('config API', function () { let logErrorSpy; let logWarnSpy; - beforeEach(() => { + beforeEach(function () { const config = newConfig(); getConfig = config.getConfig; setConfig = config.setConfig; @@ -20,30 +20,30 @@ describe('config API', () => { logWarnSpy = sinon.spy(utils, 'logWarn'); }); - afterEach(() => { + afterEach(function () { utils.logError.restore(); utils.logWarn.restore(); }); - it('setConfig is a function', () => { + it('setConfig is a function', function () { expect(setConfig).to.be.a('function'); }); - it('getConfig returns an object', () => { + it('getConfig returns an object', function () { expect(getConfig()).to.be.a('object'); }); - it('sets and gets arbitrary configuarion properties', () => { + it('sets and gets arbitrary configuarion properties', function () { setConfig({ baz: 'qux' }); expect(getConfig('baz')).to.equal('qux'); }); - it('only accepts objects', () => { + it('only accepts objects', function () { setConfig('invalid'); expect(getConfig('0')).to.not.equal('i'); }); - it('sets multiple config properties', () => { + it('sets multiple config properties', function () { setConfig({ foo: 'bar' }); setConfig({ biz: 'buz' }); var config = getConfig(); @@ -51,28 +51,28 @@ describe('config API', () => { expect(config.biz).to.equal('buz'); }); - it('overwrites existing config properties', () => { + it('overwrites existing config properties', function () { setConfig({ foo: {biz: 'buz'} }); setConfig({ foo: {baz: 'qux'} }); expect(getConfig('foo')).to.eql({baz: 'qux'}); }); - it('sets debugging', () => { + it('sets debugging', function () { setConfig({ debug: true }); expect(getConfig('debug')).to.be.true; }); - it('sets bidderTimeout', () => { + it('sets bidderTimeout', function () { setConfig({ bidderTimeout: 1000 }); expect(getConfig('bidderTimeout')).to.be.equal(1000); }); - it('gets user-defined publisherDomain', () => { + it('gets user-defined publisherDomain', function () { setConfig({ publisherDomain: 'fc.kahuna' }); expect(getConfig('publisherDomain')).to.equal('fc.kahuna'); }); - it('gets default userSync config', () => { + it('gets default userSync config', function () { const DEFAULT_USERSYNC = { syncEnabled: true, pixelEnabled: true, @@ -83,7 +83,7 @@ describe('config API', () => { expect(getConfig('userSync')).to.eql(DEFAULT_USERSYNC); }); - it('has subscribe functionality for adding listeners to config updates', () => { + it('has subscribe functionality for adding listeners to config updates', function () { const listener = sinon.spy(); getConfig(listener); @@ -94,7 +94,7 @@ describe('config API', () => { sinon.assert.calledWith(listener, { foo: 'bar' }); }); - it('subscribers can unsubscribe', () => { + it('subscribers can unsubscribe', function () { const listener = sinon.spy(); const unsubscribe = getConfig(listener); @@ -106,7 +106,7 @@ describe('config API', () => { sinon.assert.notCalled(listener); }); - it('subscribers can subscribe to topics', () => { + it('subscribers can subscribe to topics', function () { const listener = sinon.spy(); getConfig('logging', listener); @@ -117,7 +117,7 @@ describe('config API', () => { sinon.assert.calledWithExactly(listener, { logging: true }); }); - it('topic subscribers are only called when that topic is changed', () => { + it('topic subscribers are only called when that topic is changed', function () { const listener = sinon.spy(); const wildcard = sinon.spy(); @@ -130,12 +130,12 @@ describe('config API', () => { sinon.assert.calledOnce(wildcard); }); - it('sets priceGranularity', () => { + it('sets priceGranularity', function () { setConfig({ priceGranularity: 'low' }); expect(getConfig('priceGranularity')).to.be.equal('low'); }); - it('set mediaTypePriceGranularity', () => { + it('set mediaTypePriceGranularity', function () { const customPriceGranularity = { 'buckets': [{ 'min': 0, @@ -158,7 +158,7 @@ describe('config API', () => { expect(configResult.native).to.be.equal('medium'); }); - it('sets priceGranularity and customPriceBucket', () => { + it('sets priceGranularity and customPriceBucket', function () { const goodConfig = { 'buckets': [{ 'min': 0, @@ -172,18 +172,18 @@ describe('config API', () => { expect(getConfig('customPriceBucket')).to.equal(goodConfig); }); - it('should log error for invalid priceGranularity', () => { + it('should log error for invalid priceGranularity', function () { setConfig({ priceGranularity: '' }); const error = 'Prebid Error: no value passed to `setPriceGranularity()`'; assert.ok(logErrorSpy.calledWith(error), 'expected error was logged'); }); - it('should log a warning on invalid values', () => { + it('should log a warning on invalid values', function () { setConfig({ bidderSequence: 'unrecognized sequence' }); expect(logWarnSpy.calledOnce).to.equal(true); }); - it('should not log warnings when given recognized values', () => { + it('should not log warnings when given recognized values', function () { setConfig({ bidderSequence: 'fixed' }); setConfig({ bidderSequence: 'random' }); expect(logWarnSpy.called).to.equal(false); diff --git a/test/spec/cpmBucketManager_spec.js b/test/spec/cpmBucketManager_spec.js index 3d56299ebfd..88e04be9ad6 100644 --- a/test/spec/cpmBucketManager_spec.js +++ b/test/spec/cpmBucketManager_spec.js @@ -2,8 +2,8 @@ import { expect } from 'chai'; import {getPriceBucketString, isValidPriceConfig} from 'src/cpmBucketManager'; let cpmFixtures = require('test/fixtures/cpmInputsOutputs.json'); -describe('cpmBucketManager', () => { - it('getPriceBucketString function generates the correct price strings', () => { +describe('cpmBucketManager', function () { + it('getPriceBucketString function generates the correct price strings', function () { let input = cpmFixtures.cpmInputs; for (let i = 0; i < input.length; i++) { let output = getPriceBucketString(input[i]); @@ -12,7 +12,7 @@ describe('cpmBucketManager', () => { } }); - it('gets the correct custom bucket strings', () => { + it('gets the correct custom bucket strings', function () { let cpm = 16.50908; let customConfig = { 'buckets': [{ @@ -35,7 +35,7 @@ describe('cpmBucketManager', () => { expect(JSON.stringify(output)).to.deep.equal(expected); }); - it('gets the correct custom bucket strings with irregular increment', () => { + it('gets the correct custom bucket strings with irregular increment', function () { let cpm = 14.50908; let customConfig = { 'buckets': [{ @@ -58,7 +58,7 @@ describe('cpmBucketManager', () => { expect(JSON.stringify(output)).to.deep.equal(expected); }); - it('gets the correct custom bucket strings in non-USD currency', () => { + it('gets the correct custom bucket strings in non-USD currency', function () { let cpm = 16.50908 * 110.49; let customConfig = { 'buckets': [{ @@ -81,7 +81,81 @@ describe('cpmBucketManager', () => { expect(JSON.stringify(output)).to.deep.equal(expected); }); - it('gets custom bucket strings and it should honor 0', () => { + it('gets the correct custom bucket strings with specific cpms that round oddly with certain increments', function () { + let customConfig = { + 'buckets': [{ + 'precision': 4, + 'min': 0, + 'max': 4, + 'increment': 0.10, + }] + }; + let cpm = 2.21; + let expected = '{"low":"2.00","med":"2.20","high":"2.21","auto":"2.20","dense":"2.21","custom":"2.2000"}'; + let output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + + cpm = 3.15; + expected = '{"low":"3.00","med":"3.10","high":"3.15","auto":"3.15","dense":"3.15","custom":"3.1000"}'; + output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + + customConfig = { + 'buckets': [{ + 'precision': 3, + 'min': 0, + 'max': 6, + 'increment': 0.08, + }] + }; + cpm = 4.89; + expected = '{"low":"4.50","med":"4.80","high":"4.89","auto":"4.85","dense":"4.85","custom":"4.880"}'; + output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + + customConfig = { + 'buckets': [{ + 'precision': 3, + 'min': 0, + 'max': 6, + 'increment': 0.05, + }] + }; + cpm = 2.98; + expected = '{"low":"2.50","med":"2.90","high":"2.98","auto":"2.95","dense":"2.98","custom":"2.950"}'; + output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + + cpm = 2.99; + expected = '{"low":"2.50","med":"2.90","high":"2.99","auto":"2.95","dense":"2.99","custom":"2.950"}'; + output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + + customConfig = { + 'buckets': [{ + 'precision': 2, + 'min': 0, + 'max': 6, + 'increment': 0.01, + }] + }; + cpm = 4.01; + expected = '{"low":"4.00","med":"4.00","high":"4.01","auto":"4.00","dense":"4.00","custom":"4.01"}'; + output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + + cpm = 4.68; + expected = '{"low":"4.50","med":"4.60","high":"4.68","auto":"4.65","dense":"4.65","custom":"4.68"}'; + output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + + cpm = 4.69; + expected = '{"low":"4.50","med":"4.60","high":"4.69","auto":"4.65","dense":"4.65","custom":"4.69"}'; + output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + }); + + it('gets custom bucket strings and it should honor 0', function () { let cpm = 16.50908; let customConfig = { 'buckets': [ @@ -98,7 +172,7 @@ describe('cpmBucketManager', () => { expect(JSON.stringify(output)).to.deep.equal(expected); }); - it('gets the custom bucket strings without passing precision and it should honor the default precision', () => { + it('gets the custom bucket strings without passing precision and it should honor the default precision', function () { let cpm = 16.50908; let customConfig = { 'buckets': [ @@ -114,7 +188,7 @@ describe('cpmBucketManager', () => { expect(JSON.stringify(output)).to.deep.equal(expected); }); - it('checks whether custom config is valid', () => { + it('checks whether custom config is valid', function () { let badConfig = { 'buckets': [{ 'min': 0, diff --git a/test/spec/debugging_spec.js b/test/spec/debugging_spec.js new file mode 100644 index 00000000000..ba9702b0324 --- /dev/null +++ b/test/spec/debugging_spec.js @@ -0,0 +1,144 @@ + +import { expect } from 'chai'; +import { sessionLoader, addBidResponseHook, getConfig, disableOverrides, boundHook } from 'src/debugging'; +import { addBidResponse } from 'src/auction'; +import { config } from 'src/config'; + +describe('bid overrides', function () { + let sandbox; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + window.sessionStorage.clear(); + config.resetConfig(); + sandbox.restore(); + }); + + describe('initialization', function () { + beforeEach(function () { + sandbox.stub(config, 'setConfig'); + }); + + afterEach(function () { + disableOverrides(); + }); + + it('should happen when enabled with setConfig', function () { + getConfig({ + enabled: true + }); + + expect(addBidResponse.getHooks().some(hook => hook.hook === boundHook)).to.equal(true); + }); + + it('should happen when configuration found in sessionStorage', function () { + sessionLoader({ + getItem: () => ('{"enabled": true}') + }); + expect(addBidResponse.getHooks().some(hook => hook.hook === boundHook)).to.equal(true); + }); + + it('should not throw if sessionStorage is inaccessible', function () { + expect(() => { + sessionLoader({ + getItem() { + throw new Error('test'); + } + }); + }).not.to.throw(); + }); + }); + + describe('hook', function () { + let mockBids; + let bids; + + beforeEach(function () { + let baseBid = { + 'bidderCode': 'rubicon', + 'width': 970, + 'height': 250, + 'statusMessage': 'Bid available', + 'mediaType': 'banner', + 'source': 'client', + 'currency': 'USD', + 'cpm': 0.5, + 'ttl': 300, + 'netRevenue': false, + 'adUnitCode': '/19968336/header-bid-tag-0' + }; + mockBids = []; + mockBids.push(baseBid); + mockBids.push(Object.assign({}, baseBid, { + bidderCode: 'appnexus' + })); + + bids = []; + }); + + function run(overrides) { + mockBids.forEach(bid => { + let next = (adUnitCode, bid) => { + bids.push(bid); + }; + addBidResponseHook.bind(overrides)(next, bid.adUnitCode, bid) + }); + } + + it('should allow us to exclude bidders', function () { + run({ + enabled: true, + bidders: ['appnexus'] + }); + + expect(bids.length).to.equal(1); + expect(bids[0].bidderCode).to.equal('appnexus'); + }); + + it('should allow us to override all bids', function () { + run({ + enabled: true, + bids: [{ + cpm: 2 + }] + }); + + expect(bids.length).to.equal(2); + expect(bids[0].cpm).to.equal(2); + expect(bids[1].cpm).to.equal(2); + }); + + it('should allow us to override bids by bidder', function () { + run({ + enabled: true, + bids: [{ + bidder: 'rubicon', + cpm: 2 + }] + }); + + expect(bids.length).to.equal(2); + expect(bids[0].cpm).to.equal(2); + expect(bids[1].cpm).to.equal(0.5); + }); + + it('should allow us to override bids by adUnitCode', function () { + mockBids[1].adUnitCode = 'test'; + + run({ + enabled: true, + bids: [{ + adUnitCode: 'test', + cpm: 2 + }] + }); + + expect(bids.length).to.equal(2); + expect(bids[0].cpm).to.equal(0.5); + expect(bids[1].cpm).to.equal(2); + }); + }); +}); diff --git a/test/spec/e2e/banner/basic_banner_ad.spec.js b/test/spec/e2e/banner/basic_banner_ad.spec.js new file mode 100644 index 00000000000..ee01ce627db --- /dev/null +++ b/test/spec/e2e/banner/basic_banner_ad.spec.js @@ -0,0 +1,48 @@ +const expect = require('chai').expect; +const { host, protocol } = require('../../../helpers/testing-utils'); + +const TEST_PAGE_URL = `${protocol}://${host}:9999/test/pages/banner.html`; +const CREATIVE_IFRAME_CSS_SELECTOR = 'iframe[id="google_ads_iframe_/19968336/header-bid-tag-0_0"]'; + +const EXPECTED_TARGETING_KEYS = { + hb_format: 'banner', + hb_source: 'client', + hb_pb: '0.50', + hb_bidder: 'appnexus', + hb_format_appnexus: 'banner', + hb_source_appnexus: 'client', + hb_pb_appnexus: '0.50', + hb_bidder_appnexus: 'appnexus' +} + +describe('Prebid.js Banner Ad Unit Test', function () { + before(function loadTestPage() { + browser.url(TEST_PAGE_URL).pause(3000); + try { + browser.waitForExist(CREATIVE_IFRAME_CSS_SELECTOR, 2000); + const creativeIframe = $(CREATIVE_IFRAME_CSS_SELECTOR).value; + browser.frame(creativeIframe); + } catch (e) { + // If creative Iframe didn't load, repeat the steps again! + // Due to some reason if the Ad server doesn't respond, the test case will time out after 60000 ms as defined in file wdio.conf.js + loadTestPage(); + } + }); + + it('should load the targeting keys with correct values', function () { + const result = browser.execute(function () { + return window.top.pbjs.getAdserverTargeting('div-gpt-ad-1460505748561-1'); + }); + + const targetingKeys = result.value['div-gpt-ad-1460505748561-1']; + + expect(targetingKeys).to.include(EXPECTED_TARGETING_KEYS); + expect(targetingKeys.hb_adid).to.be.a('string'); + expect(targetingKeys.hb_adid_appnexus).to.be.a('string'); + expect(targetingKeys.hb_size).to.satisfy((size) => size === '300x250' || '300x600'); + }); + + it('should render the Banner Ad on the page', function () { + expect(browser.isVisible('body > div[class="GoogleActiveViewElement"] > a > img')).to.be.true; + }); +}); diff --git a/test/spec/e2e/common/globals.js b/test/spec/e2e/common/globals.js deleted file mode 100644 index 3e781d5fa21..00000000000 --- a/test/spec/e2e/common/globals.js +++ /dev/null @@ -1,9 +0,0 @@ -var HtmlReporter = require('nightwatch-html-reporter'); -var reporter = new HtmlReporter({ - openBrowser: true, - reportsDirectory: __dirname + '/reports', - themeName: 'cover', -}); -module.exports = { - reporter: reporter.fn -}; diff --git a/test/spec/e2e/common/utils.js b/test/spec/e2e/common/utils.js deleted file mode 100644 index 2c9fee68eda..00000000000 --- a/test/spec/e2e/common/utils.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - findIframeInDiv: function(divid) { - var div = document.getElementById(divid); - var iframes = div.getElementsByTagName('iframe'); - console.log(iframes.length); - try { - if (iframes.length === 1 && iframes[0].contentWindow.document.body.innerHTML === '') { - return false; - } else { - return true; - } - } catch (e) { - return true; - } - } -}; diff --git a/test/spec/e2e/custom-assertions/first.js b/test/spec/e2e/custom-assertions/first.js deleted file mode 100644 index e393e8c15a0..00000000000 --- a/test/spec/e2e/custom-assertions/first.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Checks if the given attribute of an element has the expected value. - * - * ``` - * this.demoTest = function (client) { - * browser.assert.attributeEquals('body', 'data-attr', 'some value'); - * }; - * ``` - * - * @method attributeEquals - * @param {string} selector The selector (CSS / Xpath) used to locate the element. - * @param {string} attribute The attribute name - * @param {string} expected The expected value of the attribute to check. - * @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default. - * @api assertions - */ - -var util = require('util'); -exports.assertion = function(expected, msg) { - var DEFAULT_MSG = 'Testing if attribute %s of <%s> equals "%s".'; - - this.message = msg; - - this.expected = function() { - return expected; - }; - - this.pass = function(value) { - return value === expected; - }; - - this.failure = function(result) { - var failed = false; - return failed; - }; - - this.value = function(result) { - console.log('**********'); - console.log(result); - return result.value; - }; - - this.command = function(callback) { - var _this = this; - var execcallback = function(result) { - // console.log(_this); - console.log('**********'); - console.log(result); - console.log(callback.toString()); - if (callback) { - return callback.call(_this, result.value); - } - }; - - this.api.execute(function() { - // cusotm logic - return 'hello'; - }, [], execcallback); - - // var result = {'value':'hello'}; - - return this; - }; -}; diff --git a/test/spec/e2e/custom-reporter/junit.xml.ejs b/test/spec/e2e/custom-reporter/junit.xml.ejs deleted file mode 100644 index f03e0ee38e5..00000000000 --- a/test/spec/e2e/custom-reporter/junit.xml.ejs +++ /dev/null @@ -1,32 +0,0 @@ - - - - - <% for (var item in module.completed) { - var testcase = module.completed[item]; - var assertions = testcase.assertions %> - <% - for (var i = 0; i < assertions.length; i++) { %><% if (assertions[i].failure) { %> <%= assertions[i].stackTrace %><% } %> -<% if (assertions[i].screenshots && assertions[i].screenshots.length > 0) { %><% for (var j = 0; j < assertions[i].screenshots.length; j++) { %>[[ATTACHMENT|<%= assertions[i].screenshots[j] %>]]<% } %><% } %> - <% } - if (testcase.failed > 0 && testcase.stackTrace) { - %><%= testcase.stackTrace %><% } %> - <% if (systemerr != '') {%> - - <%= systemerr %> - <% } %> - <% } %> - <% if (module.skipped && (module.skipped.length > 0)) { %> - <% for (var j = 0; j < module.skipped.length; j++) { %> - - - - <% } %> - <% } %> - - diff --git a/test/spec/e2e/custom-reporter/pbjs-html-reporter.js b/test/spec/e2e/custom-reporter/pbjs-html-reporter.js deleted file mode 100644 index bcd5eab1e21..00000000000 --- a/test/spec/e2e/custom-reporter/pbjs-html-reporter.js +++ /dev/null @@ -1,114 +0,0 @@ -var fs = require('fs'); -var mkpath = require('mkpath'); -var path = require('path'); -var ejs = require('ejs'); - -module.exports = new function() { - var tmpl = __dirname + '/junit.xml.ejs'; - var tmplData; - var globalResults; - - function loadTemplate(cb) { - if (tmplData) { - cb(tmplData); - return; - } - fs.readFile(tmpl, function (err, data) { - if (err) { - throw err; - } - tmplData = data.toString(); - cb(tmplData); - }); - } - - function adaptAssertions(module) { - Object.keys(module.completed).forEach(function(item) { - var testcase = module.completed[item]; - var assertions = testcase.assertions; - for (var i = 0; i < assertions.length; i++) { - if (assertions[i].stackTrace) { - assertions[i].stackTrace = stackTraceFilter(assertions[i].stackTrace.split('\n')); - } - } - - if (testcase.failed > 0 && testcase.stackTrace) { - var stackParts = testcase.stackTrace.split('\n'); - var errorMessage = stackParts.shift(); - testcase.stackTrace = stackTraceFilter(stackParts); - testcase.message = errorMessage; - } - }); - } - - function writeReport(moduleKey, data, opts, callback) { - var module = globalResults.modules[moduleKey]; - var pathParts = moduleKey.split(path.sep); - var moduleName = pathParts.pop(); - var output_folder = opts.output_folder; - adaptAssertions(module); - - var rendered = ejs.render(data, { - module: module, - moduleName: moduleName, - systemerr: globalResults.errmessages.join('\n'), - }); - - if (pathParts.length) { - output_folder = path.join(output_folder, pathParts.join(path.sep)); - mkpath.sync(output_folder); - } - - var filename = path.join(output_folder, opts.filename_prefix + moduleName + '.xml'); - fs.writeFile(filename, rendered, function(err) { - callback(err); - globalResults.errmessages.length = 0; - }); - } - - function stackTraceFilter(parts) { - var stack = parts.reduce(function(list, line) { - if (contains(line, [ - 'node_modules', - '(node.js:', - '(events.js:' - ])) { - return list; - } - - list.push(line); - return list; - }, []); - - return stack.join('\n'); - } - - function contains(str, text) { - if (Object.prototype.toString.call(text) === '[object Array]') { - for (var i = 0; i < text.length; i++) { - if (contains(str, text[i])) { - return true; - } - } - } - return str.indexOf(text) > -1; - } - - this.write = function(results, options, callback) { - options.filename_prefix = process.env.__NIGHTWATCH_ENV + '_'; - globalResults = results; - var keys = Object.keys(results.modules); - - loadTemplate(function createReport(data) { - var moduleKey = keys.shift(); - - writeReport(moduleKey, data, options, function(err) { - if (err || (keys.length === 0)) { - callback(err); - } else { - createReport(data); - } - }); - }); - }; -}(); diff --git a/test/spec/e2e/gpt-examples/all_bidders_instant_load.html b/test/spec/e2e/gpt-examples/all_bidders_instant_load.html deleted file mode 100644 index bf31b769431..00000000000 --- a/test/spec/e2e/gpt-examples/all_bidders_instant_load.html +++ /dev/null @@ -1,830 +0,0 @@ - - - - - - - - - -

    Prebid.js Test3

    - -

    adequant

    -
    -

    No response

    - -
    - -

    adform

    -
    -

    No response

    - -
    - - -

    aol

    -
    -

    No response

    - -
    - -

    appnexus

    -
    -

    No response

    - -
    - -

    indexExchange

    -
    -

    No response

    - -
    - -

    openx

    -
    -

    No response

    - -
    - -

    pubmatic

    -
    -

    No response

    - -
    - -

    pulsepoint

    -
    -

    No response

    - -
    - -

    rubicon

    -
    -

    No response

    - -
    - -

    sonobi

    -
    -

    No response

    - -
    - -

    sovrn

    -
    -

    No response

    - -
    - -

    springserve

    -
    -

    No response

    - -
    - -

    triplelift

    -
    -

    No response

    - -
    - -

    yieldbot

    -
    -

    No response

    - -
    - -

    nginad

    -
    -

    No response

    - -
    - -

    brightcom

    -
    -

    No response

    - -
    - -

    sekindo

    -
    -

    No response

    - -
    - -

    kruxlink

    -
    -

    No response

    - -
    - -

    AdMedia

    -
    -

    No response

    - -
    - - - - - - - - - - diff --git a/test/spec/e2e/gpt-examples/e2e_default.html b/test/spec/e2e/gpt-examples/e2e_default.html deleted file mode 100644 index 9e0437d6275..00000000000 --- a/test/spec/e2e/gpt-examples/e2e_default.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - -

    Prebid.js TestCase1

    - - -
    - -
    -
    - -
    - - - \ No newline at end of file diff --git a/test/spec/e2e/gpt-examples/gpt_default.html b/test/spec/e2e/gpt-examples/gpt_default.html deleted file mode 100644 index 93ec054b59a..00000000000 --- a/test/spec/e2e/gpt-examples/gpt_default.html +++ /dev/null @@ -1,684 +0,0 @@ - - - - - - - - - - -

    Prebid.js Test3

    - - -
    - -
    - -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - - -
    - -
    - -
    - -
    -
    - -
    -
    - -
    - - - -
    - -
    - - - - - - - - - diff --git a/test/spec/e2e/gpt-examples/gpt_outstream.html b/test/spec/e2e/gpt-examples/gpt_outstream.html deleted file mode 100644 index 42ba48c98e7..00000000000 --- a/test/spec/e2e/gpt-examples/gpt_outstream.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - Prebid.js outstream video example - - - - - -

    Prebid Outstream Video Ads

    - -
    -

    -In scelerisque sem sed tortor posuere sagittis. Fusce scelerisque odio at tincidunt ultricies. Fusce egestas, erat non finibus dictum, nulla arcu viverra nibh, at bibendum ligula nisi egestas magna. Nulla eu finibus nulla. Pellentesque at mi eget turpis consequat scelerisque. Sed lacinia, nisi sit amet egestas vestibulum, elit odio iaculis leo, et lacinia risus enim non lacus. Cras nec neque eget nunc gravida maximus. Ut hendrerit convallis sollicitudin. Donec cursus erat vel metus gravida, et pretium justo iaculis. Curabitur condimentum blandit augue, quis interdum leo. Vivamus dapibus est nec dui efficitur, eu imperdiet nulla sollicitudin. Suspendisse laoreet velit vitae arcu mollis, ac interdum lorem venenatis. Aenean nec purus varius, accumsan ex at, luctus arcu. Quisque consectetur tortor eros, placerat lacinia eros aliquam a. Proin non porttitor libero. -

    -

    -Proin eget vulputate est. Nunc sit amet neque a tortor ullamcorper suscipit non eu neque. Quisque at massa in metus feugiat rutrum. Nulla et orci orci. Aliquam erat volutpat. Cras tincidunt metus lectus, sed suscipit augue mollis vitae. Sed quis condimentum tortor, sit amet consectetur erat. Nulla pellentesque turpis lacus, eu venenatis massa fringilla at. Duis sed pharetra turpis. Maecenas vel porttitor neque. Praesent quis felis sapien. Donec suscipit euismod dui, vitae fermentum nisi ornare in. -

    -

    -Suspendisse tempor felis accumsan orci finibus, imperdiet mollis arcu imperdiet. In eu dolor condimentum, pulvinar nisl a, sollicitudin nunc. Ut vel lectus libero. Praesent rhoncus leo tortor, at mollis nulla sagittis eget. Quisque tempus tempor augue sed rutrum. Sed vitae volutpat quam. Proin vestibulum eros metus, a luctus erat condimentum eu. Vivamus ullamcorper ultricies dui, ac malesuada leo finibus semper. Cras diam augue, imperdiet sed efficitur id, aliquam sed purus. Praesent eget turpis quis sapien interdum sagittis. Vivamus placerat nunc a tempus fermentum. Praesent laoreet leo at tellus porta, ut viverra tortor pharetra. Quisque elit velit, eleifend eget imperdiet vel, suscipit ac nisi. Aliquam egestas mauris ut massa fringilla laoreet. -

    -

    -Quisque ac luctus nisi, vitae ornare arcu. Proin fermentum sapien vitae odio vestibulum porta. Suspendisse faucibus sapien enim, et faucibus urna tempus et. Integer porttitor justo sed faucibus blandit. Morbi semper lectus vitae semper facilisis. Quisque molestie accumsan arcu, eget bibendum dui euismod et. Sed in mattis lacus, nec lacinia sem. Fusce sed tortor posuere, iaculis justo varius, elementum est. -

    -

    -Etiam condimentum, eros commodo semper tristique, lorem leo pharetra massa, eget cursus justo enim id urna. Sed imperdiet mauris vitae ante bibendum elementum. Etiam eu dui porttitor leo imperdiet cursus. Maecenas consequat, neque a dapibus viverra, nunc velit volutpat nibh, ut cursus sem tortor ac arcu. Praesent convallis lacus vel nisi aliquam, in posuere libero scelerisque. Curabitur et lacinia nisl. Nunc id ligula neque. Phasellus non eros et leo ultrices ultricies. Nulla facilisi. Donec ut augue urna. Suspendisse sodales nisi at ex faucibus, et tempus magna fermentum. Proin non arcu interdum, pulvinar est at, vehicula odio. Morbi nec maximus sem. Ut eu tristique urna. -

    -

    -Pellentesque eget quam sem. Nam interdum eleifend leo, mattis sagittis metus ornare tristique. Cras pretium odio lectus, vitae viverra massa consequat eget. Suspendisse porttitor pretium lectus in scelerisque. Phasellus euismod porta lectus eget pharetra. Ut et viverra mi, ut imperdiet lacus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc tempus sapien sit amet tortor rhoncus dignissim. Sed at augue et sem lacinia feugiat. Nulla vitae convallis urna. Morbi scelerisque erat quis nibh pretium, non elementum elit consectetur. Proin in feugiat nisl. -

    -

    -Morbi et ipsum purus. Integer ut pulvinar metus. Fusce maximus ex nec purus sollicitudin gravida. Vivamus dapibus volutpat erat nec tristique. Aliquam mi dolor, pretium non elementum quis, viverra non est. Pellentesque egestas, lectus a posuere imperdiet, nisi sem elementum neque, eu volutpat arcu turpis venenatis magna. Curabitur non neque consectetur, vulputate urna sed, vestibulum lacus. Aenean mollis, risus non pulvinar egestas, lectus lectus finibus dui, sit amet pretium metus mauris vitae nibh. In non ultricies odio. -

    -

    -Donec dictum sem ac risus molestie lobortis. Maecenas at justo vehicula, iaculis orci eget, eleifend nunc. In non justo imperdiet, blandit leo in, interdum mi. Proin feugiat libero et erat dictum efficitur. Nunc auctor lacus feugiat erat euismod cursus. Sed vehicula ante vel quam pretium blandit. Maecenas congue quis mauris vitae efficitur. Cras sit amet justo at sem dictum ornare vitae eu ex. Nunc ornare odio nec leo consectetur cursus. Mauris eu dolor tellus. Etiam dignissim ut nunc et mollis. Cras at pulvinar velit, ut tincidunt velit. Cras vitae fermentum ante. Aenean interdum dolor in scelerisque consectetur. -

    -

    -Curabitur auctor leo sit amet massa faucibus ultrices. Maecenas dignissim libero ac cursus cursus. Curabitur eget sapien leo. Phasellus pretium blandit facilisis. Proin egestas urna a sagittis tempus. Donec in nibh ex. Vestibulum efficitur felis aliquam urna ultrices, at gravida nibh pretium. Morbi dictum vulputate pretium. Donec at nisi rutrum, pharetra nunc a, placerat felis. Quisque rhoncus congue fermentum. Quisque pharetra est at nisl sagittis suscipit. Maecenas scelerisque porta eleifend. Mauris nulla leo, consectetur at eros vel, elementum pretium diam. -

    -

    -In nisi libero, porta ut ullamcorper a, dapibus nec velit. Vestibulum congue rhoncus congue. Nulla a libero sit amet risus feugiat hendrerit id placerat ex. In hac habitasse platea dictumst. Pellentesque ut ullamcorper risus. Nunc et ipsum nisi. Vivamus a interdum diam, hendrerit pellentesque orci. -

    -

    -Vestibulum ut massa blandit, maximus sem vitae, vulputate mauris. Nam condimentum velit a facilisis dignissim. Nunc venenatis pharetra dapibus. Praesent ullamcorper risus sit amet molestie consectetur. Cras mauris felis, consequat et enim a, ultricies pretium enim. Nulla porttitor nunc mi, sed posuere magna venenatis non. Donec lobortis consectetur mauris, fermentum auctor dui dignissim sed. Sed vel venenatis urna. Donec velit velit, imperdiet non vulputate non, eleifend sed nisi. -

    -

    -Proin et turpis velit. Donec tempus dictum dolor, eget eleifend lacus. Donec eu felis in ante iaculis ultrices. Mauris varius, turpis quis venenatis convallis, enim dolor ornare justo, in dictum ipsum purus quis dolor. Ut condimentum feugiat lectus ut auctor. Maecenas luctus consequat erat, nec pretium urna pulvinar in. Donec gravida rhoncus aliquet. Cras aliquet odio eget orci hendrerit, non posuere velit dignissim. Nunc tempus aliquam iaculis. Nunc viverra lobortis mauris et malesuada. Donec congue suscipit mauris. Phasellus efficitur, leo at mollis maximus, lorem mauris pretium urna, nec scelerisque ante neque eu erat. Nam rhoncus malesuada velit nec ultricies. -

    -
    -

    Prebid Outstream Video Ad

    - -
    - -

    -Proin blandit in arcu sed porttitor. Morbi in erat vel risus mollis interdum. Proin vel odio semper, porttitor risus sed, tristique odio. Donec viverra massa et dui scelerisque, ac sagittis odio viverra. Aliquam lacinia enim sit amet dapibus ultrices. Nulla mollis, massa eget interdum egestas, lectus eros pretium eros, vel consectetur velit odio vel odio. Proin euismod aliquam finibus. Phasellus facilisis mollis est, non consequat lectus volutpat nec. -

    -

    -Ut vel ultricies erat. Pellentesque non ipsum quis odio ornare tempus in cursus ex. In a turpis non quam pulvinar tincidunt. Maecenas tortor neque, dapibus a quam aliquet, dictum pellentesque leo. Sed aliquet tellus est, in tempus magna congue ut. Phasellus at tincidunt lorem, id fringilla risus. Nunc vel pulvinar massa. Aliquam erat volutpat. Phasellus semper interdum justo at eleifend. Curabitur feugiat quam sed mollis facilisis. -

    -

    -Quisque consectetur sem a elit aliquet facilisis. Quisque dignissim velit at quam rhoncus dignissim. Proin feugiat sem at turpis ultrices imperdiet. Integer vel eros vel ante ultricies dapibus vitae eget dui. Fusce sollicitudin semper tortor at molestie. Pellentesque nec metus sed mauris aliquet iaculis. Mauris malesuada tortor nec mi dictum, feugiat euismod enim gravida. Vestibulum dapibus ut nulla vel euismod. Nunc lobortis, mauris at pretium faucibus, ligula diam venenatis nulla, in mattis sapien arcu feugiat dolor. In at dui leo. Cras elementum condimentum turpis. -

    -

    -Donec eget dolor ac nulla lobortis bibendum. Praesent commodo accumsan ligula eget commodo. Suspendisse sit amet dignissim metus. Sed ut eros viverra, viverra lectus eget, ornare eros. Mauris elementum lacinia dapibus. Donec magna nisl, suscipit quis mattis eget, tincidunt sed sapien. Curabitur elementum nulla eget lorem gravida dapibus. Nunc vel dolor et libero pretium interdum vitae eget mauris. Vestibulum et erat in nulla sollicitudin luctus ut quis nulla. -

    -

    -Maecenas iaculis pellentesque quam at fringilla. Donec dui elit, suscipit eget varius id, suscipit efficitur metus. Nulla rutrum ultrices tempor. Vivamus hendrerit justo ac fermentum euismod. Vestibulum tempus pulvinar tempus. Curabitur congue neque luctus dolor vehicula, non efficitur metus fringilla. Nam a imperdiet ex. Integer at est hendrerit, rutrum justo eu, ornare odio. Etiam convallis sapien a purus vehicula, eget gravida purus semper. Fusce ex enim, volutpat ac feugiat et, rhoncus vel tortor. Aenean ultrices libero sed neque fermentum tempor. Morbi tincidunt dui turpis, non mollis est dignissim at. Suspendisse molestie convallis interdum. Donec sit amet fermentum purus. -

    -

    -Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam et ex orci. Vivamus rutrum est vel porta imperdiet. Cras ultricies tortor dolor, nec mollis felis ullamcorper vel. Praesent scelerisque vehicula sem, nec feugiat mauris tempus ac. Donec at enim non sem commodo sodales. Ut sit amet risus sit amet ante viverra venenatis. Aliquam sodales mollis est eget ultricies. Etiam pulvinar sapien et ipsum elementum pharetra. -

    -

    -Nam blandit metus erat, sit amet congue ipsum cursus sed. In a commodo ante, sit amet tincidunt quam. Aenean lobortis et nibh in venenatis. Aliquam faucibus purus quis neque consectetur, quis consequat risus maximus. Proin mollis imperdiet felis, eget tempus ipsum scelerisque ut. Sed euismod interdum augue sed varius. Sed volutpat tellus ut risus porta accumsan. -

    -

    -Mauris aliquet eu arcu sed pharetra. Duis nec leo volutpat libero finibus malesuada in eget velit. Aliquam facilisis urna mauris, et aliquam ipsum dictum finibus. Donec eget mi fermentum, vehicula odio at, molestie orci. In a hendrerit lectus. Aenean congue ipsum ac imperdiet suscipit. Maecenas eleifend pretium metus id mollis. -

    -
    -

    Prebid Outstream Video Ad

    - -
    - -

    -Ut vel ultricies erat. Pellentesque non ipsum quis odio ornare tempus in cursus ex. In a turpis non quam pulvinar tincidunt. Maecenas tortor neque, dapibus a quam aliquet, dictum pellentesque leo. Sed aliquet tellus est, in tempus magna congue ut. Phasellus at tincidunt lorem, id fringilla risus. Nunc vel pulvinar massa. Aliquam erat volutpat. Phasellus semper interdum justo at eleifend. Curabitur feugiat quam sed mollis facilisis. -

    -

    -Quisque consectetur sem a elit aliquet facilisis. Quisque dignissim velit at quam rhoncus dignissim. Proin feugiat sem at turpis ultrices imperdiet. Integer vel eros vel ante ultricies dapibus vitae eget dui. Fusce sollicitudin semper tortor at molestie. Pellentesque nec metus sed mauris aliquet iaculis. Mauris malesuada tortor nec mi dictum, feugiat euismod enim gravida. Vestibulum dapibus ut nulla vel euismod. Nunc lobortis, mauris at pretium faucibus, ligula diam venenatis nulla, in mattis sapien arcu feugiat dolor. In at dui leo. Cras elementum condimentum turpis. -

    -

    -Donec eget dolor ac nulla lobortis bibendum. Praesent commodo accumsan ligula eget commodo. Suspendisse sit amet dignissim metus. Sed ut eros viverra, viverra lectus eget, ornare eros. Mauris elementum lacinia dapibus. Donec magna nisl, suscipit quis mattis eget, tincidunt sed sapien. Curabitur elementum nulla eget lorem gravida dapibus. Nunc vel dolor et libero pretium interdum vitae eget mauris. Vestibulum et erat in nulla sollicitudin luctus ut quis nulla. -

    -

    -Maecenas iaculis pellentesque quam at fringilla. Donec dui elit, suscipit eget varius id, suscipit efficitur metus. Nulla rutrum ultrices tempor. Vivamus hendrerit justo ac fermentum euismod. Vestibulum tempus pulvinar tempus. Curabitur congue neque luctus dolor vehicula, non efficitur metus fringilla. Nam a imperdiet ex. Integer at est hendrerit, rutrum justo eu, ornare odio. Etiam convallis sapien a purus vehicula, eget gravida purus semper. Fusce ex enim, volutpat ac feugiat et, rhoncus vel tortor. Aenean ultrices libero sed neque fermentum tempor. Morbi tincidunt dui turpis, non mollis est dignissim at. Suspendisse molestie convallis interdum. Donec sit amet fermentum purus. -

    - -
    - - - diff --git a/test/spec/e2e/gpt-examples/gpt_yieldbot.html b/test/spec/e2e/gpt-examples/gpt_yieldbot.html deleted file mode 100644 index 12766c537f6..00000000000 --- a/test/spec/e2e/gpt-examples/gpt_yieldbot.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - - - - -
    -
    - Yieldbot integration test mode: - -
      -
    • START (i.e.force bids to be returned)
    • -
    • STOP
    • -
    -
    - -

    Prebid.js Yieldbot Adapter Test

    -
    -
    - -
    -

    Lorem ipsum dolor. Sit amet proin. Integer cursus mi mus curabitur euismod vel quos duis bibendum nec interdum porta dolor a viverra nisl fusce. Volutpat sit at. Donec nisl taciti. Eget eu lobortis. Excepteur diam orci lacus nibh pharetra. Justo neque maecenas. Viverra molestie dolor ante rutrum vivamus libero urna suscipit leo praesent ultricies. In dignissim qui ante bibendum in. Habitasse ac arcu non nulla augue. Felis lectus non tempus in aliquam. Sit porttitor nec. Sodales non sit eu duis.

    -
    - -
    -

    Donec feugiat ornare a amet optio. Vitae sit sapien. Vitae nec justo. Fusce ac in semper ligula duis eget vel sit. Augue mauris sit. A adipisicing orci est augue dapibus ullamcorper faucibus fermentum. Et phasellus in tempus vivamus praesent. Nisl dui porttitor. Iaculis vulputate eros ut interdum eu. Lacus quis magna varius in quis. Congue erat porttitor sit eu vitae pharetra scelerisque nec. Dolor dui vel ut velit vestibulum. Lectus ullamcorper mi. Curabitur ipsum pellentesque sed erat est est sapien in tempor sodales viverra. Dui volutpat morbi eleifend fringilla quis. Neque erat erat. Rhoncus sed posuere. Dapibus fusce ut lacus mus est pede sed quisquam. Quis aliquam pellentesque. Wisi ac odio eu wisi amet ut ipsum a erat aliquam nunc.

    -

    Dis vitae penatibus. Mollis mauris bibendum. Porta orci amet dolor sed felis in neque per cursus molestie pellentesque. Quis accusamus vel sed dapibus orci congue ut eros. Nec faucibus inceptos. Hendrerit in eget nulla tellus lacinia. Fusce ut ut mattis.

    -

    Vivamus mauris metus. Ridiculus habitant lorem in nulla in quam eros ut. Libero aliquam platea. In enim consectetuer eget mattis accumsan aenean faucibus tincidunt. Amet donec vitae wisi pellentesque magna non lacinia qui. In erat in maecenas amet dui. Aliquam elit vel. Ligula sodales lacus. Nisl a purus. Pharetra velit porttitor vel vel non turpis viverra fringilla lorem arcu pellentesque sed aliquet nonummy quisque dapibus ullamcorper. Mollis ipsum nulla. Tempor tempus vitae. Luctus amet vel. Suspendisse sagittis vestibulum fusce eu.

    -

    Urna et felis bibendum felis sit vestibulum wisi pharetra quisque ac quis cursus suspendisse quisque aenean luctus curabitur. Eget nec leo. Mi placerat cras nulla et integer eget in sed. Non magni parturient. Egestas iaculis malesuada. Nec a duis. Pede condimentum ullamcorper. Augue arcu tellus. In velit in in duis odio dictum wisi proin quis eget sit. Felis tempus inceptos. Turpis risus eu. Mi vivamus consequat. Lectus dui imperdiet amet orci vehicula in vel pellentesque habitant suscipit aliquam. Proin porttitor vitae ultricies a in. Est duis pede. Tristique velit vestibulum odio sodales morbi magna ut vitae. Elementum imperdiet sodales ultrices tortor mollis vehicula lorem varius pellentesque mi ut sit turpis feugiat. In convallis urna. Justo aliquam sed quis scelerisque nonummy. Lobortis rhoncus ornare. Pellentesque leo quam at beatae in. Erat lorem tempus. Molestie faucibus a id mauris montes. Sed orci vulputate. Libero justo curabitur. Amet orci ante. Non felis est erat cras purus id at id nibh nunc facilisis amet metus sagittis pellentesque eros amet. Vitae sit nulla vitae wisi diam. Tincidunt ipsum eleifend semper tortor non. Tellus amet aliquet.

    -

    Dis vitae penatibus. Mollis mauris bibendum. Porta orci amet dolor sed felis in neque per cursus molestie pellentesque. Quis accusamus vel sed dapibus orci congue ut eros. Nec faucibus inceptos. Hendrerit in eget nulla tellus lacinia. Fusce ut ut mattis.

    -

    Vivamus mauris metus. Ridiculus habitant lorem in nulla in quam eros ut. Libero aliquam platea. In enim consectetuer eget mattis accumsan aenean faucibus tincidunt. Amet donec vitae wisi pellentesque magna non lacinia qui. In erat in maecenas amet dui. Aliquam elit vel. Ligula sodales lacus. Nisl a purus. Pharetra velit porttitor vel vel non turpis viverra fringilla lorem arcu pellentesque sed aliquet nonummy quisque dapibus ullamcorper. Mollis ipsum nulla. Tempor tempus vitae. Luctus amet vel. Suspendisse sagittis vestibulum fusce eu.

    -

    Urna et felis bibendum felis sit vestibulum wisi pharetra quisque ac quis cursus suspendisse quisque aenean luctus curabitur. Eget nec leo. Mi placerat cras nulla et integer eget in sed. Non magni parturient. Egestas iaculis malesuada. Nec a duis. Pede condimentum ullamcorper. Augue arcu tellus. In velit in in duis odio dictum wisi proin quis eget sit. Felis tempus inceptos. Turpis risus eu. Mi vivamus consequat. Lectus dui imperdiet amet orci vehicula in vel pellentesque habitant suscipit aliquam. Proin porttitor vitae ultricies a in. Est duis pede. Tristique velit vestibulum odio sodales morbi magna ut vitae. Elementum imperdiet sodales ultrices tortor mollis vehicula lorem varius pellentesque mi ut sit turpis feugiat. In convallis urna. Justo aliquam sed quis scelerisque nonummy. Lobortis rhoncus ornare. Pellentesque leo quam at beatae in. Erat lorem tempus. Molestie faucibus a id mauris montes. Sed orci vulputate. Libero justo curabitur. Amet orci ante. Non felis est erat cras purus id at id nibh nunc facilisis amet metus sagittis pellentesque eros amet. Vitae sit nulla vitae wisi diam. Tincidunt ipsum eleifend semper tortor non. Tellus amet aliquet.

    -
    -
    - -
    -
    - -
    -
    -

    Dis vitae penatibus. Mollis mauris bibendum. Porta orci amet dolor sed felis in neque per cursus molestie pellentesque. Quis accusamus vel sed dapibus orci congue ut eros. Nec faucibus inceptos. Hendrerit in eget nulla tellus lacinia. Fusce ut ut mattis.

    -
    - - diff --git a/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js b/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js new file mode 100644 index 00000000000..82310738246 --- /dev/null +++ b/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js @@ -0,0 +1,68 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCats = ['Food', 'Retail Stores/Chains', 'Pet Food/Supplies', 'Travel/Hotels/Airlines', 'Automotive', 'Health Care Services']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{1,3}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads using custom adserver translation file', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_custom_adserver_translation.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfCats = $$(listOfCategoriesXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let cat = listOfCats[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validCats, cat), `Could not find Category ${cat} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js b/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js new file mode 100644 index 00000000000..224ff1cbc34 --- /dev/null +++ b/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js @@ -0,0 +1,68 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCats = ['Food', 'Retail Stores/Chains', 'Pet Food/Supplies', 'Travel/Hotels/Airlines', 'Automotive', 'Health Care Services']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{1,3}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads using requireExactDuration field', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_requireExactDuration.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfCats = $$(listOfCategoriesXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let cat = listOfCats[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validCats, cat), `Could not find Category ${cat} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js b/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js new file mode 100644 index 00000000000..95237366d0e --- /dev/null +++ b/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js @@ -0,0 +1,63 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads without using brandCategoryExclusion', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_wo_brandCategoryExclusion.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js b/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js new file mode 100644 index 00000000000..6b628067138 --- /dev/null +++ b/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js @@ -0,0 +1,68 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCats = ['Food', 'Retail Stores/Chains', 'Pet Food/Supplies', 'Travel/Hotels/Airlines', 'Automotive', 'Health Care Services']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{1,3}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads not using requireExactDuration field', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_wo_requireExactDuration.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfCats = $$(listOfCategoriesXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let cat = listOfCats[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validCats, cat), `Could not find Category ${cat} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/test/spec/e2e/native/basic_native_ad.spec.js b/test/spec/e2e/native/basic_native_ad.spec.js new file mode 100644 index 00000000000..ed09228b532 --- /dev/null +++ b/test/spec/e2e/native/basic_native_ad.spec.js @@ -0,0 +1,59 @@ +const expect = require('chai').expect; +const { host, protocol } = require('../../../helpers/testing-utils'); + +const TEST_PAGE_URL = `${protocol}://${host}:9999/test/pages/native.html`; +const CREATIVE_IFRAME_CSS_SELECTOR = 'iframe[id="google_ads_iframe_/19968336/prebid_native_example_1_0"]'; + +const EXPECTED_TARGETING_KEYS = { + hb_source: 'client', + hb_source_appnexus: 'client', + hb_pb_appnexus: '10.00', + hb_native_title_appn: 'This is a Prebid Native Creative', + hb_native_linkurl: 'http://prebid.org/dev-docs/show-native-ads.html', + hb_format: 'native', + hb_native_brand: 'Prebid.org', + hb_size: '0x0', + hb_bidder_appnexus: 'appnexus', + hb_native_linkurl_ap: 'http://prebid.org/dev-docs/show-native-ads.html', + hb_native_title: 'This is a Prebid Native Creative', + hb_pb: '10.00', + hb_native_brand_appn: 'Prebid.org', + hb_bidder: 'appnexus', + hb_format_appnexus: 'native', + hb_size_appnexus: '0x0' +} + +describe('Prebid.js Native Ad Unit Test', function () { + before(function loadTestPage() { + browser.url(TEST_PAGE_URL).pause(3000); + try { + browser.waitForExist(CREATIVE_IFRAME_CSS_SELECTOR, 2000); + } catch (e) { + // If creative Iframe didn't load, repeat the steps again! + // Due to some reason if the Ad server doesn't respond, the test case will time out after 60000 ms as defined in file wdio.conf.js + loadTestPage(); + } + }); + + it('should load the targeting keys with correct values', function () { + const result = browser.execute(function () { + return window.pbjs.getAdserverTargeting('/19968336/prebid_native_example_2'); + }); + + const targetingKeys = result.value['/19968336/prebid_native_example_2']; + expect(targetingKeys).to.include(EXPECTED_TARGETING_KEYS); + expect(targetingKeys.hb_adid).to.be.a('string'); + expect(targetingKeys.hb_native_body).to.be.a('string'); + expect(targetingKeys.hb_native_body_appne).to.be.a('string'); + expect(targetingKeys.hb_native_icon).to.be.a('string'); + expect(targetingKeys.hb_native_icon_appne).to.be.a('string'); + expect(targetingKeys.hb_native_image).to.be.a('string'); + expect(targetingKeys.hb_adid_appnexus).to.be.a('string'); + }); + + it('should render the native ad on the page', function () { + const creativeIframe = $(CREATIVE_IFRAME_CSS_SELECTOR).value; + browser.frame(creativeIframe); + expect(browser.isVisible('body > div[class="GoogleActiveViewElement"] > div[class="card"]')).to.be.true; + }); +}); diff --git a/test/spec/e2e/outstream/basic_outstream_video_ad.spec.js b/test/spec/e2e/outstream/basic_outstream_video_ad.spec.js new file mode 100644 index 00000000000..15b0bb29309 --- /dev/null +++ b/test/spec/e2e/outstream/basic_outstream_video_ad.spec.js @@ -0,0 +1,53 @@ +const expect = require('chai').expect; +const { host, protocol } = require('../../../helpers/testing-utils'); + +const TEST_PAGE_URL = `${protocol}://${host}:9999/test/pages/outstream.html`; +const CREATIVE_IFRAME_CSS_SELECTOR = 'div[id="video_ad_unit_1"] > div:nth-child(2) > iframe:nth-child(1)'; + +const EXPECTED_TARGETING_KEYS = { + hb_cache_id: '', + hb_uuid: '', + hb_format: 'video', + hb_source: 'client', + hb_size: '640x480', + hb_pb: '10.00', + hb_bidder: 'appnexus', + hb_format_appnexus: 'video', + hb_source_appnexus: 'client', + hb_size_appnexus: '640x480', + hb_pb_appnexus: '10.00', + hb_bidder_appnexus: 'appnexus' +}; + +describe('Prebid.js Outstream Video Ad Test', function () { + before(function loadTestPage() { + browser + .url(TEST_PAGE_URL) + .scroll(0, 300) + .pause(3000); + try { + browser.waitForExist(CREATIVE_IFRAME_CSS_SELECTOR, 5000); + } catch (e) { + // If creative Iframe didn't load, repeat the steps again! + // Due to some reason if the Ad server doesn't respond, the test case will time out after 60000 ms as defined in file wdio.conf.js + loadTestPage(); + } + }); + + it('should load the targeting keys with correct values', function () { + const result = browser.execute(function () { + return window.pbjs.getAdserverTargeting('video_ad_unit_2'); + }); + + const targetingKeys = result.value['video_ad_unit_2']; + expect(targetingKeys).to.include(EXPECTED_TARGETING_KEYS); + expect(targetingKeys.hb_adid).to.be.a('string'); + expect(targetingKeys.hb_adid_appnexus).to.be.a('string'); + }); + + it('should render the native ad on the page', function() { + const creativeIframe = $(CREATIVE_IFRAME_CSS_SELECTOR).value; + browser.frame(creativeIframe); + expect(browser.isVisible('body > div[class="video-js"] > video')); + }); +}); diff --git a/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js b/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js deleted file mode 100644 index 5803ad5ceb3..00000000000 --- a/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js +++ /dev/null @@ -1,125 +0,0 @@ -// var verify = require('verify'); -var util = require('../../common/utils.js'); - -module.exports = { - 'adequant ad rendering': function (browser) { - browser - .url('http://an.localhost:9999/test/spec/e2e/gpt-examples/all_bidders_instant_load.html') - .waitForElementVisible('body', 5000) - .pause(7000) - .execute(util.findIframeInDiv, ['div-1'], function(result) { - this.verify.equal(result.value, true, 'adequant ad not rendered'); - }); - }, - 'adform ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-2'], function(result) { - this.verify.equal(result.value, true, 'adform ad not rendered'); - }); - }, - 'aol ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-3'], function(result) { - this.verify.equal(result.value, true, 'aol ad not rendered'); - }); - }, - 'appnexus ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-4'], function(result) { - this.verify.equal(result.value, true, 'appnexus ad not rendered'); - }); - }, - 'indexExchange ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-5'], function(result) { - this.verify.equal(result.value, true, 'indexExchange ad not rendered'); - }); - }, - 'openx ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-6'], function(result) { - this.verify.equal(result.value, true, 'openx ad not rendered'); - }); - }, - 'pubmatic ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-7'], function(result) { - this.verify.equal(result.value, true, 'pubmatic ad not rendered'); - }); - }, - 'pulsepoint ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-8'], function(result) { - this.verify.equal(result.value, true, 'pulsepoint ad not rendered'); - }); - }, - 'rubicon ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-9'], function(result) { - this.verify.equal(result.value, true, 'rubicon ad not rendered'); - }); - }, - 'sonobi ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-10'], function(result) { - this.verify.equal(result.value, true, 'sonobi ad not rendered'); - }); - }, - 'sovrn ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-11'], function(result) { - this.verify.equal(result.value, true, 'sovrn ad not rendered'); - }); - }, - 'springserve ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-12'], function(result) { - this.verify.equal(result.value, true, 'springserve ad not rendered'); - }); - }, - 'triplelift ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-13'], function(result) { - this.verify.equal(result.value, true, 'triplelift ad not rendered'); - }); - }, - 'yieldbot ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-14'], function(result) { - this.verify.equal(result.value, true, 'yieldbot ad not rendered'); - }); - }, - 'nginad ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-15'], function(result) { - this.verify.equal(result.value, true, 'nginad ad not rendered'); - }); - }, - 'brightcom ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-16'], function(result) { - this.verify.equal(result.value, true, 'brightcom ad not rendered'); - }); - }, - 'sekindo ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-17'], function(result) { - this.verify.equal(result.value, true, 'sekindo ad not rendered'); - }); - }, - 'kruxlink ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-18'], function(result) { - this.verify.equal(result.value, true, 'kruxlink ad not rendered'); - }); - }, - 'AdMedia ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-19'], function(result) { - this.verify.equal(result.value, true, 'AdMedia ad not rendered'); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/e2e/testcase1/dom-group/dom_spec.js b/test/spec/e2e/testcase1/dom-group/dom_spec.js deleted file mode 100644 index a58030c32b7..00000000000 --- a/test/spec/e2e/testcase1/dom-group/dom_spec.js +++ /dev/null @@ -1,51 +0,0 @@ -// var assert = require('assert'); - -module.exports = { - - 'Test rendering ad div-2': function (browser) { - var checkAdRendering2 = function() { - var div = document.getElementById('div-2'); - var iframes = div.getElementsByTagName('iframe'); - try { - if (iframes.length == 1 && iframes[0].contentWindow.document.body.innerHTML == '') { - return false; - } else { - return true; - } - } catch (e) { - return true; - } - } - - browser - .url('http://an.localhost:9999/test/spec/e2e/gpt-examples/e2e_default.html') - .waitForElementVisible('body', 3000) - .pause(3000) - .execute(checkAdRendering2, [], function(result) { - this.assert.equal(result.value, true, 'Ad of div-2 not rendered'); - }); - }, - 'Test rendering ad div-1': function (browser) { - var checkAdRendering = function() { - var div = document.getElementById('div-1'); - var iframes = div.getElementsByTagName('iframe'); - try { - if (iframes.length == 1 && iframes[0].contentWindow.document.body.innerHTML == '') { - return false; - } else { - return true; - } - } catch (e) { - return true; - } - } - - browser - .execute(checkAdRendering, [], function(result) { - this.assert.equal(result.value, true, 'Ad of div-1 not rendered'); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js b/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js deleted file mode 100644 index 796707641cd..00000000000 --- a/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -var assert = require('assert'); -var utils = require('util'); - -module.exports = { - 'AdserverTargeting Test Case 1': function (browser) { - browser - .url('http://localhost:9999/test/spec/e2e/gpt-examples/gpt_default.html') - .waitForElementVisible('body', 3000) - .pause(3000) - .execute(function() { - if (typeof window.pbjs.bidderSettings === 'undefined') { - var pbjsBidderSettingsObject = [ - 'hb_bidder', - 'hb_adid', - 'hb_pb', - 'hb_size' - ]; - } else { - var pbjsBidderSettings = window.pbjs.bidderSettings; - var pbjsBidderSettingsObject = {}; - Object.keys(pbjsBidderSettings).forEach(function (prop) { - // if(prop == 'standard') return; - var value = pbjsBidderSettings[prop]; - var bs = value.adserverTargeting.map(function(item) { - return item.key; - }); - pbjsBidderSettings.standard.adserverTargeting.map(function(value) { - if (bs.indexOf(value.key) == -1) { - bs.push(value.key) - } - }); - pbjsBidderSettingsObject[prop] = bs; - }); - } - - var adserverTargetingObject = {}; - var adserverTargeting = window.pbjs.getAdserverTargeting(); - Object.keys(adserverTargeting).forEach(function(value) { - if (Object.keys(adserverTargeting[value]).length == 0) return; - adserverTargetingObject[adserverTargeting[value].hb_bidder] = Object.keys(adserverTargeting[value]) - }); - - return [pbjsBidderSettingsObject, adserverTargetingObject]; - }, [], function(result) { - Object.keys(result.value[1]).forEach(function(key) { - if (utils.isArray(result.value[0])) { - assert.deepEqual(result.value[0].sort(), result.value[1][key].sort()); - } else { - if (result.value[0].hasOwnProperty(key)) { - var obj1 = result.value[0][key].sort(); - } else { - var obj1 = result.value[0]['standard'].sort(); - } - assert.deepEqual(obj1, result.value[1][key].sort()); - } - }); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js b/test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js deleted file mode 100644 index c7921709c0f..00000000000 --- a/test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js +++ /dev/null @@ -1,34 +0,0 @@ -// var assert = require('assert'); -var assert = require('chai').assert; -var utils = require('util'); - -module.exports = { - 'bidReceived not empty': function(browser) { - browser - .url('http://localhost:9999/test/spec/e2e/gpt-examples/gpt_default.html') - .waitForElementVisible('body', 3000) - .pause(5000) - .execute(function() { - return window.pbjs._bidsReceived.length; - }, [], function(result) { - // browser.assert.first(false, 'Bid response empty'); - assert.isOk(result.value, 'Bid response empty'); - }); - }, - 'check keys': function(browser) { - browser - .execute(function() { - return window.pbjs._bidsReceived; - }, [], function(result) { - // minimum expected keys in bid received - var expected = ['bidderCode', 'width', 'height', 'adId', 'cpm', 'requestId', 'bidder', 'adUnitCode', 'timeToRespond']; - Object.keys(result.value).forEach(function(key) { - var compare = Object.keys(result.value[key]); - assert.includeMembers(compare, expected, 'include members'); - }); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/hook_spec.js b/test/spec/hook_spec.js deleted file mode 100644 index 1fab4ecd1b7..00000000000 --- a/test/spec/hook_spec.js +++ /dev/null @@ -1,151 +0,0 @@ - -import { expect } from 'chai'; -import { createHook, hooks } from 'src/hook'; - -describe('the hook module', () => { - let sandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should call all sync hooks attached to a function', () => { - let called = []; - let calledWith; - - let testFn = () => { - called.push(testFn); - }; - let testHook = (...args) => { - called.push(testHook); - calledWith = args; - }; - let testHook2 = () => { - called.push(testHook2); - }; - let testHook3 = () => { - called.push(testHook3); - }; - - let hookedTestFn = createHook('sync', testFn, 'testHook'); - - hookedTestFn.addHook(testHook, 50); - hookedTestFn.addHook(testHook2, 100); - - // make sure global test hooks work as well (with default priority) - hooks['testHook'].addHook(testHook3); - - hookedTestFn(1, 2, 3); - - expect(called).to.deep.equal([ - testHook2, - testHook, - testHook3, - testFn - ]); - - expect(calledWith).to.deep.equal([1, 2, 3]); - - called = []; - - hookedTestFn.removeHook(testHook); - hooks['testHook'].removeHook(testHook3); - - hookedTestFn(1, 2, 3); - - expect(called).to.deep.equal([ - testHook2, - testFn - ]); - }); - - it('should allow context to be passed to hooks, but keep bound contexts', () => { - let context; - let fn = function() { - context = this; - }; - - let boundContext = {}; - let calledBoundContext; - let hook = function() { - calledBoundContext = this; - }.bind(boundContext); - - let hookFn = createHook('sync', fn); - hookFn.addHook(hook); - - let newContext = {}; - hookFn.bind(newContext)(); - - expect(context).to.equal(newContext); - expect(calledBoundContext).to.equal(boundContext); - }); - - describe('asyncSeries', () => { - it('should call function as normal if no hooks attached', () => { - let fn = sandbox.spy(); - let hookFn = createHook('asyncSeries', fn); - - hookFn(1); - - expect(fn.calledOnce).to.equal(true); - expect(fn.firstCall.args[0]).to.equal(1); - }); - - it('should call hooks correctly applied in asyncSeries', () => { - let called = []; - - let testFn = (called) => { - called.push(testFn); - }; - let testHook = (called, next) => { - called.push(testHook); - next(called); - }; - let testHook2 = (called, next) => { - called.push(testHook2); - next(called); - }; - - let hookedTestFn = createHook('asyncSeries', testFn); - hookedTestFn.addHook(testHook); - hookedTestFn.addHook(testHook2); - - hookedTestFn(called); - - expect(called).to.deep.equal([ - testHook, - testHook2, - testFn - ]); - }); - - it('should allow context to be passed to hooks, but keep bound contexts', () => { - let context; - let fn = function() { - context = this; - }; - - let boundContext1 = {}; - let calledBoundContext1; - let hook1 = function(next) { - calledBoundContext1 = this; - next() - }.bind(boundContext1); - - let hookFn = createHook('asyncSeries', fn); - hookFn.addHook(hook1); - - let newContext = {}; - hookFn = hookFn.bind(newContext); - hookFn(); - - expect(context).to.equal(newContext); - expect(calledBoundContext1).to.equal(boundContext1); - }); - }); -}); diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index ff701d265a7..08ea0a863ee 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -1,23 +1,169 @@ -const { userSync } = require('../../../src/userSync'); -const { config } = require('../../../src/config'); +import { expect } from 'chai'; -const { expect } = require('chai'); -const { - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs -} = require('../../../modules/33acrossBidAdapter'); +import * as utils from 'src/utils'; +import { config } from 'src/config'; + +import { spec } from 'modules/33acrossBidAdapter'; describe('33acrossBidAdapter:', function () { const BIDDER_CODE = '33across'; const SITE_ID = 'pub1234'; const PRODUCT_ID = 'product1'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; - const SYNC_ENDPOINT = 'https://de.tynt.com/deb/v2?m=xch&rt=html'; + + let element, win; + let bidRequests; + let sandbox; + + function TtxRequestBuilder() { + const ttxRequest = { + imp: [{ + banner: { + format: [ + { + w: 300, + h: 250, + ext: {} + }, + { + w: 728, + h: 90, + ext: {} + } + ], + ext: { + ttx: { + viewability: { + amount: 100 + } + } + } + }, + ext: { + ttx: { + prod: PRODUCT_ID + } + } + }], + site: { + id: SITE_ID + }, + id: 'b1', + user: { + ext: { + consent: undefined + } + }, + regs: { + ext: { + gdpr: 0 + } + }, + ext: { + ttx: { + caller: [{ + 'name': 'prebidjs', + 'version': '$prebid.version$' + }] + } + } + }; + + this.withSizes = sizes => { + Object.assign(ttxRequest.imp[0].banner, { format: sizes }); + return this; + }; + + this.withViewabiliuty = viewability => { + Object.assign(ttxRequest.imp[0].banner, { + ext: { + ttx: { viewability } + } + }); + return this; + }; + + this.withGdprConsent = (consent, gdpr) => { + Object.assign(ttxRequest, { + user: { + ext: { consent } + } + }); + Object.assign(ttxRequest, { + regs: { + ext: { gdpr } + } + }); + return this; + }; + + this.withSite = site => { + Object.assign(ttxRequest, { site }); + return this; + }; + + this.build = () => ttxRequest; + } + + function ServerRequestBuilder() { + const serverRequest = { + 'method': 'POST', + 'url': END_POINT, + 'data': null, + 'options': { + 'contentType': 'text/plain', + 'withCredentials': true + } + }; + + this.withData = data => { + serverRequest['data'] = JSON.stringify(data); + return this; + }; + + this.withUrl = url => { + serverRequest['url'] = url; + return this; + }; + + this.withOptions = options => { + serverRequest['options'] = options; + return this; + }; + + this.build = () => serverRequest; + } beforeEach(function() { - this.bidRequests = [ + element = { + x: 0, + y: 0, + + width: 0, + height: 0, + + getBoundingClientRect: () => { + return { + width: element.width, + height: element.height, + + left: element.x, + top: element.y, + right: element.x + element.width, + bottom: element.y + element.height + }; + } + }; + win = { + document: { + visibilityState: 'visible' + }, + + innerWidth: 800, + innerHeight: 600 + }; + + bidRequests = [ { bidId: 'b1', bidder: '33across', @@ -29,20 +175,24 @@ describe('33acrossBidAdapter:', function () { adUnitCode: 'div-id', auctionId: 'r1', sizes: [ - [ 300, 250 ], - [ 728, 90 ] + [300, 250], + [728, 90] ], transactionId: 't1' } ]; - this.sandbox = sinon.sandbox.create(); + + sandbox = sinon.sandbox.create(); + sandbox.stub(document, 'getElementById').withArgs('div-id').returns(element); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns(win); }); afterEach(function() { - this.sandbox.restore(); + sandbox.restore(); }); - describe('isBidRequestValid:', function () { + describe('isBidRequestValid:', function() { it('returns true when valid bid request is sent', function() { const validBid = { bidder: BIDDER_CODE, @@ -50,9 +200,9 @@ describe('33acrossBidAdapter:', function () { siteId: SITE_ID, productId: PRODUCT_ID } - } + }; - expect(isBidRequestValid(validBid)).to.be.true; + expect(spec.isBidRequestValid(validBid)).to.be.true; }); it('returns true when valid test bid request is sent', function() { @@ -63,29 +213,29 @@ describe('33acrossBidAdapter:', function () { productId: PRODUCT_ID, test: 1 } - } + }; - expect(isBidRequestValid(validBid)).to.be.true; + expect(spec.isBidRequestValid(validBid)).to.be.true; }); - it('returns false when bidder not set to "33across"', function () { + it('returns false when bidder not set to "33across"', function() { const invalidBid = { bidder: 'foo', params: { siteId: SITE_ID, productId: PRODUCT_ID } - } + }; - expect(isBidRequestValid(invalidBid)).to.be.false; + expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('returns false when params not set', function() { const invalidBid = { bidder: 'foo' - } + }; - expect(isBidRequestValid(invalidBid)).to.be.false; + expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('returns false when site ID is not set in params', function() { @@ -94,9 +244,9 @@ describe('33acrossBidAdapter:', function () { params: { productId: PRODUCT_ID } - } + }; - expect(isBidRequestValid(invalidBid)).to.be.false; + expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('returns false when product ID not set in params', function() { @@ -105,198 +255,214 @@ describe('33acrossBidAdapter:', function () { params: { siteId: SITE_ID } - } + }; - expect(isBidRequestValid(invalidBid)).to.be.false; + expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); }); describe('buildRequests:', function() { - it('returns corresponding server requests for each valid bidRequest', function() { - const ttxRequest = { - imp: [ { - banner: { - format: [ - { - w: 300, - h: 250, - ext: {} - }, - { - w: 728, - h: 90, - ext: {} - } - ] - }, - ext: { - ttx: { - prod: PRODUCT_ID - } - } - } ], - site: { - id: SITE_ID - }, - id: 'b1' - }; - const serverRequest = { - 'method': 'POST', - 'url': END_POINT, - 'data': JSON.stringify(ttxRequest), - 'options': { - 'contentType': 'application/json', - 'withCredentials': true - } - } - const builtServerRequests = buildRequests(this.bidRequests); - expect(builtServerRequests).to.deep.equal([ serverRequest ]); - expect(builtServerRequests.length).to.equal(1); + context('when element is fully in view', function() { + it('returns 100', function() { + const ttxRequest = new TtxRequestBuilder() + .withViewabiliuty({amount: 100}) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + Object.assign(element, { width: 600, height: 400 }); + + expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + }); }); - it('returns corresponding test server requests for each valid bidRequest', function() { - this.sandbox.stub(config, 'getConfig').callsFake(() => { - return { - 'url': 'https://foo.com/hb/' - } + context('when element is out of view', function() { + it('returns 0', function() { + const ttxRequest = new TtxRequestBuilder() + .withViewabiliuty({amount: 0}) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + Object.assign(element, { x: -300, y: 0, width: 207, height: 320 }); + + expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); }); + }); - const ttxRequest = { - imp: [ { - banner: { - format: [ - { - w: 300, - h: 250, - ext: { } - }, - { - w: 728, - h: 90, - ext: { } - } - ] - }, - ext: { - ttx: { - prod: PRODUCT_ID - } - } - } ], - site: { - id: SITE_ID - }, - id: 'b1' - }; - const serverRequest = { - method: 'POST', - url: 'https://foo.com/hb/', - data: JSON.stringify(ttxRequest), - options: { - contentType: 'application/json', - withCredentials: true - } - }; + context('when element is partially in view', function() { + it('returns percentage', function() { + const ttxRequest = new TtxRequestBuilder() + .withViewabiliuty({amount: 75}) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); - const builtServerRequests = buildRequests(this.bidRequests); - expect(builtServerRequests).to.deep.equal([ serverRequest ]); - expect(builtServerRequests.length).to.equal(1); + Object.assign(element, { width: 800, height: 800 }); + + expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + }); }); - it('returns corresponding test server requests for each valid bidRequest', function() { - this.sandbox.stub(config, 'getConfig').callsFake(() => { - return { - 'url': 'https://foo.com/hb/' - } + context('when width or height of the element is zero', function() { + it('try to use alternative values', function() { + const ttxRequest = new TtxRequestBuilder() + .withSizes([{ w: 800, h: 2400, ext: {} }]) + .withViewabiliuty({amount: 25}) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + Object.assign(element, { width: 0, height: 0 }); + bidRequests[0].sizes = [[800, 2400]]; + + expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); }); - this.bidRequests[0].params.test = 1; - const ttxRequest = { - imp: [ { - banner: { - format: [ - { - w: 300, - h: 250, - ext: { } - }, - { - w: 728, - h: 90, - ext: { } - } - ] - }, - ext: { - ttx: { - prod: PRODUCT_ID - } + }); + + context('when nested iframes', function() { + it('returns \'nm\'', function() { + const ttxRequest = new TtxRequestBuilder() + .withViewabiliuty({amount: spec.NON_MEASURABLE}) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + utils.getWindowSelf.restore(); + sandbox.stub(utils, 'getWindowTop').returns({}); + sandbox.stub(utils, 'getWindowSelf').returns(win); + + expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + }); + }); + + context('when tab is inactive', function() { + it('returns 0', function() { + const ttxRequest = new TtxRequestBuilder() + .withViewabiliuty({amount: 0}) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + win.document.visibilityState = 'hidden'; + sandbox.stub(utils, 'getWindowTop').returns(win); + + expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + }); + }); + + context('when gdpr consent data exists', function() { + let bidderRequest; + + beforeEach(function() { + bidderRequest = { + gdprConsent: { + consentString: 'foobarMyPreference', + gdprApplies: true } - } ], - site: { - id: SITE_ID - }, - id: 'b1', - test: 1 - }; - const serverRequest = { - method: 'POST', - url: 'https://foo.com/hb/', - data: JSON.stringify(ttxRequest), - options: { - contentType: 'application/json', - withCredentials: true } - }; + }); + + it('returns corresponding server requests with gdpr consent data', function() { + const ttxRequest = new TtxRequestBuilder() + .withGdprConsent('foobarMyPreference', 1) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); - const builtServerRequests = buildRequests(this.bidRequests); - expect(builtServerRequests).to.deep.equal([ serverRequest ]); - expect(builtServerRequests.length).to.equal(1); + it('returns corresponding test server requests with gdpr consent data', function() { + sandbox.stub(config, 'getConfig').callsFake(() => { + return { + 'url': 'https://foo.com/hb/' + } + }); + + const ttxRequest = new TtxRequestBuilder() + .withGdprConsent('foobarMyPreference', 1) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .withUrl('https://foo.com/hb/') + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); }); - afterEach(function() { - delete this.bidRequests; - }) + context('when gdpr consent data does not exist', function() { + let bidderRequest; + + beforeEach(function() { + bidderRequest = {}; + }); + + it('returns corresponding server requests with default gdpr consent data', function() { + const ttxRequest = new TtxRequestBuilder() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + + it('returns corresponding test server requests with default gdpr consent data', function() { + sandbox.stub(config, 'getConfig').callsFake(() => { + return { + 'url': 'https://foo.com/hb/' + } + }); + + const ttxRequest = new TtxRequestBuilder() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .withUrl('https://foo.com/hb/') + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + }); }); describe('interpretResponse', function() { + let ttxRequest, serverRequest; + beforeEach(function() { - this.ttxRequest = { - imp: [ { - banner: { - format: [ - { - w: 300, - h: 250, - ext: {} - }, - { - w: 728, - h: 90, - ext: {} - } - ] - }, - ext: { - ttx: { - prod: PRODUCT_ID - } - } - } ], - site: { + ttxRequest = new TtxRequestBuilder() + .withSite({ id: SITE_ID, page: 'http://test-url.com' - }, - id: 'b1' - }; - this.serverRequest = { - method: 'POST', - url: '//staging-ssc.33across.com/api/v1/hb', - data: JSON.stringify(this.ttxRequest), - options: { - contentType: 'application/json', + }) + .build(); + serverRequest = new ServerRequestBuilder() + .withUrl('//staging-ssc.33across.com/api/v1/hb') + .withData(ttxRequest) + .withOptions({ + contentType: 'text/plain', withCredentials: false - } - }; + }) + .build(); }); context('when exactly one bid is returned', function() { @@ -307,18 +473,17 @@ describe('33acrossBidAdapter:', function () { id: 'b1', seatbid: [ { - bid: [ { + bid: [{ id: '1', adm: '

    I am an ad

    ', crid: 1, h: 250, w: 300, price: 0.0938 - } ] + }] } ] }; - const bidResponse = { requestId: 'b1', bidderCode: BIDDER_CODE, @@ -330,9 +495,9 @@ describe('33acrossBidAdapter:', function () { creativeId: 1, currency: 'USD', netRevenue: true - } + }; - expect(interpretResponse({ body: serverResponse }, this.serverRequest)).to.deep.equal([ bidResponse ]); + expect(spec.interpretResponse({ body: serverResponse }, serverRequest)).to.deep.equal([bidResponse]); }); }); @@ -345,7 +510,7 @@ describe('33acrossBidAdapter:', function () { seatbid: [] }; - expect(interpretResponse({ body: serverResponse }, this.serverRequest)).to.deep.equal([]); + expect(spec.interpretResponse({ body: serverResponse }, serverRequest)).to.deep.equal([]); }); }); @@ -357,7 +522,7 @@ describe('33acrossBidAdapter:', function () { id: 'b1', seatbid: [ { - bid: [ { + bid: [{ id: '1', adm: '

    I am an ad

    ', crid: 1, @@ -376,18 +541,17 @@ describe('33acrossBidAdapter:', function () { ] }, { - bid: [ { + bid: [{ id: '3', adm: '

    I am an ad

    ', crid: 3, h: 250, w: 300, price: 0.0938 - } ] + }] } ] }; - const bidResponse = { requestId: 'b1', bidderCode: BIDDER_CODE, @@ -401,46 +565,192 @@ describe('33acrossBidAdapter:', function () { netRevenue: true }; - expect(interpretResponse({ body: serverResponse }, this.serverRequest)).to.deep.equal([ bidResponse ]); + expect(spec.interpretResponse({ body: serverResponse }, serverRequest)).to.deep.equal([bidResponse]); }); }); + }); - context('and register user sync', function() { - it('via the production endpoint', function() { - const spy = this.sandbox.spy(userSync, 'registerSync'); - const serverResponse = { - cur: 'USD', - ext: {}, - id: 'b1', - seatbid: [] + describe('getUserSyncs', function() { + let syncs; + + beforeEach(function() { + syncs = [ + { + type: 'iframe', + url: 'https://de.tynt.com/deb/v2?m=xch&rt=html&id=id1' + }, + { + type: 'iframe', + url: 'https://de.tynt.com/deb/v2?m=xch&rt=html&id=id2' + }, + ]; + bidRequests = [ + { + bidId: 'b1', + bidder: '33across', + bidderRequestId: 'b1a', + params: { + siteId: 'id1', + productId: 'foo' + }, + adUnitCode: 'div-id', + auctionId: 'r1', + sizes: [ + [300, 250] + ], + transactionId: 't1' + }, + { + bidId: 'b2', + bidder: '33across', + bidderRequestId: 'b2a', + params: { + siteId: 'id2', + productId: 'foo' + }, + adUnitCode: 'div-id', + auctionId: 'r1', + sizes: [ + [300, 250] + ], + transactionId: 't2' } - interpretResponse({ body: serverResponse }, this.serverRequest); - const syncUrl = `${SYNC_ENDPOINT}&id=${this.ttxRequest.site.id}`; + ]; + }); + + context('when iframe is not enabled', function() { + it('returns empty sync array', function() { + const syncOptions = {}; - const registerSyncCalled = spy.calledWith('iframe', '33across', syncUrl); - expect(registerSyncCalled).to.be.true; + spec.buildRequests(bidRequests); + + expect(spec.getUserSyncs(syncOptions)).to.deep.equal([]); }); + }); - it('via the test endpoint', function() { - const spy = this.sandbox.spy(userSync, 'registerSync'); + context('when iframe is enabled', function() { + let syncOptions; + beforeEach(function() { + syncOptions = { + iframeEnabled: true + }; + }); - this.sandbox.stub(config, 'getConfig').callsFake(() => { - return { - 'syncUrl': 'https://foo.com/deb/v2?m=xch' - } + context('when there is no gdpr consent data', function() { + it('returns sync urls with undefined consent string as param', function() { + spec.buildRequests(bidRequests); + + const syncResults = spec.getUserSyncs(syncOptions, {}, undefined); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=undefined` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=undefined` + } + ] + + expect(syncResults).to.deep.equal(expectedSyncs); + }) + }); + + context('when gdpr applies but there is no consent string', function() { + it('returns sync urls with undefined consent string as param and gdpr=1', function() { + spec.buildRequests(bidRequests); + + const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: true}); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=undefined&gdpr=1` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=undefined&gdpr=1` + } + ]; + + expect(syncResults).to.deep.equal(expectedSyncs); }); + }); - const serverResponse = { - cur: 'USD', - ext: {}, - id: 'b1', - seatbid: [] - } - interpretResponse({ body: serverResponse }, this.serverRequest); - const syncUrl = `https://foo.com/deb/v2?m=xch&id=${this.ttxRequest.site.id}`; + context('when gdpr applies and there is consent string', function() { + it('returns sync urls with gdpr_consent=consent string as param and gdpr=1', function() { + spec.buildRequests(bidRequests); - const registerSyncCalled = spy.calledWith('iframe', '33across', syncUrl); - expect(registerSyncCalled).to.be.true; + const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: true, consentString: 'consent123A'}); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=consent123A&gdpr=1` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=consent123A&gdpr=1` + } + ]; + + expect(syncResults).to.deep.equal(expectedSyncs); + }); + }); + + context('when gdpr does not apply and there is no consent string', function() { + it('returns sync urls with undefined consent string as param and gdpr=0', function() { + spec.buildRequests(bidRequests); + + const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: false}); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=undefined&gdpr=0` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=undefined&gdpr=0` + } + ]; + expect(syncResults).to.deep.equal(expectedSyncs); + }); + }); + + context('when gdpr is unknown and there is consent string', function() { + it('returns sync urls with only consent string as param', function() { + spec.buildRequests(bidRequests); + + const syncResults = spec.getUserSyncs(syncOptions, {}, {consentString: 'consent123A'}); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=consent123A` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=consent123A` + } + ]; + expect(syncResults).to.deep.equal(expectedSyncs); + }); + }); + + context('when gdpr does not apply and there is consent string (yikes!)', function() { + it('returns sync urls with consent string as param and gdpr=0', function() { + spec.buildRequests(bidRequests); + + const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: false, consentString: 'consent123A'}); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=consent123A&gdpr=0` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=consent123A&gdpr=0` + } + ]; + expect(syncResults).to.deep.equal(expectedSyncs); + }); }); }); }); diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js index 84346a1149f..f5aa1014702 100644 --- a/test/spec/modules/a4gBidAdapter_spec.js +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { spec } from 'modules/a4gBidAdapter'; -describe('a4gAdapterTests', () => { - describe('bidRequestValidity', () => { - it('bidRequest with zoneId and deliveryUrl params', () => { +describe('a4gAdapterTests', function () { + describe('bidRequestValidity', function () { + it('bidRequest with zoneId and deliveryUrl params', function () { expect(spec.isBidRequestValid({ bidder: 'a4g', params: { @@ -13,7 +13,7 @@ describe('a4gAdapterTests', () => { })).to.equal(true); }); - it('bidRequest with only zoneId', () => { + it('bidRequest with only zoneId', function () { expect(spec.isBidRequestValid({ bidder: 'a4g', params: { @@ -22,7 +22,7 @@ describe('a4gAdapterTests', () => { })).to.equal(true); }); - it('bidRequest with only deliveryUrl', () => { + it('bidRequest with only deliveryUrl', function () { expect(spec.isBidRequestValid({ bidder: 'a4g', params: { @@ -32,7 +32,7 @@ describe('a4gAdapterTests', () => { }); }); - describe('bidRequest', () => { + describe('bidRequest', function () { const bidRequests = [{ 'bidder': 'a4g', 'bidId': '51ef8751f9aead', @@ -58,27 +58,27 @@ describe('a4gAdapterTests', () => { 'auctionId': '18fd8b8b0bd757' }]; - it('bidRequest method', () => { + it('bidRequest method', function () { const request = spec.buildRequests(bidRequests); expect(request.method).to.equal('GET'); }); - it('bidRequest url', () => { + it('bidRequest url', function () { const request = spec.buildRequests(bidRequests); expect(request.url).to.match(new RegExp(`${bidRequests[1].params.deliveryUrl}`)); }); - it('bidRequest data', () => { + it('bidRequest data', function () { const request = spec.buildRequests(bidRequests); - expect(request.data).to.exists; + expect(request.data).to.exist; }); - it('bidRequest zoneIds', () => { + it('bidRequest zoneIds', function () { const request = spec.buildRequests(bidRequests); expect(request.data.zoneId).to.equal('59304;59354'); }); - it('bidRequest gdpr consent', () => { + it('bidRequest gdpr consent', function () { const consentString = 'consentString'; const bidderRequest = { bidderCode: 'a4g', @@ -99,7 +99,7 @@ describe('a4gAdapterTests', () => { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const bidRequest = [{ 'bidder': 'a4g', 'bidId': '51ef8751f9aead', @@ -124,7 +124,7 @@ describe('a4gAdapterTests', () => { headers: {} }; - it('required keys', () => { + it('required keys', function () { const result = spec.interpretResponse(bidResponse, bidRequest); let requiredKeys = [ diff --git a/test/spec/modules/aardvarkBidAdapter_spec.js b/test/spec/modules/aardvarkBidAdapter_spec.js index d2b9cbc0fa8..b532fa4264a 100644 --- a/test/spec/modules/aardvarkBidAdapter_spec.js +++ b/test/spec/modules/aardvarkBidAdapter_spec.js @@ -1,9 +1,10 @@ import { expect } from 'chai'; -import { spec } from 'modules/aardvarkBidAdapter'; +import * as utils from 'src/utils'; +import { spec, resetUserSync } from 'modules/aardvarkBidAdapter'; -describe('aardvarkAdapterTest', () => { - describe('forming valid bidRequests', () => { - it('should accept valid bidRequests', () => { +describe('aardvarkAdapterTest', function () { + describe('forming valid bidRequests', function () { + it('should accept valid bidRequests', function () { expect(spec.isBidRequestValid({ bidder: 'aardvark', params: { @@ -14,7 +15,7 @@ describe('aardvarkAdapterTest', () => { })).to.equal(true); }); - it('should reject invalid bidRequests', () => { + it('should reject invalid bidRequests', function () { expect(spec.isBidRequestValid({ bidder: 'aardvark', params: { @@ -25,7 +26,7 @@ describe('aardvarkAdapterTest', () => { }); }); - describe('executing network requests', () => { + describe('executing network requests', function () { const bidRequests = [{ bidder: 'aardvark', params: { @@ -37,7 +38,8 @@ describe('aardvarkAdapterTest', () => { sizes: [300, 250], bidId: '1abgs362e0x48a8', bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', + userId: { tdid: 'eff98622-b5fd-44fa-9a49-6e846922d532' } }, { bidder: 'aardvark', @@ -54,21 +56,27 @@ describe('aardvarkAdapterTest', () => { auctionId: 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' }]; - it('should use HTTP GET method', () => { - const requests = spec.buildRequests(bidRequests); - requests.forEach(function(requestItem) { + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + + it('should use HTTP GET method', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { expect(requestItem.method).to.equal('GET'); }); }); - it('should call the correct bidRequest url', () => { - const requests = spec.buildRequests(bidRequests); + it('should call the correct bidRequest url', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(1); expect(requests[0].url).to.match(new RegExp('^\/\/adzone.pub.com/xiby/TdAx_RAZd/aardvark\?')); }); - it('should have correct data', () => { - const requests = spec.buildRequests(bidRequests); + it('should have correct data', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(1); expect(requests[0].data.version).to.equal(1); expect(requests[0].data.jsonp).to.equal(false); @@ -76,9 +84,16 @@ describe('aardvarkAdapterTest', () => { expect(requests[0].data.rtkreferer).to.not.be.undefined; expect(requests[0].data.RAZd).to.equal('22aidtbx5eabd9'); }); + + it('should have tdid, it is available in bidRequest', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.tdid).to.equal('eff98622-b5fd-44fa-9a49-6e846922d532'); + }); + }); }); - describe('splitting multi-auction ad units into own requests', () => { + describe('splitting multi-auction ad units into own requests', function () { const bidRequests = [{ bidder: 'aardvark', params: { @@ -108,22 +123,28 @@ describe('aardvarkAdapterTest', () => { auctionId: 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' }]; - it('should use HTTP GET method', () => { - const requests = spec.buildRequests(bidRequests); - requests.forEach(function(requestItem) { + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + + it('should use HTTP GET method', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { expect(requestItem.method).to.equal('GET'); }); }); - it('should call the correct bidRequest urls for each auction', () => { - const requests = spec.buildRequests(bidRequests); + it('should call the correct bidRequest urls for each auction', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests[0].url).to.match(new RegExp('^\/\/bidder.rtk.io/Toby/TdAx/aardvark\?')); expect(requests[0].data.categories.length).to.equal(2); expect(requests[1].url).to.match(new RegExp('^\/\/adzone.pub.com/xiby/RAZd/aardvark\?')); }); - it('should have correct data', () => { - const requests = spec.buildRequests(bidRequests); + it('should have correct data', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(2); expect(requests[0].data.version).to.equal(1); expect(requests[0].data.jsonp).to.equal(false); @@ -136,9 +157,16 @@ describe('aardvarkAdapterTest', () => { expect(requests[1].data.rtkreferer).to.not.be.undefined; expect(requests[1].data.RAZd).to.equal('22aidtbx5eabd9'); }); + + it('should have no tdid, it is not available in bidRequest', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.tdid).to.be.undefined; + }); + }); }); - describe('GDPR conformity', () => { + describe('GDPR conformity', function () { const bidRequests = [{ bidder: 'aardvark', params: { @@ -157,10 +185,13 @@ describe('aardvarkAdapterTest', () => { gdprConsent: { consentString: 'awefasdfwefasdfasd', gdprApplies: true + }, + refererInfo: { + referer: 'http://example.com' } }; - it('should transmit correct data', () => { + it('should transmit correct data', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(1); expect(requests[0].data.gdpr).to.equal(true); @@ -168,7 +199,7 @@ describe('aardvarkAdapterTest', () => { }); }); - describe('GDPR absence conformity', () => { + describe('GDPR absence conformity', function () { const bidRequests = [{ bidder: 'aardvark', params: { @@ -184,10 +215,13 @@ describe('aardvarkAdapterTest', () => { }]; const bidderRequest = { - gdprConsent: undefined + gdprConsent: undefined, + refererInfo: { + referer: 'http://example.com' + } }; - it('should transmit correct data', () => { + it('should transmit correct data', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(1); expect(requests[0].data.gdpr).to.be.undefined; @@ -195,8 +229,8 @@ describe('aardvarkAdapterTest', () => { }); }); - describe('interpretResponse', () => { - it('should handle bid responses', () => { + describe('interpretResponse', function () { + it('should handle bid responses', function () { const serverResponse = { body: [ { @@ -219,6 +253,7 @@ describe('aardvarkAdapterTest', () => { cid: '1abgs362e0x48a8', adm: '', ttl: 200, + ex: 'extraproperty' } ], headers: {} @@ -234,6 +269,7 @@ describe('aardvarkAdapterTest', () => { expect(result[0].currency).to.equal('USD'); expect(result[0].ttl).to.equal(200); expect(result[0].dealId).to.equal('dealing'); + expect(result[0].ex).to.be.undefined; expect(result[0].ad).to.not.be.undefined; expect(result[1].requestId).to.equal('1abgs362e0x48a8'); @@ -243,9 +279,10 @@ describe('aardvarkAdapterTest', () => { expect(result[1].currency).to.equal('USD'); expect(result[1].ttl).to.equal(200); expect(result[1].ad).to.not.be.undefined; + expect(result[1].ex).to.equal('extraproperty'); }); - it('should handle nobid responses', () => { + it('should handle nobid responses', function () { var emptyResponse = [{ nurl: '', cid: '9e5a09319e18f1', @@ -257,8 +294,129 @@ describe('aardvarkAdapterTest', () => { }]; var result = spec.interpretResponse({ body: emptyResponse }, {}); - expect(result.length).to.equal(1); - expect(result[0].cpm).to.equal(0.0); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + const syncOptions = { + iframeEnabled: true + }; + + it('should produce sync url', function () { + const syncs = spec.getUserSyncs(syncOptions); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('//sync.rtk.io/cs'); + }); + + it('should return empty, as we sync only once', function () { + const syncs = spec.getUserSyncs(syncOptions); + expect(syncs.length).to.equal(0); + }); + + it('should reset hasSynced flag, allowing another sync', function () { + resetUserSync(); + + const syncs = spec.getUserSyncs(syncOptions); + expect(syncs.length).to.equal(1); + }); + + it('should return empty when iframe disallowed', function () { + resetUserSync(); + + const noIframeOptions = { iframeEnabled: false }; + const syncs = spec.getUserSyncs(noIframeOptions); + expect(syncs.length).to.equal(0); + }); + + it('should produce sync url with gdpr params', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA' + }; + + resetUserSync(); + + const syncs = spec.getUserSyncs(syncOptions, null, gdprConsent); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('//sync.rtk.io/cs?g=1&c=BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'); + }); + }); + + describe('reading window.top properties', function () { + const bidCategories = ['bcat1', 'bcat2', 'bcat3']; + const bidRequests = [{ + bidder: 'aardvark', + params: { + ai: 'xiby', + sc: 'TdAx', + host: 'adzone.pub.com', + categories: bidCategories + }, + adUnitCode: 'RTK_aaaa', + transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', + sizes: [300, 250], + bidId: '1abgs362e0x48a8', + bidderRequestId: '70deaff71c281d', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', + userId: { tdid: 'eff98622-b5fd-44fa-9a49-6e846922d532' } + }]; + + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + + const topWin = { + innerWidth: 1366, + innerHeight: 768, + rtkcategories: ['cat1', 'cat2', 'cat3'] + }; + + let sandbox; + beforeEach(function () { + sandbox = sinon.createSandbox(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should have window.top dimensions', function () { + sandbox.stub(utils, 'getWindowTop').returns(topWin); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.w).to.equal(topWin.innerWidth); + expect(requestItem.data.h).to.equal(topWin.innerHeight); + }); + }); + + it('should have window dimensions, as backup', function () { + sandbox.stub(utils, 'getWindowTop').returns(undefined); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.w).to.equal(window.innerWidth); + expect(requestItem.data.h).to.equal(window.innerHeight); + }); + }); + + it('should have window.top & bid categories', function () { + sandbox.stub(utils, 'getWindowTop').returns(topWin); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + utils._each(topWin.categories, function (cat) { + expect(requestItem.data.categories).to.contain(cat); + }); + utils._each(bidCategories, function (cat) { + expect(requestItem.data.categories).to.contain(cat); + }); + }); }); }); }); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js new file mode 100644 index 00000000000..7437b45b6c1 --- /dev/null +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -0,0 +1,273 @@ +import { expect } from 'chai'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { spec } from 'modules/adagioBidAdapter'; + +describe('adagioAdapter', () => { + const adapter = newBidder(spec); + const ENDPOINT = 'https://mp.4dex.io/prebid'; + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'adagio', + 'params': { + siteId: '123', + placementId: 4, + categories: ['IAB12', 'IAB12-2'] + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c180kg4267tyqz', + 'bidderRequestId': '8vfscuixrovn8i', + 'auctionId': 'lel4fhp239i9km', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when site params is not passed', () => { + let bidTest = Object.assign({}, bid); + delete bidTest.params.siteId; + expect(spec.isBidRequestValid(bidTest)).to.equal(false); + }); + + it('should return false when placement params is not passed', () => { + let bidTest = Object.assign({}, bid); + delete bidTest.params.placementId; + expect(spec.isBidRequestValid(bidTest)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + let bidRequests = [ + // siteId 123 + { + 'bidder': 'adagio', + 'params': { + siteId: '123', + placementId: 4, + pagetypeId: '232', + categories: ['IAB12'] + }, + 'adUnitCode': 'adunit-code1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c180kg4267tyqz', + 'bidderRequestId': '8vfscuixrovn8i', + 'auctionId': 'lel4fhp239i9km', + }, + { + 'bidder': 'adagio', + 'params': { + siteId: '123', + placementId: 3, + pagetypeId: '232', + categories: ['IAB12'] + }, + 'adUnitCode': 'adunit-code2', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c180kg4267tyqz', + 'bidderRequestId': '8vfscuixrovn8i', + 'auctionId': 'lel4fhp239i9km', + }, + // siteId 456 + { + 'bidder': 'adagio', + 'params': { + siteId: '456', + placementId: 4, + pagetypeId: '232', + categories: ['IAB12'] + }, + 'adUnitCode': 'adunit-code3', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c180kg4267tyqz', + 'bidderRequestId': '8vfscuixrovn8i', + 'auctionId': 'lel4fhp239i9km', + } + ]; + + let consentString = 'theConsentString'; + let bidderRequest = { + 'bidderCode': 'adagio', + 'auctionId': '12jejebn', + 'bidderRequestId': 'hehehehbeheh', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true, + allowAuctionWithoutConsent: true + } + }; + + it('groups requests by siteId', () => { + const requests = spec.buildRequests(bidRequests); + expect(requests).to.have.lengthOf(2); + + expect(requests[0].data.siteId).to.equal('123'); + expect(requests[0].data.adUnits).to.have.lengthOf(2); + + expect(requests[1].data.siteId).to.equal('456'); + expect(requests[1].data.adUnits).to.have.lengthOf(1); + }); + + it('sends bid request to ENDPOINT_PB via POST', () => { + const requests = spec.buildRequests(bidRequests); + expect(requests).to.have.lengthOf(2); + const request = requests[0]; + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(ENDPOINT); + }); + + it('features params must be an empty object if featurejs is not loaded', () => { + const requests = spec.buildRequests(bidRequests); + expect(requests).to.have.lengthOf(2); + const request = requests[0]; + const expected = {}; + expect(request.data.adUnits[0].params.features).to.deep.equal(expected); + }); + + it('GDPR consent is applied', () => { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.have.lengthOf(2); + const request = requests[0]; + expect(request.data.gdpr).to.exist; + expect(request.data.gdpr.consentString).to.exist.and.to.equal(consentString); + expect(request.data.gdpr.consentRequired).to.exist.and.to.equal(1); + }); + + it('GDPR consent is not applied', () => { + bidderRequest.gdprConsent.gdprApplies = false; + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.have.lengthOf(2); + const request = requests[0]; + expect(request.data.gdpr).to.exist; + expect(request.data.gdpr.consentString).to.exist.and.to.equal(consentString); + expect(request.data.gdpr.consentRequired).to.exist.and.to.equal(0); + }); + + it('GDPR consent is undefined', () => { + delete bidderRequest.gdprConsent.consentString; + delete bidderRequest.gdprConsent.gdprApplies; + delete bidderRequest.gdprConsent.allowAuctionWithoutConsent; + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.have.lengthOf(2); + const request = requests[0]; + expect(request.data.gdpr).to.exist; + expect(request.data.gdpr).to.not.have.property('consentString'); + expect(request.data.gdpr).to.not.have.property('gdprApplies'); + expect(request.data.gdpr).to.not.have.property('allowAuctionWithoutConsent'); + }); + + it('GDPR consent bidderRequest does not have gdprConsent', () => { + delete bidderRequest.gdprConsent; + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.have.lengthOf(2); + const request = requests[0]; + expect(request.data.gdpr).to.exist; + expect(request.data.gdpr).to.be.empty; + }); + }); + + describe('interpretResponse', () => { + let serverResponse = { + body: { + bids: [ + { + ad: '
    ', + cpm: 1, + creativeId: 'creativeId', + currency: 'EUR', + height: 250, + netRevenue: true, + requestId: 'c180kg4267tyqz', + ttl: 360, + width: 300 + } + ] + } + }; + + let bidRequest = { + 'data': { + 'adUnits': [ + { + 'bidder': 'adagio', + 'params': { + siteId: '666', + placementId: 4, + pagetypeId: '232', + categories: ['IAB12'] + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c180kg4267tyqz', + 'bidderRequestId': '8vfscuixrovn8i', + 'auctionId': 'lel4fhp239i9km', + } + ] + } + }; + + it('should get correct bid response', () => { + let expectedResponse = [{ + ad: '
    ', + cpm: 1, + creativeId: 'creativeId', + currency: 'EUR', + height: 250, + netRevenue: true, + requestId: 'c180kg4267tyqz', + ttl: 360, + width: 300, + categories: [], + pagetypeId: '232', + placementId: 4, + }]; + expect(spec.interpretResponse(serverResponse, bidRequest)).to.be.an('array'); + expect(spec.interpretResponse(serverResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + }); + + describe('getUserSyncs', () => { + const syncOptions = { + 'iframeEnabled': 'true' + } + const serverResponses = [ + { + body: { + userSyncs: [ + { + t: 'i', + u: 'https://test.url.com/setuid' + }, + { + t: 'p', + u: 'https://test.url.com/setuid' + } + ] + } + } + ]; + + const emptyServerResponses = [ + { + body: '' + } + ]; + + it('should handle correctly user syncs', () => { + let result = spec.getUserSyncs(syncOptions, serverResponses); + let emptyResult = spec.getUserSyncs(syncOptions, emptyServerResponses); + expect(result[0].type).to.equal('iframe'); + expect(result[0].url).contain('setuid'); + expect(result[1].type).to.equal('image'); + expect(emptyResult).to.equal(false); + }); + }); +}); diff --git a/test/spec/modules/adbutlerBidAdapter_spec.js b/test/spec/modules/adbutlerBidAdapter_spec.js index a46039402e6..cf6b52f70e5 100644 --- a/test/spec/modules/adbutlerBidAdapter_spec.js +++ b/test/spec/modules/adbutlerBidAdapter_spec.js @@ -1,10 +1,10 @@ import {expect} from 'chai'; import {spec} from 'modules/adbutlerBidAdapter'; -describe('AdButler adapter', () => { +describe('AdButler adapter', function () { let bidRequests; - beforeEach(() => { + beforeEach(function () { bidRequests = [ { bidder: 'adbutler', @@ -25,9 +25,9 @@ describe('AdButler adapter', () => { ]; }); - describe('implementation', () => { - describe('for requests', () => { - it('should accept valid bid', () => { + describe('implementation', function () { + describe('for requests', function () { + it('should accept valid bid', function () { let validBid = { bidder: 'adbutler', params: { @@ -40,7 +40,7 @@ describe('AdButler adapter', () => { expect(isValid).to.equal(true); }); - it('should reject invalid bid', () => { + it('should reject invalid bid', function () { let invalidBid = { bidder: 'adbutler', params: { @@ -52,7 +52,7 @@ describe('AdButler adapter', () => { expect(isValid).to.equal(false); }); - it('should use custom domain string', () => { + it('should use custom domain string', function () { let bidRequests = [ { bidId: '3c9408cdbf2f68', @@ -73,7 +73,7 @@ describe('AdButler adapter', () => { expect(requestURL).to.have.string('.dan.test'); }); - it('should set default domain', () => { + it('should set default domain', function () { let requests = spec.buildRequests(bidRequests), request = requests[0]; @@ -82,14 +82,14 @@ describe('AdButler adapter', () => { expect(domain).to.equal('http://servedbyadbutler.com'); }); - it('should set the keyword parameter', () => { + it('should set the keyword parameter', function () { let requests = spec.buildRequests(bidRequests), requestURL = requests[0].url; expect(requestURL).to.have.string(';kw=red;'); }); - it('should increment the count for the same zone', () => { + it('should increment the count for the same zone', function () { let bidRequests = [ { sizes: [[300, 250]], @@ -118,8 +118,8 @@ describe('AdButler adapter', () => { }); }); - describe('bid responses', () => { - it('should return complete bid response', () => { + describe('bid responses', function () { + it('should return complete bid response', function () { let serverResponse = { body: { status: 'SUCCESS', @@ -149,7 +149,7 @@ describe('AdButler adapter', () => { expect(bids[0].ad).to.have.string('http://tracking.pixel.com/params=info'); }); - it('should return empty bid response', () => { + it('should return empty bid response', function () { let serverResponse = { status: 'NO_ELIGIBLE_ADS', zone_id: 210083, @@ -162,7 +162,7 @@ describe('AdButler adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response on incorrect size', () => { + it('should return empty bid response on incorrect size', function () { let serverResponse = { status: 'SUCCESS', account_id: 167283, @@ -177,7 +177,7 @@ describe('AdButler adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response with CPM too low', () => { + it('should return empty bid response with CPM too low', function () { let serverResponse = { status: 'SUCCESS', account_id: 167283, @@ -192,7 +192,7 @@ describe('AdButler adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response with CPM too high', () => { + it('should return empty bid response with CPM too high', function () { let serverResponse = { status: 'SUCCESS', account_id: 167283, diff --git a/test/spec/modules/adformBidAdapter_spec.js b/test/spec/modules/adformBidAdapter_spec.js index 21ff84bdad5..f50474ae500 100644 --- a/test/spec/modules/adformBidAdapter_spec.js +++ b/test/spec/modules/adformBidAdapter_spec.js @@ -2,11 +2,12 @@ import {assert, expect} from 'chai'; import * as url from 'src/url'; import {spec} from 'modules/adformBidAdapter'; import { BANNER, VIDEO } from 'src/mediaTypes'; +import { config } from 'src/config'; -describe('Adform adapter', () => { +describe('Adform adapter', function () { let serverResponse, bidRequest, bidResponses; let bids = []; - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'adform', 'params': { @@ -14,11 +15,11 @@ describe('Adform adapter', () => { } }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { assert(spec.isBidRequestValid(bid)); }); - it('should return false when required params are missing', () => { + it('should return false when required params are missing', function () { bid.params = { adxDomain: 'adx.adform.net' }; @@ -26,14 +27,14 @@ describe('Adform adapter', () => { }); }); - describe('buildRequests', () => { - it('should pass multiple bids via single request', () => { + describe('buildRequests', function () { + it('should pass multiple bids via single request', function () { let request = spec.buildRequests(bids); let parsedUrl = parseUrl(request.url); assert.lengthOf(parsedUrl.items, 7); }); - it('should handle global request parameters', () => { + it('should handle global request parameters', function () { let parsedUrl = parseUrl(spec.buildRequests([bids[0]]).url); let query = parsedUrl.query; @@ -45,12 +46,30 @@ describe('Adform adapter', () => { assert.equal(query.url, encodeURIComponent('some// there')); }); - it('should set correct request method', () => { + it('should set correct request method', function () { let request = spec.buildRequests([bids[0]]); assert.equal(request.method, 'GET'); }); - it('should correctly form bid items', () => { + it('should pass request currency from config', function () { + config.setConfig({ currency: { adServerCurrency: 'PLN' } }); + let request = parseUrl(spec.buildRequests(bids).url); + + request.items.forEach(item => { + assert.equal(item.rcur, 'PLN'); + }); + }); + + it('should prefer bid currency over global config', function () { + config.setConfig({ currency: { adServerCurrency: 'PLN' } }); + bids[0].params.rcur = 'USD'; + let request = parseUrl(spec.buildRequests(bids).url); + const currencies = request.items.map(item => item.rcur); + + assert.deepEqual(currencies, [ 'USD', 'PLN', 'PLN', 'PLN', 'PLN', 'PLN', 'PLN' ]); + }); + + it('should correctly form bid items', function () { let bidList = bids; let request = spec.buildRequests(bidList); let parsedUrl = parseUrl(request.url); @@ -93,22 +112,13 @@ describe('Adform adapter', () => { ]); }); - it('should not change original validBidRequests object', () => { + it('should not change original validBidRequests object', function () { var resultBids = JSON.parse(JSON.stringify(bids[0])); let request = spec.buildRequests([bids[0]]); assert.deepEqual(resultBids, bids[0]); }); - it('should send GDPR Consent data to adform', () => { - var resultBids = JSON.parse(JSON.stringify(bids[0])); - let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: 1, consentString: 'concentDataString'}}); - let parsedUrl = parseUrl(request.url).query; - - assert.equal(parsedUrl.gdpr, 1); - assert.equal(parsedUrl.gdpr_consent, 'concentDataString'); - }); - - it('should set gross to the request, if there is any gross priceType', () => { + it('should set gross to the request, if there is any gross priceType', function () { let request = spec.buildRequests([bids[5], bids[5]]); let parsedUrl = parseUrl(request.url); @@ -119,27 +129,50 @@ describe('Adform adapter', () => { assert.equal(parsedUrl.query.pt, 'gross'); }); - }); - describe('interpretResponse', () => { - it('should respond with empty response when there is empty serverResponse', () => { - let result = spec.interpretResponse({ body: {} }, {}); - assert.deepEqual(result, []); - }); - it('should respond with empty response when sizes doesn\'t match', () => { - serverResponse.body[0].response = 'banner'; - serverResponse.body[0].width = 100; - serverResponse.body[0].height = 150; + describe('gdpr', function () { + it('should send GDPR Consent data to adform if gdprApplies', function () { + let resultBids = JSON.parse(JSON.stringify(bids[0])); + let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); + let parsedUrl = parseUrl(request.url).query; - serverResponse.body = [serverResponse.body[0]]; - bidRequest.bids = [bidRequest.bids[0]]; - let result = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(parsedUrl.gdpr, 'true'); + assert.equal(parsedUrl.gdpr_consent, 'concentDataString'); + }); + + it('should not send GDPR Consent data to adform if gdprApplies is false or undefined', function () { + let resultBids = JSON.parse(JSON.stringify(bids[0])); + let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: false, consentString: 'concentDataString'}}); + let parsedUrl = parseUrl(request.url).query; - assert.equal(serverResponse.body.length, 1); - assert.equal(serverResponse.body[0].response, 'banner'); + assert.ok(!parsedUrl.gdpr); + assert.ok(!parsedUrl.gdpr_consent); + + request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: undefined, consentString: 'concentDataString'}}); + assert.ok(!parsedUrl.gdpr); + assert.ok(!parsedUrl.gdpr_consent); + }); + + it('should return GDPR Consent data with request data', function () { + let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); + + assert.deepEqual(request.gdpr, { + gdpr: true, + gdpr_consent: 'concentDataString' + }); + + request = spec.buildRequests([bids[0]]); + assert.ok(!request.gdpr); + }); + }); + }); + + describe('interpretResponse', function () { + it('should respond with empty response when there is empty serverResponse', function () { + let result = spec.interpretResponse({ body: {} }, {}); assert.deepEqual(result, []); }); - it('should respond with empty response when response from server is not banner', () => { + it('should respond with empty response when response from server is not banner', function () { serverResponse.body[0].response = 'not banner'; serverResponse.body = [serverResponse.body[0]]; bidRequest.bids = [bidRequest.bids[0]]; @@ -147,7 +180,7 @@ describe('Adform adapter', () => { assert.deepEqual(result, []); }); - it('should interpret server response correctly with one bid', () => { + it('should interpret server response correctly with one bid', function () { serverResponse.body = [serverResponse.body[0]]; bidRequest.bids = [bidRequest.bids[0]]; let result = spec.interpretResponse(serverResponse, bidRequest)[0]; @@ -166,7 +199,7 @@ describe('Adform adapter', () => { assert.equal(result.transactionId, '5f33781f-9552-4ca1'); }); - it('should set correct netRevenue', () => { + it('should set correct netRevenue', function () { serverResponse.body = [serverResponse.body[0]]; bidRequest.bids = [bidRequest.bids[1]]; bidRequest.netRevenue = 'gross'; @@ -175,22 +208,22 @@ describe('Adform adapter', () => { assert.equal(result.netRevenue, false); }); - it('should create bid response item for every requested item', () => { + it('should create bid response item for every requested item', function () { let result = spec.interpretResponse(serverResponse, bidRequest); assert.lengthOf(result, 5); }); - it('should create bid response with vast xml', () => { + it('should create bid response with vast xml', function () { const result = spec.interpretResponse(serverResponse, bidRequest)[3]; assert.equal(result.vastXml, ''); }); - it('should create bid response with vast url', () => { + it('should create bid response with vast url', function () { const result = spec.interpretResponse(serverResponse, bidRequest)[4]; assert.equal(result.vastUrl, 'vast://url'); }); - it('should set mediaType on bid response', () => { + it('should set mediaType on bid response', function () { const expected = [ BANNER, BANNER, BANNER, VIDEO, VIDEO ]; const result = spec.interpretResponse(serverResponse, bidRequest); for (let i = 0; i < result.length; i++) { @@ -198,16 +231,82 @@ describe('Adform adapter', () => { } }); - it('should set default netRevenue as gross', () => { + it('should set default netRevenue as gross', function () { bidRequest.netRevenue = 'gross'; const result = spec.interpretResponse(serverResponse, bidRequest); for (let i = 0; i < result.length; i++) { assert.equal(result[i].netRevenue, false); } }); + + it('should set gdpr if it exist in bidRequest', function () { + bidRequest.gdpr = { + gdpr: true, + gdpr_consent: 'ERW342EIOWT34234KMGds' + }; + let result = spec.interpretResponse(serverResponse, bidRequest); + for (let i = 0; i < result.length; i++) { + assert.equal(result[i].gdpr, true); + assert.equal(result[i].gdpr_consent, 'ERW342EIOWT34234KMGds'); + }; + + bidRequest.gdpr = undefined; + result = spec.interpretResponse(serverResponse, bidRequest); + for (let i = 0; i < result.length; i++) { + assert.ok(!result[i].gdpr); + assert.ok(!result[i].gdpr_consent); + }; + }); + + describe('verifySizes', function () { + it('should respond with empty response when sizes doesn\'t match', function () { + serverResponse.body[0].response = 'banner'; + serverResponse.body[0].width = 100; + serverResponse.body[0].height = 150; + + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + let result = spec.interpretResponse(serverResponse, bidRequest); + + assert.equal(serverResponse.body.length, 1); + assert.equal(serverResponse.body[0].response, 'banner'); + assert.deepEqual(result, []); + }); + it('should respond with empty response when sizes as a strings doesn\'t match', function () { + serverResponse.body[0].response = 'banner'; + serverResponse.body[0].width = 100; + serverResponse.body[0].height = 150; + + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + + bidRequest.bids[0].sizes = [['101', '150']]; + let result = spec.interpretResponse(serverResponse, bidRequest); + + assert.equal(serverResponse.body.length, 1); + assert.equal(serverResponse.body[0].response, 'banner'); + assert.deepEqual(result, []); + }); + it('should support size dimensions as a strings', function () { + serverResponse.body[0].response = 'banner'; + serverResponse.body[0].width = 300; + serverResponse.body[0].height = 600; + + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + + bidRequest.bids[0].sizes = [['300', '250'], ['250', '300'], ['300', '600'], ['600', '300']] + let result = spec.interpretResponse(serverResponse, bidRequest); + + assert.equal(result[0].width, 300); + assert.equal(result[0].height, 600); + }); + }) }); - beforeEach(() => { + beforeEach(function () { + config.setConfig({ currency: {} }); + let sizes = [[250, 300], [300, 250], [300, 600]]; let placementCode = ['div-01', 'div-02', 'div-03', 'div-04', 'div-05']; let params = [{ mid: 1, url: 'some// there' }, {adxDomain: null, mid: 2, someVar: 'someValue', pt: 'gross'}, { adxDomain: null, mid: 3, pdom: 'home' }, {mid: 5, pt: 'net'}, {mid: 6, pt: 'gross'}]; diff --git a/test/spec/modules/adformOpenRTBBidAdapter_spec.js b/test/spec/modules/adformOpenRTBBidAdapter_spec.js new file mode 100644 index 00000000000..32795c24ef6 --- /dev/null +++ b/test/spec/modules/adformOpenRTBBidAdapter_spec.js @@ -0,0 +1,637 @@ +// jshint esversion: 6, es3: false, node: true +import {assert, expect} from 'chai'; +import * as url from 'src/url'; +import {spec} from 'modules/adformOpenRTBBidAdapter'; +import { NATIVE } from 'src/mediaTypes'; +import { config } from 'src/config'; + +describe('AdformOpenRTB adapter', function () { + let serverResponse, bidRequest, bidResponses; + let bids = []; + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'adformOpenRTB', + 'params': { + 'mid': '19910113' + } + }; + + 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 = { adxDomain: 'adx.adform.net' }; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + it('should send request with correct structure', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { + siteId: 'siteId', + adxDomain: '10.8.57.207' + } + }]; + let request = spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }); + + assert.equal(request.method, 'POST'); + assert.equal(request.url, '//10.8.57.207/adx/openrtb'); + assert.deepEqual(request.options, {contentType: 'application/json'}); + assert.ok(request.data); + }); + + describe('gdpr', function () { + it('should send GDPR Consent data to adform if gdprApplies', function () { + let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { referer: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); + assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); + assert.equal(typeof request.regs.ext.gdpr, 'number'); + }); + + it('should send gdpr as number', function () { + let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { referer: 'page' } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(typeof request.regs.ext.gdpr, 'number'); + }); + + it('should not send GDPR Consent data to adform if gdprApplies is false or undefined', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let bidderRequest = {gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, refererInfo: { referer: 'page' }}; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + it('should send default GDPR Consent data to adform', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + }); + + it('should add test and is_debug to request, if test is set in parameters', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', test: 1 } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.ok(request.is_debug); + assert.equal(request.test, 1); + }); + + it('should have default request structure', function () { + let keys = 'site,device,source,ext,imp'.split(','); + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + let data = Object.keys(request); + + assert.deepEqual(keys, data); + }); + + it('should set request keys correct values', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' }, + transactionId: 'transactionId' + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.source.tid, validBidRequests[0].transactionId); + assert.equal(request.source.fd, 1); + }); + + it('should send info about device', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.device.ua, navigator.userAgent); + }); + it('should send info about the site', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', publisher: {id: '123123', domain: 'publisher.domain.com', name: 'publisher\'s name'} } + }]; + let refererInfo = { referer: 'page' }; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data); + + assert.deepEqual(request.site, { + page: refererInfo.referer, + publisher: validBidRequests[0].params.publisher, + id: validBidRequests[0].params.siteId + }); + }); + + it('should send currency if defined', function () { + config.setConfig({ currency: { adServerCurrency: 'EUR' } }); + let validBidRequests = [{ params: {} }]; + let refererInfo = { referer: 'page' }; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data); + + assert.deepEqual(request.cur, [ 'EUR' ]); + }); + + describe('priceType', function () { + it('should send default priceType', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.ext.pt, 'net'); + }); + it('should send correct priceType value', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', priceType: 'net' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.ext.pt, 'net'); + }); + }); + + describe('bids', function () { + it('should add more than one bid to the request', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }, { + bidId: 'bidId2', + params: { siteId: 'siteId' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.imp.length, 2); + }); + it('should add incrementing values of id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId' } + }, { + bidId: 'bidId2', + params: { siteId: 'siteId' } + }, { + bidId: 'bidId3', + params: { siteId: 'siteId' } + }]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; + + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].id, i + 1); + } + }); + + it('should add mid', function () { + let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', mid: 1000 } }, + { bidId: 'bidId2', params: { siteId: 'siteId', mid: 1001 } }, + { bidId: 'bidId3', params: { siteId: 'siteId', mid: 1002 } }]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].tagid, validBidRequests[i].params.mid); + } + }); + + describe('native', function () { + describe('assets', function () { + it('should set correct asset id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }]; + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0].native.request.assets; + + assert.equal(assets[0].id, 0); + assert.equal(assets[1].id, 3); + assert.equal(assets[2].id, 4); + }); + it('should add required key if it is necessary', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 }, + sponsoredBy: { required: true, len: 140 } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0].native.request.assets; + + assert.equal(assets[0].required, 1); + assert.ok(!assets[1].required); + assert.ok(!assets[2].required); + assert.equal(assets[3].required, 1); + }); + + it('should map img and data assets', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: true, sizes: [150, 50] }, + icon: { required: false, sizes: [50, 50] }, + body: { required: false, len: 140 }, + sponsoredBy: { required: true }, + cta: { required: false }, + clickUrl: { required: false } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].title); + assert.equal(assets[0].title.len, 140); + assert.deepEqual(assets[1].img, { type: 3, w: 150, h: 50 }); + assert.deepEqual(assets[2].img, { type: 1, w: 50, h: 50 }); + assert.deepEqual(assets[3].data, { type: 2, len: 140 }); + assert.deepEqual(assets[4].data, { type: 1 }); + assert.deepEqual(assets[5].data, { type: 12 }); + assert.ok(!assets[6]); + }); + + describe('icon/image sizing', function () { + it('should flatten sizes and utilise first pair', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + image: { + sizes: [[200, 300], [100, 200]] + }, + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.w, 200); + assert.equal(assets[0].img.h, 300); + }); + }); + + it('should utilise aspect_ratios', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + image: { + aspect_ratios: [{ + min_width: 100, + ratio_height: 3, + ratio_width: 1 + }] + }, + icon: { + aspect_ratios: [{ + min_width: 10, + ratio_height: 5, + ratio_width: 2 + }] + } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 100); + assert.equal(assets[0].img.hmin, 300); + + assert.ok(assets[1].img); + assert.equal(assets[1].img.wmin, 10); + assert.equal(assets[1].img.hmin, 25); + }); + + it('should not throw error if aspect_ratios config is not defined', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + image: { + aspect_ratios: [] + }, + icon: { + aspect_ratios: [] + } + } + }]; + + assert.doesNotThrow(() => spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } })); + }); + }); + + it('should expect any dimensions if min_width not passed', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + image: { + aspect_ratios: [{ + ratio_height: 3, + ratio_width: 1 + }] + } + } + }]; + + let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0].native.request.assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 0); + assert.equal(assets[0].img.hmin, 0); + assert.ok(!assets[1]); + }); + }); + }); + }); + + describe('interpretResponse', function () { + it('should return if no body in response', function () { + let serverResponse = {}; + let bidRequest = {}; + + assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); + }); + it('should return more than one bids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{impid: '1', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}}] + }, { + bid: [{impid: '2', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 1, data: {value: 'Asset title text'}}]}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId2', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(spec.interpretResponse(serverResponse, bidRequest).length, 2); + }); + + it('should parse seatbids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [ + {impid: '1', native: {ver: '1.1', link: { url: 'link1' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}}, + {impid: '4', native: {ver: '1.1', link: { url: 'link4' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}} + ] + }, { + bid: [{impid: '2', native: {ver: '1.1', link: { url: 'link2' }, assets: [{id: 1, data: {value: 'Asset title text'}}]}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId2', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId3', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + }, + { + bidId: 'bidId4', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest).map(bid => { + const { requestId, native: { clickUrl } } = bid; + return [ requestId, clickUrl ]; + }); + + assert.equal(bids.length, 3); + assert.deepEqual(bids, [[ 'bidId1', 'link1' ], [ 'bidId2', 'link2' ], [ 'bidId4', 'link4' ]]); + }); + + it('should set correct values to bid', function () { + let serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ + bid: [ + { + impid: '1', + price: 93.1231, + crid: '12312312', + native: { + assets: [], + link: { url: 'link' }, + imptrackers: ['imptrackers url1', 'imptrackers url2'] + } + } + ] + }], + cur: 'NOK' + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { siteId: 'siteId', mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 }, + image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, + body: { len: 140 } + } + } + ] + }; + + const bids = spec.interpretResponse(serverResponse, bidRequest); + const bid = serverResponse.body.seatbid[0].bid[0]; + assert.deepEqual(bids[0].requestId, bidRequest.bids[0].bidId); + assert.deepEqual(bids[0].cpm, bid.price); + assert.deepEqual(bids[0].creativeId, bid.crid); + assert.deepEqual(bids[0].ttl, 360); + assert.deepEqual(bids[0].netRevenue, false); + assert.deepEqual(bids[0].currency, serverResponse.body.cur); + assert.deepEqual(bids[0].mediaType, 'native'); + assert.deepEqual(bids[0].bidderCode, 'adformOpenRTB'); + }); + it('should set correct native params', function () { + const bid = [ + { + impid: '1', + price: 93.1231, + crid: '12312312', + native: { + assets: [ + { + data: null, + id: 0, + img: null, + required: 0, + title: {text: 'title', len: null}, + video: null + }, { + data: null, + id: 2, + img: {type: null, url: 'test.url.com/Files/58345/308185.jpg?bv=1', w: 30, h: 10}, + required: 0, + title: null, + video: null + }, { + data: null, + id: 3, + img: {type: null, url: 'test.url.com/Files/58345/308200.jpg?bv=1', w: 100, h: 100}, + required: 0, + title: null, + video: null + }, { + data: {type: null, len: null, value: 'body'}, + id: 4, + img: null, + required: 0, + title: null, + video: null + }, { + data: {type: null, len: null, value: 'cta'}, + id: 1, + img: null, + required: 0, + title: null, + video: null + }, { + data: {type: null, len: null, value: 'sponsoredBy'}, + id: 5, + img: null, + required: 0, + title: null, + video: null + } + ], + link: { url: 'clickUrl', clicktrackers: ['clickTracker1', 'clickTracker2'] }, + imptrackers: ['imptrackers url1', 'imptrackers url2'], + jstracker: 'jstracker' + } + } + ]; + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ bid }], + cur: 'NOK' + } + }; + let bidRequest = { + data: {}, + bids: [{ bidId: 'bidId1' }] + }; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0].native; + const native = bid[0].native; + const assets = native.assets; + assert.deepEqual({ + clickUrl: native.link.url, + clickTrackers: native.link.clicktrackers, + impressionTrackers: native.imptrackers, + javascriptTrackers: [ native.jstracker ], + title: assets[0].title.text, + icon: {url: assets[1].img.url, width: assets[1].img.w, height: assets[1].img.h}, + image: {url: assets[2].img.url, width: assets[2].img.w, height: assets[2].img.h}, + body: assets[3].data.value, + cta: assets[4].data.value, + sponsoredBy: assets[5].data.value + }, result); + }); + it('should return empty when there is no bids in response', function () { + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ bid: [] }], + cur: 'NOK' + } + }; + let bidRequest = { + data: {}, + bids: [{ bidId: 'bidId1' }] + }; + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.ok(!result); + }); + }); +}); diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js index 4239712ccaf..2b8834f1e1d 100644 --- a/test/spec/modules/adgenerationBidAdapter_spec.js +++ b/test/spec/modules/adgenerationBidAdapter_spec.js @@ -1,31 +1,32 @@ import {expect} from 'chai'; -import * as utils from 'src/utils'; import {spec} from 'modules/adgenerationBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; import {NATIVE} from 'src/mediaTypes'; +import {config} from 'src/config'; +import prebid from '../../../package.json'; -describe('AdgenerationAdapter', () => { +describe('AdgenerationAdapter', function () { const adapter = newBidder(spec); const ENDPOINT = ['http://api-test.scaleout.jp/adsv/v1', 'https://d.socdm.com/adsv/v1']; - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - let bid = { + describe('isBidRequestValid', function () { + const bid = { 'bidder': 'adg', 'params': { id: '58278', // banner } }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = {}; @@ -33,17 +34,16 @@ describe('AdgenerationAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { const bidRequests = [ { // banner bidder: 'adg', params: { id: '58278', - width: '300', - height: '250' + currency: 'JPY', }, adUnitCode: 'adunit-code', - sizes: [[300, 250]], + sizes: [[300, 250], [320, 100]], bidId: '2f6ac468a9c15e', bidderRequestId: '14a9f773e30243', auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', @@ -53,8 +53,7 @@ describe('AdgenerationAdapter', () => { bidder: 'adg', params: { id: '58278', - width: '300', - height: '250' + currency: 'JPY', }, mediaTypes: { native: { @@ -87,35 +86,60 @@ describe('AdgenerationAdapter', () => { transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' } ]; + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; const data = { - banner: 'posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&imark=1', - native: 'posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3' + banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.0.1&imark=1&tp=http%3A%2F%2Fexample.com`, + bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.0.1&imark=1&tp=http%3A%2F%2Fexample.com`, + native: 'posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=' + prebid.version + '&sdkname=prebidjs&adapterver=1.0.1&tp=http%3A%2F%2Fexample.com' }; - it('sends bid request to ENDPOINT via GET', () => { - const request = spec.buildRequests(bidRequests)[0]; + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.url).to.equal(ENDPOINT[1]); expect(request.method).to.equal('GET'); }); - it('sends bid request to debug ENDPOINT via GET', () => { + it('sends bid request to debug ENDPOINT via GET', function () { bidRequests[0].params.debug = true; - const request = spec.buildRequests(bidRequests)[0]; + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.url).to.equal(ENDPOINT[0]); expect(request.method).to.equal('GET'); }); - it('should attache params to the banner request', () => { - const request = spec.buildRequests(bidRequests)[0]; + it('should attache params to the banner request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.data).to.equal(data.banner); }); - it('should attache params to the native request', () => { - const request = spec.buildRequests(bidRequests)[1]; + it('should attache params to the native request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[1]; expect(request.data).to.equal(data.native); }); + it('allows setConfig to set bidder currency for JPY', function () { + config.setConfig({ + currency: { + adServerCurrency: 'JPY' + } + }); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data).to.equal(data.banner); + config.resetConfig(); + }); + it('allows setConfig to set bidder currency for USD', function () { + config.setConfig({ + currency: { + adServerCurrency: 'USD' + } + }); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data).to.equal(data.bannerUSD); + config.resetConfig(); + }); }); - - describe('interpretResponse', () => { + describe('interpretResponse', function () { const bidRequests = { banner: { bidRequest: { @@ -175,7 +199,7 @@ describe('AdgenerationAdapter', () => { results: [], }, banner: { - ad: '
    ', + ad: '
    ', beacon: '', cpm: 36.0008, displaytype: '1', @@ -191,11 +215,11 @@ describe('AdgenerationAdapter', () => { dealid: 'fd5sa5fa7f', ttl: 1000, results: [ - {ad: '
    '}, + {ad: '
    '}, ] }, native: { - ad: '↵ ↵ ↵ ↵ ↵
    ↵ ', + ad: '↵ ↵ ↵ ↵ ↵
    ↵ ', beacon: '', cpm: 36.0008, displaytype: '1', @@ -214,7 +238,7 @@ describe('AdgenerationAdapter', () => { { data: { label: 'optout_url', - value: 'https://supership.jp/optout/' + value: 'https://supership.jp/optout/#' }, id: 502 }, @@ -237,7 +261,7 @@ describe('AdgenerationAdapter', () => { id: 2, img: { h: 250, - url: 'https://s3-ap-northeast-1.amazonaws.com/sdk-temp/adg-sample-ad/img/300x250.png', + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', w: 300 }, required: 1 @@ -267,10 +291,10 @@ describe('AdgenerationAdapter', () => { required: 0 } ], - imptrackers: ['https://s3-ap-northeast-1.amazonaws.com/adg-dummy-dsp/1x1.gif'], + imptrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'], link: { clicktrackers: [ - 'https://s3-ap-northeast-1.amazonaws.com/adg-dummy-dsp/1x1_clicktracker_access.gif' + 'https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif' ], url: 'https://supership.jp' }, @@ -298,8 +322,7 @@ describe('AdgenerationAdapter', () => { currency: 'JPY', netRevenue: true, ttl: 1000, - referrer: utils.getTopWindowUrl(), - ad: '
    ', + ad: '
    ', }, native: { requestId: '2f6ac468a9c15e', @@ -311,12 +334,11 @@ describe('AdgenerationAdapter', () => { currency: 'JPY', netRevenue: true, ttl: 1000, - referrer: utils.getTopWindowUrl(), - ad: '↵
    ', + ad: '↵
    ', native: { title: 'Title', image: { - url: 'https://s3-ap-northeast-1.amazonaws.com/sdk-temp/adg-sample-ad/img/300x250.png', + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', height: 250, width: 300 }, @@ -328,20 +350,21 @@ describe('AdgenerationAdapter', () => { sponsoredBy: 'Sponsored', body: 'Description', cta: 'CTA', + privacyLink: 'https://supership.jp/optout/#', clickUrl: 'https://supership.jp', - clickTrackers: ['https://s3-ap-northeast-1.amazonaws.com/adg-dummy-dsp/1x1_clicktracker_access.gif'], - impressionTrackers: ['https://s3-ap-northeast-1.amazonaws.com/adg-dummy-dsp/1x1.gif'] + clickTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif'], + impressionTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'] }, mediaType: NATIVE } }; - it('no bid responses', () => { + it('no bid responses', function () { const result = spec.interpretResponse({body: serverResponse.noAd}, bidRequests.banner); expect(result.length).to.equal(0); }); - it('handles banner responses', () => { + it('handles banner responses', function () { const result = spec.interpretResponse({body: serverResponse.banner}, bidRequests.banner)[0]; expect(result.requestId).to.equal(bidResponses.banner.requestId); expect(result.width).to.equal(bidResponses.banner.width); @@ -351,11 +374,10 @@ describe('AdgenerationAdapter', () => { expect(result.currency).to.equal(bidResponses.banner.currency); expect(result.netRevenue).to.equal(bidResponses.banner.netRevenue); expect(result.ttl).to.equal(bidResponses.banner.ttl); - expect(result.referrer).to.equal(bidResponses.banner.referrer); expect(result.ad).to.equal(bidResponses.banner.ad); }); - it('handles native responses', () => { + it('handles native responses', function () { const result = spec.interpretResponse({body: serverResponse.native}, bidRequests.native)[0]; expect(result.requestId).to.equal(bidResponses.native.requestId); expect(result.width).to.equal(bidResponses.native.width); @@ -365,7 +387,6 @@ describe('AdgenerationAdapter', () => { expect(result.currency).to.equal(bidResponses.native.currency); expect(result.netRevenue).to.equal(bidResponses.native.netRevenue); expect(result.ttl).to.equal(bidResponses.native.ttl); - expect(result.referrer).to.equal(bidResponses.native.referrer); expect(result.native.title).to.equal(bidResponses.native.native.title); expect(result.native.image.url).to.equal(bidResponses.native.native.image.url); expect(result.native.image.height).to.equal(bidResponses.native.native.image.height); @@ -376,6 +397,7 @@ describe('AdgenerationAdapter', () => { expect(result.native.sponsoredBy).to.equal(bidResponses.native.native.sponsoredBy); expect(result.native.body).to.equal(bidResponses.native.native.body); expect(result.native.cta).to.equal(bidResponses.native.native.cta); + expect(decodeURIComponent(result.native.privacyLink)).to.equal(bidResponses.native.native.privacyLink); expect(result.native.clickUrl).to.equal(bidResponses.native.native.clickUrl); expect(result.native.impressionTrackers[0]).to.equal(bidResponses.native.native.impressionTrackers[0]); expect(result.native.clickTrackers[0]).to.equal(bidResponses.native.native.clickTrackers[0]); diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js new file mode 100644 index 00000000000..348fb772319 --- /dev/null +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -0,0 +1,300 @@ +import {expect} from 'chai'; +import {spec} from 'modules/adheseBidAdapter'; + +const BID_ID = 456; +const TTL = 360; +const NET_REVENUE = true; + +let minimalBid = function() { + return { + 'bidId': BID_ID, + 'bidder': 'adhese', + 'params': { + account: 'demo', + location: '_main_page_', + format: 'leaderboard' + } + } +}; + +let bidWithParams = function(data) { + let bid = minimalBid(); + bid.params.data = data; + return bid; +}; + +describe('AdheseAdapter', function () { + describe('getUserSyncs', function () { + const serverResponses = [{ + account: 'demo' + }]; + const gdprConsent = { + gdprApplies: true, + consentString: 'CONSENT_STRING' + }; + it('should return empty when iframe disallowed', function () { + expect(spec.getUserSyncs({ iframeEnabled: false }, serverResponses, gdprConsent)).to.be.empty; + }); + it('should return empty when no serverResponses present', function () { + expect(spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent)).to.be.empty; + }); + it('should return empty when no account info present in the response', function () { + expect(spec.getUserSyncs({ iframeEnabled: true }, [{}], gdprConsent)).to.be.empty; + }); + it('should return usersync url when iframe allowed', function () { + expect(spec.getUserSyncs({ iframeEnabled: true }, serverResponses, gdprConsent)).to.deep.equal([{ type: 'iframe', url: 'https://user-sync.adhese.com/iframe/user_sync.html?account=demo&gdpr=1&consentString=CONSENT_STRING' }]); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(minimalBid())).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, minimalBid()); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'CONSENT_STRING' + }, + refererInfo: { + referer: 'http://prebid.org/dev-docs/subjects?_d=1' + } + }; + + it('should include all extra bid params', function () { + let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }) ], bidderRequest); + + expect(req.url).to.contain('/sl_main_page_-leaderboard/ag25'); + }); + + it('should include duplicate bid params once', function () { + let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }), bidWithParams({ 'ag': '25', 'ci': 'gent' }) ], bidderRequest); + + expect(req.url).to.contain('/sl_main_page_-leaderboard/ag25/cigent'); + }); + + it('should split multiple target values', function () { + let req = spec.buildRequests([ bidWithParams({ 'ci': 'london' }), bidWithParams({ 'ci': 'gent' }) ], bidderRequest); + + expect(req.url).to.contain('/sl_main_page_-leaderboard/cilondon;gent'); + }); + + it('should include gdpr consent param', function () { + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(req.url).to.contain('/xtCONSENT_STRING'); + }); + + it('should include referer param in base64url format', function () { + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(req.url).to.contain('/xfaHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ'); + }); + + it('should include bids', function () { + let bid = minimalBid(); + let req = spec.buildRequests([ bid ], bidderRequest); + + expect(req.bids).to.deep.equal([ bid ]); + }); + }); + + describe('interpretResponse', () => { + let bidRequest = { + bids: [ minimalBid() ] + }; + + it('should get correct ssp banner response', () => { + let sspBannerResponse = { + body: [ + { + origin: 'APPNEXUS', + originInstance: '', + ext: 'js', + slotName: '_main_page_-leaderboard', + adType: 'leaderboard', + originData: { + seatbid: [{ + bid: [{ + crid: '60613369', + dealid: null + }], + seat: '958' + }] + }, + width: '728', + height: '90', + body: '
    ', + tracker: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', + impressionCounter: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', + extension: {'prebid': {'cpm': {'amount': '1.000000', 'currency': 'USD'}}} + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + ad: '
    ', + cpm: 1, + currency: 'USD', + creativeId: '60613369', + dealId: '', + width: 728, + height: 90, + mediaType: 'banner', + netRevenue: NET_REVENUE, + ttl: TTL, + }]; + expect(spec.interpretResponse(sspBannerResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct ssp video response', () => { + let sspVideoResponse = { + body: [ + { + origin: 'RUBICON', + ext: 'js', + slotName: '_main_page_-leaderboard', + adType: 'leaderboard', + width: '640', + height: '350', + body: '', + extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}} + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastXml: '', + cpm: 2.1, + currency: 'USD', + creativeId: 'RUBICON', + dealId: '', + width: 640, + height: 350, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + }]; + expect(spec.interpretResponse(sspVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct Adhese banner response', () => { + const adheseBannerResponse = { + body: [ + { + adType: 'largeleaderboard', // it can differ from the requested slot + adFormat: 'largeleaderboard', + timeStamp: '1544009030000', + orderId: '22051', + adspaceId: '162363', + body: '', + tag: '', + tracker: 'https://hosts-demo.adhese.com/track/tracker', + altText: '', + height: '150', + width: '840', + tagUrl: 'https://pool-demo.adhese.com/pool/lib/90511.js', + libId: '90511', + id: '742898', + advertiserId: '2081', + ext: 'js', + url: 'https://hosts-demo.adhese.com/raylene/url', + clickTag: 'https://hosts-demo.adhese.com/raylene/clickTag', + poolPath: 'https://hosts-demo.adhese.com/pool/lib/', + orderName: 'Luminus boiler comodity-Pareto -201812', + creativeName: 'nl_demo _network_ron_dlbd_840x150_fix_dir_asv_std_dis_brd_nrt_na_red', + slotName: '_main_page_-leaderboard', + slotID: '29306', + impressionCounter: 'https://hosts-demo.adhese.com/track/742898', + origin: 'JERLICIA', + originData: {}, + auctionable: true, + extension: { + prebid: { + cpm: { + amount: '5.96', + currency: 'USD' + } + } + } + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + ad: '', + cpm: 5.96, + currency: 'USD', + creativeId: '742898', + dealId: '22051', + width: 840, + height: 150, + mediaType: 'banner', + netRevenue: NET_REVENUE, + ttl: TTL, + }]; + expect(spec.interpretResponse(adheseBannerResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct Adhese video response', () => { + const adheseVideoResponse = { + body: [ + { + adType: 'preroll', + adFormat: '', + orderId: '22248', + adspaceId: '164196', + body: '', + height: '360', + width: '640', + tag: "", + libId: '89860', + id: '742470', + advertiserId: '2263', + ext: 'advar', + orderName: 'Smartphoto EOY-20181112', + creativeName: 'PREROLL', + slotName: '_main_page_-leaderboard', + slotID: '41711', + impressionCounter: 'https://hosts-demo.adhese.com/track/742898', + origin: 'JERLICIA', + originData: {}, + auctionable: true + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastXml: '', + cpm: 0, + currency: 'USD', + creativeId: '742470', + dealId: '22248', + width: 640, + height: 360, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + }]; + expect(spec.interpretResponse(adheseVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should return no bids for empty adserver response', () => { + let adserverResponse = { body: [] }; + expect(spec.interpretResponse(adserverResponse, bidRequest)).to.be.empty; + }); + }); +}); diff --git a/test/spec/modules/adkernelAdnAnalyticsAdapter_spec.js b/test/spec/modules/adkernelAdnAnalyticsAdapter_spec.js index f5d1a5d02f1..26fd13afd1f 100644 --- a/test/spec/modules/adkernelAdnAnalyticsAdapter_spec.js +++ b/test/spec/modules/adkernelAdnAnalyticsAdapter_spec.js @@ -1,7 +1,6 @@ import analyticsAdapter, {ExpiringQueue, getUmtSource, storage} from 'modules/adkernelAdnAnalyticsAdapter'; import {expect} from 'chai'; -import adaptermanager from 'src/adaptermanager'; -import * as ajax from 'src/ajax'; +import adapterManager from 'src/adapterManager'; import CONSTANTS from 'src/constants.json'; const events = require('../../../src/events'); @@ -33,81 +32,81 @@ const CAMPAIGN = { c5: '5' }; -describe('', () => { +describe('', function () { let sandbox; - before(() => { + before(function () { sandbox = sinon.sandbox.create(); }); - after(() => { + after(function () { sandbox.restore(); analyticsAdapter.disableAnalytics(); }); - describe('UTM source parser', () => { + describe('UTM source parser', function () { let stubSetItem; let stubGetItem; - before(() => { + before(function () { stubSetItem = sandbox.stub(storage, 'setItem'); stubGetItem = sandbox.stub(storage, 'getItem'); }); - afterEach(() => { + afterEach(function () { sandbox.reset(); }); - it('should parse first direct visit as (direct)', () => { + it('should parse first direct visit as (direct)', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); stubSetItem.returns(undefined); let source = getUmtSource('http://example.com'); expect(source).to.be.eql(DIRECT); }); - it('should respect past campaign visits before direct', () => { + it('should respect past campaign visits before direct', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(JSON.stringify(CAMPAIGN)); stubSetItem.returns(undefined); let source = getUmtSource('http://example.com'); expect(source).to.be.eql(CAMPAIGN); }); - it('should parse visit from google as organic', () => { + it('should parse visit from google as organic', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); stubSetItem.returns(undefined); let source = getUmtSource('http://example.com', 'https://www.google.com/search?q=pikachu'); expect(source).to.be.eql(GOOGLE_ORGANIC); }); - it('should respect previous campaign visit before organic', () => { + it('should respect previous campaign visit before organic', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(JSON.stringify(CAMPAIGN)); stubSetItem.returns(undefined); let source = getUmtSource('http://example.com', 'https://www.google.com/search?q=pikachu'); expect(source).to.be.eql(CAMPAIGN); }); - it('should parse referral visit', () => { + it('should parse referral visit', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); stubSetItem.returns(undefined); let source = getUmtSource('http://example.com', 'http://lander.com/lander.html'); expect(source).to.be.eql(REFERRER); }); - it('should respect previous campaign visit before referral', () => { + it('should respect previous campaign visit before referral', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(JSON.stringify(CAMPAIGN)); stubSetItem.returns(undefined); let source = getUmtSource('http://example.com', 'https://www.google.com/search?q=pikachu'); expect(source).to.be.eql(CAMPAIGN); }); - it('should parse referral visit from same domain as direct', () => { + it('should parse referral visit from same domain as direct', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); stubSetItem.returns(undefined); let source = getUmtSource('http://lander.com/news.html', 'http://lander.com/lander.html'); expect(source).to.be.eql(DIRECT); }); - it('should parse campaign visit', () => { + it('should parse campaign visit', function () { stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); stubSetItem.returns(undefined); let source = getUmtSource('http://lander.com/index.html?utm_campaign=new_campaign&utm_source=adkernel&utm_medium=email&utm_c1=1&utm_c2=2&utm_c3=3&utm_c4=4&utm_c5=5'); @@ -115,12 +114,12 @@ describe('', () => { }); }); - describe('ExpiringQueue', () => { + describe('ExpiringQueue', function () { let timer; - before(() => { + before(function () { timer = sandbox.useFakeTimers(0); }); - after(() => { + after(function () { timer.restore(); }); @@ -184,32 +183,32 @@ describe('', () => { size: '300x250' }; - describe('Analytics adapter', () => { + describe('Analytics adapter', function () { let ajaxStub; let timer; - before(() => { - ajaxStub = sandbox.stub(ajax, 'ajax'); + before(function () { + ajaxStub = sandbox.stub(analyticsAdapter, 'ajaxCall'); timer = sandbox.useFakeTimers(0); }); - beforeEach(() => { + beforeEach(function () { sandbox.stub(events, 'getEvents').callsFake(() => { return [] }); }); - afterEach(() => { + afterEach(function () { events.getEvents.restore(); }); - it('should be configurable', () => { - adaptermanager.registerAnalyticsAdapter({ + it('should be configurable', function () { + adapterManager.registerAnalyticsAdapter({ code: 'adkernelAdn', adapter: analyticsAdapter }); - adaptermanager.enableAnalytics({ + adapterManager.enableAnalytics({ provider: 'adkernelAdn', options: { pubId: 777, @@ -221,21 +220,21 @@ describe('', () => { expect(analyticsAdapter.context).to.have.property('pubId', 777); }); - it('should handle auction init event', () => { + it('should handle auction init event', function () { events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {config: {}, timeout: 3000}); const ev = analyticsAdapter.context.queue.peekAll(); expect(ev).to.have.length(1); expect(ev[0]).to.be.eql({event: 'auctionInit'}); }); - it('should handle bid request event', () => { + it('should handle bid request event', function () { events.emit(CONSTANTS.EVENTS.BID_REQUESTED, REQUEST); const ev = analyticsAdapter.context.queue.peekAll(); expect(ev).to.have.length(2); expect(ev[1]).to.be.eql({event: 'bidRequested', adapter: 'adapter', tagid: 'container-1'}); }); - it('should handle bid response event', () => { + it('should handle bid response event', function () { events.emit(CONSTANTS.EVENTS.BID_RESPONSE, RESPONSE); const ev = analyticsAdapter.context.queue.peekAll(); expect(ev).to.have.length(3); @@ -248,21 +247,21 @@ describe('', () => { }); }); - it('should handle auction end event', () => { + it('should handle auction end event', function () { timer.tick(447); events.emit(CONSTANTS.EVENTS.AUCTION_END, RESPONSE); let ev = analyticsAdapter.context.queue.peekAll(); expect(ev).to.have.length(0); expect(ajaxStub.calledOnce).to.be.equal(true); - ev = JSON.parse(ajaxStub.firstCall.args[2]).hb_ev; + ev = JSON.parse(ajaxStub.firstCall.args[0]).hb_ev; expect(ev[3]).to.be.eql({event: 'auctionEnd', time: 0.447}); }); - it('should handle winning bid', () => { + it('should handle winning bid', function () { events.emit(CONSTANTS.EVENTS.BID_WON, RESPONSE); timer.tick(4500); expect(ajaxStub.calledTwice).to.be.equal(true); - let ev = JSON.parse(ajaxStub.secondCall.args[2]).hb_ev; + let ev = JSON.parse(ajaxStub.secondCall.args[0]).hb_ev; expect(ev[0]).to.be.eql({event: 'bidWon', adapter: 'adapter', tagid: 'container-1', val: 0.015}); }); }); diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index a7bd959ee8e..1147520131b 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -1,78 +1,103 @@ import {expect} from 'chai'; import {spec} from 'modules/adkernelAdnBidAdapter'; -import * as utils from 'src/utils'; -describe('AdkernelAdn adapter', () => { +describe('AdkernelAdn adapter', function () { const bid1_pub1 = { bidder: 'adkernelAdn', transactionId: 'transact0', bidderRequestId: 'req0', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_1', params: { - pubId: 1 + pubId: 1, + host: 'tag.adkernel.com' + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 200]] + } }, adUnitCode: 'ad-unit-1', - sizes: [[300, 250], [300, 200]] - }, - bid2_pub1 = { + }, bid2_pub1 = { bidder: 'adkernelAdn', - transactionId: 'transact1', - bidderRequestId: 'req1', + transactionId: 'transact0', + bidderRequestId: 'req0', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_2', params: { pubId: 1 }, adUnitCode: 'ad-unit-2', - sizes: [300, 250] - }, - bid1_pub2 = { + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }, bid1_pub2 = { bidder: 'adkernelAdn', transactionId: 'transact2', bidderRequestId: 'req1', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_3', params: { pubId: 7, host: 'dps-test.com' }, adUnitCode: 'ad-unit-2', - sizes: [[728, 90]] + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + } }, bid_video1 = { bidder: 'adkernelAdn', transactionId: 'transact3', bidderRequestId: 'req1', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_4', - mediaType: 'video', - sizes: [640, 300], - adUnitCode: 'video_wrapper', - params: { - pubId: 7, + mediaTypes: { video: { - mimes: ['video/mp4', 'video/webm', 'video/x-flv'], - api: [1, 2, 3, 4], - protocols: [1, 2, 3, 4, 5, 6] + context: 'instream', + playerSize: [640, 300], + mimes: ['video/mp4', 'video/webm'], + api: [1, 2], + protocols: [5, 6] } + }, + adUnitCode: 'video_wrapper', + params: { + pubId: 7 } }, bid_video2 = { bidder: 'adkernelAdn', transactionId: 'transact3', bidderRequestId: 'req1', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_5', + sizes: [[1920, 1080]], mediaTypes: { video: { playerSize: [1920, 1080], context: 'instream' } }, - adUnitCode: 'video_wrapper2', params: { - pubId: 7, - video: { - mimes: ['video/mp4', 'video/webm', 'video/x-flv'], - api: [1, 2, 3, 4], - protocols: [1, 2, 3, 4, 5, 6] - } + pubId: 7 } + }, bid_multiformat = { + bidder: 'adkernelAdn', + transactionId: 'f82c64b8-c602-42a4-9791-4a268f6559ed', + bidderRequestId: 'req-001', + auctionId: 'auc-001', + bidId: 'Bid_01', + sizes: [[300, 250], [300, 200]], + mediaTypes: { + banner: {sizes: [[300, 250], [300, 200]]}, + video: {context: 'instream', playerSize: [[640, 480]]} + }, + adUnitCode: 'ad-unit-1', + params: {pubId: 7} }; const response = { @@ -104,12 +129,27 @@ describe('AdkernelAdn adapter', () => { syncpages: ['https://dsp.adkernel.com/sync'] }; + const defaultBidderRequest = { + bidderCode: 'adkernelAdn', + bids: [], + auctionStart: 1545836987704, + timeout: 3000, + refererInfo: { + referer: 'https://example.com/index.html', + reachedTop: true, + numIframes: 0, + stack: ['https://example.com/index.html'] + }, + start: 1545836987707 + }; + describe('input parameters validation', () => { it('empty request shouldn\'t generate exception', () => { expect(spec.isBidRequestValid({ bidderCode: 'adkernelAdn' })).to.be.equal(false); }); + it('request without pubid should be ignored', () => { expect(spec.isBidRequestValid({ bidder: 'adkernelAdn', @@ -118,6 +158,7 @@ describe('AdkernelAdn adapter', () => { sizes: [[300, 250]] })).to.be.equal(false); }); + it('request with invalid pubid should be ignored', () => { expect(spec.isBidRequestValid({ bidder: 'adkernelAdn', @@ -128,101 +169,181 @@ describe('AdkernelAdn adapter', () => { sizes: [[300, 250]] })).to.be.equal(false); }); - }); - describe('banner request building', () => { - let pbRequest; - let tagRequest; - - before(() => { - let mock = sinon.stub(utils, 'getTopWindowLocation').callsFake(() => { - return { - protocol: 'https:', - hostname: 'example.com', - host: 'example.com', - pathname: '/index.html', - href: 'https://example.com/index.html' - }; - }); - pbRequest = spec.buildRequests([bid1_pub1])[0]; - tagRequest = JSON.parse(pbRequest.data); - mock.restore(); + it('request with totally invalid host should be ignored', () => { + expect(spec.isBidRequestValid({ + bidder: 'adkernelAdn', + params: { + pubId: 1, + host: 1 + }, + placementCode: 'ad-unit-0', + sizes: [[300, 250]] + })).to.be.equal(false); }); - it('should have request id', () => { + it('valid request should be accepted', () => { + expect(spec.isBidRequestValid({ + bidder: 'adkernelAdn', + params: { + pubId: 1, + host: 'network.com' + }, + placementCode: 'ad-unit-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 200]] + } + } + })).to.be.equal(true); + }); + }); + + function buildRequest(bidRequests, bidderRequestAugments = {}) { + let fullBidderRequest = Object.assign(defaultBidderRequest, bidderRequestAugments); + fullBidderRequest.auctionId = bidRequests[0].auctionId; + fullBidderRequest.transactionId = bidRequests[0].transactionId; + fullBidderRequest.bidderRequestId = bidRequests[0].bidderRequestId; + fullBidderRequest.bids = bidRequests; + let pbRequests = spec.buildRequests(bidRequests, fullBidderRequest); + let tagRequests = pbRequests.map(r => JSON.parse(r.data)); + + return [pbRequests, tagRequests]; + } + + describe('banner request building', function () { + let [_, tagRequests] = buildRequest([bid1_pub1]); + let tagRequest = tagRequests[0]; + + it('should have request id', function () { expect(tagRequest).to.have.property('id'); }); - it('should have transaction id', () => { + + it('should have transaction id', function () { expect(tagRequest).to.have.property('tid'); }); - it('should have sizes', () => { + + it('should have sizes', function () { expect(tagRequest.imp[0].banner).to.have.property('format'); expect(tagRequest.imp[0].banner.format).to.be.eql(['300x250', '300x200']); }); - it('should have impression id', () => { + + it('should have impression id', function () { expect(tagRequest.imp[0]).to.have.property('id', 'bidid_1'); }); - it('should have tagid', () => { + + it('should have tagid', function () { expect(tagRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); }); - it('should create proper site block', () => { + + it('should create proper site block', function () { expect(tagRequest.site).to.have.property('page', 'https://example.com/index.html'); expect(tagRequest.site).to.have.property('secure', 1); }); + + it('should not have user object', function () { + expect(tagRequest).to.not.have.property('user'); + }); + + it('shouldn\'t contain gdpr-related information for default request', function () { + let [_, tagRequests] = buildRequest([bid1_pub1]); + expect(tagRequests[0]).to.not.have.property('user'); + }); + + it('should contain gdpr-related information if consent is configured', function () { + let [_, bidRequests] = buildRequest([bid1_pub1], + {gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}); + expect(bidRequests[0]).to.have.property('user'); + expect(bidRequests[0].user).to.have.property('gdpr', 1); + expect(bidRequests[0].user).to.have.property('consent', 'test-consent-string'); + }); + + it('should\'t contain consent string if gdpr isn\'t applied', function () { + let [_, bidRequests] = buildRequest([bid1_pub1], {gdprConsent: {gdprApplies: false}}); + expect(bidRequests[0]).to.have.property('user'); + expect(bidRequests[0].user).to.have.property('gdpr', 0); + expect(bidRequests[0].user).to.not.have.property('consent'); + }); }); describe('video request building', () => { - let pbRequest = spec.buildRequests([bid_video1, bid_video2])[0]; - let tagRequest = JSON.parse(pbRequest.data); + let [_, tagRequests] = buildRequest([bid_video1, bid_video2]); + let tagRequest = tagRequests[0]; it('should have video object', () => { expect(tagRequest.imp[0]).to.have.property('video'); expect(tagRequest.imp[1]).to.have.property('video'); }); + it('should have tagid', () => { expect(tagRequest.imp[0]).to.have.property('tagid', 'video_wrapper'); expect(tagRequest.imp[1]).to.have.property('tagid', 'video_wrapper2'); }); + it('should have size', () => { expect(tagRequest.imp[0].video).to.have.property('w', 640); expect(tagRequest.imp[0].video).to.have.property('h', 300); expect(tagRequest.imp[1].video).to.have.property('w', 1920); expect(tagRequest.imp[1].video).to.have.property('h', 1080); }); + + it('should have video params', () => { + expect(tagRequest.imp[0].video).to.have.property('mimes'); + expect(tagRequest.imp[0].video.mimes).to.be.eql(['video/mp4', 'video/webm']); + expect(tagRequest.imp[0].video).to.have.property('api'); + expect(tagRequest.imp[0].video.api).to.be.eql([1, 2]); + expect(tagRequest.imp[0].video).to.have.property('protocols'); + expect(tagRequest.imp[0].video.protocols).to.be.eql([5, 6]); + }); + }); + + describe('multiformat request building', function () { + let [_, tagRequests] = buildRequest([bid_multiformat]); + + it('should contain single request', function () { + expect(tagRequests).to.have.length(1); + expect(tagRequests[0].imp).to.have.length(1); + }); + + it('should contain banner-only impression', function () { + expect(tagRequests[0].imp).to.have.length(1); + expect(tagRequests[0].imp[0]).to.have.property('banner'); + expect(tagRequests[0].imp[0]).to.not.have.property('video'); + }); }); - describe('requests routing', () => { - it('should issue a request for each publisher', () => { - let pbRequests = spec.buildRequests([bid1_pub1, bid_video1]); + describe('requests routing', function () { + it('should issue a request for each publisher', function () { + let [pbRequests, tagRequests] = buildRequest([bid1_pub1, bid_video1]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.have.string(`account=${bid1_pub1.params.pubId}`); expect(pbRequests[1].url).to.have.string(`account=${bid1_pub2.params.pubId}`); - let tagRequest1 = JSON.parse(pbRequests[0].data); - let tagRequest2 = JSON.parse(pbRequests[1].data); - expect(tagRequest1.imp).to.have.length(1); - expect(tagRequest2.imp).to.have.length(1); + expect(tagRequests[0].imp).to.have.length(1); + expect(tagRequests[1].imp).to.have.length(1); }); - it('should issue a request for each host', () => { - let pbRequests = spec.buildRequests([bid1_pub1, bid1_pub2]); + + it('should issue a request for each host', function () { + let [pbRequests, tagRequests] = buildRequest([bid1_pub1, bid1_pub2]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.have.string('//tag.adkernel.com/tag'); expect(pbRequests[1].url).to.have.string(`//${bid1_pub2.params.host}/tag`); - let tagRequest1 = JSON.parse(pbRequests[0].data); - let tagRequest2 = JSON.parse(pbRequests[1].data); - expect(tagRequest1.imp).to.have.length(1); - expect(tagRequest2.imp).to.have.length(1); + expect(tagRequests[0].imp).to.have.length(1); + expect(tagRequests[1].imp).to.have.length(1); }); }); - describe('responses processing', () => { + describe('responses processing', function () { let responses; - before(() => { + + before(function () { responses = spec.interpretResponse({body: response}); }); - it('should parse all responses', () => { + + it('should parse all responses', function () { expect(responses).to.have.length(3); }); - it('should return fully-initialized bid-response', () => { + + it('should return fully-initialized bid-response', function () { let resp = responses[0]; expect(resp).to.have.property('bidderCode', 'adkernelAdn'); expect(resp).to.have.property('requestId', '2c5e951baeeadd'); @@ -236,7 +357,8 @@ describe('AdkernelAdn adapter', () => { expect(resp).to.have.property('ad'); expect(resp.ad).to.have.string(''); }); - it('should return fully-initialized video bid-response', () => { + + it('should return fully-initialized video bid-response', function () { let resp = responses[2]; expect(resp).to.have.property('bidderCode', 'adkernelAdn'); expect(resp).to.have.property('requestId', '57d602ad1c9545'); @@ -248,7 +370,8 @@ describe('AdkernelAdn adapter', () => { expect(resp).to.have.property('vastUrl', 'http://vast.com/vast.xml'); expect(resp).to.not.have.property('ad'); }); - it('should perform usersync', () => { + + it('should perform usersync', function () { let syncs = spec.getUserSyncs({iframeEnabled: false}, [{body: response}]); expect(syncs).to.have.length(0); syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: response}]); @@ -256,14 +379,23 @@ describe('AdkernelAdn adapter', () => { expect(syncs[0]).to.have.property('type', 'iframe'); expect(syncs[0]).to.have.property('url', 'https://dsp.adkernel.com/sync'); }); - it('should handle user-sync only response', () => { - let request = spec.buildRequests([bid1_pub1])[0]; - let resp = spec.interpretResponse({body: usersyncOnlyResponse}, request); + + it('should handle user-sync only response', function () { + let [pbRequests, tagRequests] = buildRequest([bid1_pub1]); + let resp = spec.interpretResponse({body: usersyncOnlyResponse}, pbRequests[0]); expect(resp).to.have.length(0); }); - it('shouldn\' fail in empty response', () => { + + it('shouldn\' fail on empty response', function () { let syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: ''}]); expect(syncs).to.have.length(0); }); }); + + describe('adapter configuration', () => { + it('should have aliases', () => { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.be.equal('engagesimply'); + }); + }); }); diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index cef084c7345..1f221cd956e 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -2,56 +2,111 @@ import {expect} from 'chai'; import {spec} from 'modules/adkernelBidAdapter'; import * as utils from 'src/utils'; -describe('Adkernel adapter', () => { +describe('Adkernel adapter', function () { const bid1_zone1 = { bidder: 'adkernel', - bidId: 'Bid_01', params: {zoneId: 1, host: 'rtb.adkernel.com'}, adUnitCode: 'ad-unit-1', - sizes: [[300, 250], [300, 200]] + bidId: 'Bid_01', + bidderRequestId: 'req-001', + auctionId: 'auc-001', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 200]] + } + } }, bid2_zone2 = { bidder: 'adkernel', - bidId: 'Bid_02', params: {zoneId: 2, host: 'rtb.adkernel.com'}, adUnitCode: 'ad-unit-2', - sizes: [[728, 90]] + bidId: 'Bid_02', + bidderRequestId: 'req-001', + auctionId: 'auc-001', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + } }, bid3_host2 = { bidder: 'adkernel', - bidId: 'Bid_02', params: {zoneId: 1, host: 'rtb-private.adkernel.com'}, adUnitCode: 'ad-unit-2', - sizes: [[728, 90]] + bidId: 'Bid_02', + bidderRequestId: 'req-001', + auctionId: 'auc-001', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + } }, bid_without_zone = { bidder: 'adkernel', - bidId: 'Bid_W', params: {host: 'rtb-private.adkernel.com'}, adUnitCode: 'ad-unit-1', - sizes: [[728, 90]] + bidId: 'Bid_W', + bidderRequestId: 'req-002', + auctionId: 'auc-002', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + } }, bid_without_host = { bidder: 'adkernel', - bidId: 'Bid_W', params: {zoneId: 1}, adUnitCode: 'ad-unit-1', - sizes: [[728, 90]] + bidId: 'Bid_W', + bidderRequestId: 'req-002', + auctionId: 'auc-002', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + } }, bid_with_wrong_zoneId = { bidder: 'adkernel', - bidId: 'Bid_02', params: {zoneId: 'wrong id', host: 'rtb.adkernel.com'}, adUnitCode: 'ad-unit-2', - sizes: [[728, 90]] + bidId: 'Bid_02', + bidderRequestId: 'req-002', + auctionId: 'auc-002', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + } }, bid_video = { bidder: 'adkernel', + transactionId: '866394b8-5d37-4d49-803e-f1bdb595f73e', bidId: 'Bid_Video', - sizes: [640, 480], - mediaType: 'video', + bidderRequestId: '18b2a61ea5d9a7', + auctionId: 'de45acf1-9109-4e52-8013-f2b7cf5f6766', + sizes: [[640, 480]], params: { zoneId: 1, host: 'rtb.adkernel.com', + video: {api: [1, 2]} + }, + mediaTypes: { video: { - mimes: ['video/mp4', 'video/webm', 'video/x-flv'] + context: 'instream', + playerSize: [[640, 480]] } }, adUnitCode: 'ad-unit-1' + }, bid_multiformat = { + bidder: 'adkernel', + params: {zoneId: 1, host: 'rtb.adkernel.com'}, + mediaTypes: { + banner: {sizes: [[300, 250], [300, 200]]}, + video: {context: 'instream', playerSize: [[640, 480]]} + }, + adUnitCode: 'ad-unit-1', + transactionId: 'f82c64b8-c602-42a4-9791-4a268f6559ed', + sizes: [[300, 250], [300, 200]], + bidId: 'Bid_01', + bidderRequestId: 'req-001', + auctionId: 'auc-001' }; const bidResponse1 = { @@ -107,119 +162,175 @@ describe('Adkernel adapter', () => { } }; - describe('input parameters validation', () => { - it('empty request shouldn\'t generate exception', () => { + function buildBidderRequest(url = 'https://example.com/index.html', params = {}) { + return Object.assign({}, params, {refererInfo: {referer: url, reachedTop: true}}) + } + const DEFAULT_BIDDER_REQUEST = buildBidderRequest(); + function buildRequest(bidRequests, bidderRequest = DEFAULT_BIDDER_REQUEST, dnt = true) { + let dntmock = sinon.stub(utils, 'getDNT').callsFake(() => dnt); + let pbRequests = spec.buildRequests(bidRequests, bidderRequest); + dntmock.restore(); + let rtbRequests = pbRequests.map(r => JSON.parse(r.data)); + return [pbRequests, rtbRequests]; + } + + describe('input parameters validation', function () { + it('empty request shouldn\'t generate exception', function () { expect(spec.isBidRequestValid({ bidderCode: 'adkernel' })).to.be.equal(false); }); - it('request without zone shouldn\'t issue a request', () => { + it('request without zone shouldn\'t issue a request', function () { expect(spec.isBidRequestValid(bid_without_zone)).to.be.equal(false); }); - it('request without host shouldn\'t issue a request', () => { + it('request without host shouldn\'t issue a request', function () { expect(spec.isBidRequestValid(bid_without_host)).to.be.equal(false); }); - it('empty request shouldn\'t generate exception', () => { + it('empty request shouldn\'t generate exception', function () { expect(spec.isBidRequestValid(bid_with_wrong_zoneId)).to.be.equal(false); }); }); - describe('banner request building', () => { - let bidRequest; - before(() => { - let wmock = sinon.stub(utils, 'getTopWindowLocation').callsFake(() => ({ - protocol: 'https:', - hostname: 'example.com', - host: 'example.com', - pathname: '/index.html', - href: 'https://example.com/index.html' - })); - let dntmock = sinon.stub(utils, 'getDNT').callsFake(() => true); - let request = spec.buildRequests([bid1_zone1])[0]; - bidRequest = JSON.parse(request.data.r); - wmock.restore(); - dntmock.restore(); - }); - - it('should be a first-price auction', () => { + describe('banner request building', function () { + let bidRequest, bidRequests, _; + before(function () { + [_, bidRequests] = buildRequest([bid1_zone1]); + bidRequest = bidRequests[0]; + }); + + it('should be a first-price auction', function () { expect(bidRequest).to.have.property('at', 1); }); - it('should have banner object', () => { + it('should have banner object', function () { expect(bidRequest.imp[0]).to.have.property('banner'); }); - it('should have w/h', () => { + it('should have id', function () { + expect(bidRequest.imp[0]).to.have.property('id'); + expect(bidRequest.imp[0].id).to.be.eql('Bid_01'); + }); + + it('should have w/h', function () { expect(bidRequest.imp[0].banner).to.have.property('format'); expect(bidRequest.imp[0].banner.format).to.be.eql([{w: 300, h: 250}, {w: 300, h: 200}]); }); - it('should respect secure connection', () => { + it('should respect secure connection', function () { expect(bidRequest.imp[0]).to.have.property('secure', 1); }); - it('should have tagid', () => { + it('should have tagid', function () { expect(bidRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); }); - it('should create proper site block', () => { + it('should create proper site block', function () { expect(bidRequest.site).to.have.property('domain', 'example.com'); expect(bidRequest.site).to.have.property('page', 'https://example.com/index.html'); }); - it('should fill device with caller macro', () => { + it('should fill device with caller macro', function () { expect(bidRequest).to.have.property('device'); expect(bidRequest.device).to.have.property('ip', 'caller'); expect(bidRequest.device).to.have.property('ua', 'caller'); expect(bidRequest.device).to.have.property('dnt', 1); }); + + it('shouldn\'t contain gdpr-related information for default request', function () { + let [_, bidRequests] = buildRequest([bid1_zone1]); + expect(bidRequests[0]).to.not.have.property('regs'); + expect(bidRequests[0]).to.not.have.property('user'); + }); + + it('should contain gdpr-related information if consent is configured', function () { + let [_, bidRequests] = buildRequest([bid1_zone1], + buildBidderRequest('http://example.com/index.html', + {gdprConsent: {gdprApplies: true, consentString: 'test-consent-string', vendorData: {}}})); + let bidRequest = bidRequests[0]; + expect(bidRequest).to.have.property('regs'); + expect(bidRequest.regs.ext).to.be.eql({'gdpr': 1}); + expect(bidRequest).to.have.property('user'); + expect(bidRequest.user.ext).to.be.eql({'consent': 'test-consent-string'}); + }); + + it('should\'t contain consent string if gdpr isn\'t applied', function () { + let [_, bidRequests] = buildRequest([bid1_zone1], buildBidderRequest('https://example.com/index.html', {gdprConsent: {gdprApplies: false}})); + let bidRequest = bidRequests[0]; + expect(bidRequest).to.have.property('regs'); + expect(bidRequest.regs.ext).to.be.eql({'gdpr': 0}); + expect(bidRequest).to.not.have.property('user'); + }); + + it('should\'t pass dnt if state is unknown', function () { + let [_, bidRequests] = buildRequest([bid1_zone1], DEFAULT_BIDDER_REQUEST, false); + expect(bidRequests[0].device).to.not.have.property('dnt'); + }); }); - describe('video request building', () => { - let bidRequest; + describe('video request building', function () { + let _, bidRequests; + before(function () { + [_, bidRequests] = buildRequest([bid_video]); + }); - before(() => { - let request = spec.buildRequests([bid_video])[0]; - bidRequest = JSON.parse(request.data.r); + it('should have video object', function () { + expect(bidRequests[0].imp[0]).to.have.property('video'); }); - it('should have video object', () => { - expect(bidRequest.imp[0]).to.have.property('video'); + it('should have h/w', function () { + expect(bidRequests[0].imp[0].video).to.have.property('w', 640); + expect(bidRequests[0].imp[0].video).to.have.property('h', 480); }); - it('should have h/w', () => { - expect(bidRequest.imp[0].video).to.have.property('w', 640); - expect(bidRequest.imp[0].video).to.have.property('h', 480); + it('should have tagid', function () { + expect(bidRequests[0].imp[0]).to.have.property('tagid', 'ad-unit-1'); }); - it('should have tagid', () => { - expect(bidRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); + it('should have openrtb video impression parameters', function() { + expect(bidRequests[0].imp[0].video).to.have.property('api'); + expect(bidRequests[0].imp[0].video.api).to.be.eql([1, 2]); }); }); - describe('requests routing', () => { - it('should issue a request for each host', () => { - let pbRequests = spec.buildRequests([bid1_zone1, bid3_host2]); + describe('multiformat request building', function () { + let _, bidRequests; + before(function () { + [_, bidRequests] = buildRequest([bid_multiformat]); + }); + it('should contain single request', function () { + expect(bidRequests).to.have.length(1); + expect(bidRequests[0].imp).to.have.length(1); + }); + it('should contain banner-only impression', function () { + expect(bidRequests[0].imp).to.have.length(1); + expect(bidRequests[0].imp[0]).to.have.property('banner'); + expect(bidRequests[0].imp[0]).to.not.have.property('video'); + }); + }); + + describe('requests routing', function () { + it('should issue a request for each host', function () { + let [pbRequests, _] = buildRequest([bid1_zone1, bid3_host2]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.have.string(`//${bid1_zone1.params.host}/`); expect(pbRequests[1].url).to.have.string(`//${bid3_host2.params.host}/`); }); - it('should issue a request for each zone', () => { - let pbRequests = spec.buildRequests([bid1_zone1, bid2_zone2]); + it('should issue a request for each zone', function () { + let [pbRequests, _] = buildRequest([bid1_zone1, bid2_zone2]); expect(pbRequests).to.have.length(2); - expect(pbRequests[0].data.zone).to.be.equal(bid1_zone1.params.zoneId); - expect(pbRequests[1].data.zone).to.be.equal(bid2_zone2.params.zoneId); + expect(pbRequests[0].url).to.include(`zone=${bid1_zone1.params.zoneId}`); + expect(pbRequests[1].url).to.include(`zone=${bid2_zone2.params.zoneId}`); }); }); - describe('responses processing', () => { - it('should return fully-initialized banner bid-response', () => { - let request = spec.buildRequests([bid1_zone1])[0]; - let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; + describe('responses processing', function () { + it('should return fully-initialized banner bid-response', function () { + let [pbRequests, _] = buildRequest([bid1_zone1]); + let resp = spec.interpretResponse({body: bidResponse1}, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_01'); expect(resp).to.have.property('cpm', 3.01); expect(resp).to.have.property('width', 300); @@ -232,9 +343,9 @@ describe('Adkernel adapter', () => { expect(resp.ad).to.have.string(''); }); - it('should return fully-initialized video bid-response', () => { - let request = spec.buildRequests([bid_video])[0]; - let resp = spec.interpretResponse({body: videoBidResponse}, request)[0]; + it('should return fully-initialized video bid-response', function () { + let [pbRequests, _] = buildRequest([bid_video]); + let resp = spec.interpretResponse({body: videoBidResponse}, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_Video'); expect(resp.mediaType).to.equal('video'); expect(resp.cpm).to.equal(0.00145); @@ -243,20 +354,20 @@ describe('Adkernel adapter', () => { expect(resp.height).to.equal(480); }); - it('should add nurl as pixel for banner response', () => { - let request = spec.buildRequests([bid1_zone1])[0]; - let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; + it('should add nurl as pixel for banner response', function () { + let [pbRequests, _] = buildRequest([bid1_zone1]); + let resp = spec.interpretResponse({body: bidResponse1}, pbRequests[0])[0]; let expectedNurl = bidResponse1.seatbid[0].bid[0].nurl + '&px=1'; expect(resp.ad).to.have.string(expectedNurl); }); - it('should handle bidresponse with user-sync only', () => { - let request = spec.buildRequests([bid1_zone1])[0]; - let resp = spec.interpretResponse({body: usersyncOnlyResponse}, request); + it('should handle bidresponse with user-sync only', function () { + let [pbRequests, _] = buildRequest([bid1_zone1]); + let resp = spec.interpretResponse({body: usersyncOnlyResponse}, pbRequests[0]); expect(resp).to.have.length(0); }); - it('should perform usersync', () => { + it('should perform usersync', function () { let syncs = spec.getUserSyncs({iframeEnabled: false}, [{body: bidResponse1}]); expect(syncs).to.have.length(0); syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: bidResponse1}]); @@ -265,4 +376,11 @@ describe('Adkernel adapter', () => { expect(syncs[0]).to.have.property('url', 'http://adk.sync.com/sync'); }); }); + + describe('adapter configuration', () => { + it('should have aliases', () => { + expect(spec.aliases).to.have.lengthOf(4); + expect(spec.aliases).to.be.eql(['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia']); + }); + }); }); diff --git a/test/spec/modules/adliveBidAdapter_spec.js b/test/spec/modules/adliveBidAdapter_spec.js new file mode 100644 index 00000000000..0048fc028b8 --- /dev/null +++ b/test/spec/modules/adliveBidAdapter_spec.js @@ -0,0 +1,78 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adliveBidAdapter'; + +describe('adliveBidAdapterTests', function() { + let bidRequestData = { + bids: [ + { + bidId: 'transaction_1234', + bidder: 'adlive', + params: { + hashes: ['1e100887dd614b0909bf6c49ba7f69fdd1360437'] + }, + sizes: [[300, 250]] + } + ] + }; + let request = []; + + it('validate_pub_params', function() { + expect( + spec.isBidRequestValid({ + bidder: 'adlive', + params: { + hashes: ['1e100887dd614b0909bf6c49ba7f69fdd1360437'] + } + }) + ).to.equal(true); + }); + + it('validate_generated_params', function() { + request = spec.buildRequests(bidRequestData.bids); + let req_data = JSON.parse(request[0].data); + + expect(req_data.transaction_id).to.equal('transaction_1234'); + }); + + it('validate_response_params', function() { + let serverResponse = { + body: [ + { + hash: '1e100887dd614b0909bf6c49ba7f69fdd1360437', + content: 'Ad html', + price: 1.12, + size: [300, 250], + is_passback: 0 + } + ] + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData.bids[0]); + expect(bids).to.have.lengthOf(1); + + let bid = bids[0]; + + expect(bid.creativeId).to.equal('1e100887dd614b0909bf6c49ba7f69fdd1360437'); + expect(bid.ad).to.equal('Ad html'); + expect(bid.cpm).to.equal(1.12); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.currency).to.equal('USD'); + }); + + it('validate_response_params_with passback', function() { + let serverResponse = { + body: [ + { + hash: '1e100887dd614b0909bf6c49ba7f69fdd1360437', + content: 'Ad html passback', + size: [300, 250], + is_passback: 1 + } + ] + }; + let bids = spec.interpretResponse(serverResponse, bidRequestData.bids[0]); + + expect(bids).to.have.lengthOf(0); + }); +}); diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js new file mode 100644 index 00000000000..37a097427d5 --- /dev/null +++ b/test/spec/modules/admanBidAdapter_spec.js @@ -0,0 +1,215 @@ +import {expect} from 'chai'; +import {spec} from 'modules/admanBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//bidtor.admanmedia.com/prebid'; +const BANNER = '"'; +const VAST = ''; +const USER_SYNC_IFRAME_URL = '//cs.admanmedia.com/sync_tag/html'; + +describe('admanBidAdapter', function() { + const adapter = newBidder(spec); + + describe('inherited functions', function() { + it('exists and is a function', function() { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function() { + let bid = { + 'bidder': 'adman', + 'params': { + 'id': '1234asdf' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee' + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when id is not valid (not string)', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'id': 1234 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function() { + let bid = Object.assign({}, bid); + delete bid.params; + + bid.params = {}; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + let bidRequests = [ + { + 'bidder': 'adman', + 'bidId': '51ef8751f9aead', + 'params': { + 'id': '1234asdf' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[320, 50], [300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1 + } + ]; + + it('sends a valid bid request to ENDPOINT via POST', function() { + const request = spec.buildRequests(bidRequests, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + } + }); + + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + + const payload = JSON.parse(request.data); + expect(payload.gdpr).to.exist; + + expect(payload.bids).to.exist.and.to.be.an('array').and.to.have.lengthOf(1); + expect(payload.referer).to.exist; + + const bid = payload.bids[0]; + expect(bid).to.exist; + expect(bid.params).to.exist; + expect(bid.params.id).to.exist; + expect(bid.params.bidId).to.exist; + expect(bid.sizes).to.exist.and.to.be.an('array').and.to.have.lengthOf(3); + bid.sizes.forEach(size => { + expect(size).to.be.an('array').and.to.have.lengthOf(2); + expect(size[0]).to.be.a('number'); + expect(size[1]).to.be.a('number'); + }) + }); + + it('should send GDPR to endpoint and honor gdprApplies value', function() { + let consentString = 'bogusConsent'; + let bidderRequest = { + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.gdpr).to.exist; + expect(payload.gdpr.consent).to.equal(consentString); + expect(payload.gdpr.applies).to.equal(true); + + let bidderRequest2 = { + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': false + } + }; + + const request2 = spec.buildRequests(bidRequests, bidderRequest2); + const payload2 = JSON.parse(request2.data); + + expect(payload2.gdpr).to.exist; + expect(payload2.gdpr.consent).to.equal(consentString); + expect(payload2.gdpr.applies).to.equal(false); + }); + }); + + describe('interpretResponse', function() { + let bids = { + 'body': { + 'bids': [{ + 'ad': BANNER, + 'height': 250, + 'cpm': 0.5, + 'currency': 'USD', + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 3599, + 'width': 300, + 'creativeId': 'er2ee' + }, + { + 'vastXml': VAST, + 'cpm': 0.5, + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db95', + 'ttl': 3599, + 'width': 300, + 'creativeId': 'er2ef' + }] + } + }; + + it('should get correct bid response', function() { + let expectedResponse = [{ + 'ad': BANNER, + 'cpm': 0.5, + 'creativeId': 'er2ee', + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 3599, + 'width': 300, + }, + { + 'vastXml': VAST, + 'cpm': 0.5, + 'creativeId': 'er2ef', + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db95', + 'ttl': 3599, + 'width': 300, + }]; + // los bids vienen formateados de server + let result = spec.interpretResponse(bids); + + expect(result[0]).to.deep.equal(expectedResponse[0]); + expect(result[1]).to.deep.equal(expectedResponse[1]); + // expect(Object.keys(result[1])).to.deep.equal(Object.keys(bids[1])); + }); + + it('handles nobid responses', function() { + let bids = { + 'body': { + 'bids': [] + } + }; + + let result = spec.interpretResponse(bids); + expect(result.length).to.equal(0); + }); + }); + describe('getUserSyncs', () => { + it('should get correct user sync iframe url', function() { + expect(spec.getUserSyncs({ + iframeEnabled: true + }, [{}])).to.deep.equal([{ + type: 'iframe', + url: USER_SYNC_IFRAME_URL + }]); + }); + }); +}); diff --git a/test/spec/modules/admediaBidAdapter_spec.js b/test/spec/modules/admediaBidAdapter_spec.js new file mode 100644 index 00000000000..2e14eee938c --- /dev/null +++ b/test/spec/modules/admediaBidAdapter_spec.js @@ -0,0 +1,138 @@ +import { expect } from 'chai'; +import { spec } from 'modules/admediaBidAdapter'; + +describe('admediaAdapterTests', function () { + describe('bidRequestValidity', function () { + it('bidRequest with aid', function () { + expect(spec.isBidRequestValid({ + bidder: 'admedia', + params: { + aid: 86858, + } + })).to.equal(true); + }); + + it('bidRequest without aid', function () { + expect(spec.isBidRequestValid({ + bidder: 'a4g', + params: { + key: 86858 + } + })).to.equal(false); + }); + }); + + describe('bidRequest', function () { + const validBidRequests = [{ + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'auctionId': 'e3010a3c-5b95-4475-9ba2-1b004c737c30', + 'bidId': '2758de47c84ab58', + 'bidRequestsCount': 1, + 'bidder': 'admedia', + 'bidderRequestId': '1033407c6af0c7', + 'params': { + 'aid': 86858, + }, + 'sizes': [[300, 250], [300, 600]], + 'transactionId': '5851b2cf-ee2d-4022-abd2-d581ef01604e' + }, { + 'adUnitCode': 'div-gpt-ad-1460505748561-1', + 'auctionId': 'e3010a3c-5b95-4475-9ba2-1b004c737c30', + 'bidId': '3d2aaa400371fa', + 'bidRequestsCount': 1, + 'bidder': 'admedia', + 'bidderRequestId': '1033407c6af0c7', + 'params': { + 'aid': 84977, + }, + 'sizes': [[728, 90]], + 'transactionId': 'f8b5247e-7715-4e60-9d51-33153e78c190' + }]; + + const bidderRequest = { + 'auctionId': 'e3010a3c-5b95-4475-9ba2-1b004c737c30', + 'bidderCode': 'admedia', + 'bidderRequestId': '1033407c6af0c7', + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'http://test.com/index.html?pbjs_debug=true' + } + + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + + it('bidRequest method', function () { + expect(request.method).to.equal('POST'); + }); + + it('bidRequest url', function () { + expect(request.url).to.equal('//prebid.admedia.com/bidder/'); + }); + + it('bidRequest data', function () { + const data = JSON.parse(request.data); + expect(decodeURIComponent(data.referer)).to.be.eql(bidderRequest.refererInfo.referer); + expect(data.tags).to.be.an('array'); + expect(data.tags[0].aid).to.be.eql(validBidRequests[0].params.aid); + expect(data.tags[0].id).to.be.eql(validBidRequests[0].bidId); + expect(data.tags[0].sizes).to.be.eql(validBidRequests[0].sizes); + expect(data.tags[1].aid).to.be.eql(validBidRequests[1].params.aid); + expect(data.tags[1].id).to.be.eql(validBidRequests[1].bidId); + expect(data.tags[1].sizes).to.be.eql(validBidRequests[1].sizes); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + body: { + tags: [ + { + ad: '', + cpm: 0.9, + height: 250, + id: '5582180864bc41', + width: 300, + }, + { + error: 'Error message', + id: '6dc6ee4e157749' + }, + { + ad: '', + cpm: 0, + height: 728, + id: '5762180864bc41', + width: 90, + } + ] + }, + headers: {} + }; + + const bidRequest = {}; + + const result = spec.interpretResponse(serverResponse, bidRequest); + + it('Should return an empty array if empty or no tags in response', function () { + expect(spec.interpretResponse({body: ''}, {}).length).to.equal(0); + }); + + it('Should have only one bid', function () { + expect(result.length).to.equal(1); + }); + + it('Should have required keys', function () { + expect(result[0].requestId).to.be.eql(serverResponse.body.tags[0].id); + expect(result[0].cpm).to.be.eql(serverResponse.body.tags[0].cpm); + expect(result[0].width).to.be.eql(serverResponse.body.tags[0].width); + expect(result[0].height).to.be.eql(serverResponse.body.tags[0].height); + expect(result[0].creativeId).to.be.eql(serverResponse.body.tags[0].id); + expect(result[0].dealId).to.be.eql(serverResponse.body.tags[0].id); + expect(result[0].netRevenue).to.be.eql(true); + expect(result[0].ttl).to.be.eql(120); + expect(result[0].ad).to.be.eql(serverResponse.body.tags[0].ad); + }) + }); +}); diff --git a/test/spec/modules/admixerBidAdapter_spec.js b/test/spec/modules/admixerBidAdapter_spec.js index 13312e3d24e..54d0dbee6e4 100644 --- a/test/spec/modules/admixerBidAdapter_spec.js +++ b/test/spec/modules/admixerBidAdapter_spec.js @@ -6,16 +6,16 @@ const BIDDER_CODE = 'admixer'; const ENDPOINT_URL = '//inv-nets.admixer.net/prebid.1.0.aspx'; const ZONE_ID = '2eb6bd58-865c-47ce-af7f-a918108c3fd2'; -describe('AdmixerAdapter', () => { +describe('AdmixerAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.be.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': BIDDER_CODE, 'params': { @@ -28,11 +28,11 @@ describe('AdmixerAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -42,7 +42,7 @@ describe('AdmixerAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': BIDDER_CODE, @@ -57,21 +57,21 @@ describe('AdmixerAdapter', () => { } ]; - it('should add referrer and imp to be equal bidRequest', () => { + it('should add referrer and imp to be equal bidRequest', function () { const request = spec.buildRequests(bidRequests); const payload = JSON.parse(request.data.substr(5)); expect(payload.referrer).to.not.be.undefined; expect(payload.imps[0]).to.deep.equal(bidRequests[0]); }); - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests); expect(request.url).to.equal(ENDPOINT_URL); expect(request.method).to.equal('GET'); }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let response = { body: [{ 'currency': 'USD', @@ -86,7 +86,7 @@ describe('AdmixerAdapter', () => { }] }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const body = response.body; let expectedResponse = [ { @@ -107,7 +107,7 @@ describe('AdmixerAdapter', () => { expect(result[0]).to.deep.equal(expectedResponse[0]); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = []; let result = spec.interpretResponse(response); diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index 149b9eb4d53..1fd009ce526 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -2,16 +2,16 @@ import { expect } from 'chai'; import { spec } from 'modules/adoceanBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('AdoceanAdapter', () => { +describe('AdoceanAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { const bid = { 'bidder': 'adocean', 'params': { @@ -26,21 +26,22 @@ describe('AdoceanAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { const bid = Object.assign({}, bid); delete bid.params; bid.params = { 'masterId': 0 }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { const bidRequests = [ { 'bidder': 'adocean', @@ -54,29 +55,62 @@ describe('AdoceanAdapter', () => { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'adocean', + 'params': { + 'masterId': 'tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7', + 'slaveId': 'adoceanmyaozpniqismex', + 'emiter': 'myao.adocean.pl' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1f', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', } ]; - it('should add bidIdMap with slaveId => bidId mapping', () => { - const request = spec.buildRequests(bidRequests)[0]; - expect(request.bidIdMap).to.exists; - const bidIdMap = request.bidIdMap; - expect(bidIdMap[bidRequests[0].params.slaveId]).to.equal(bidRequests[0].bidId); + const bidderRequest = { + gdprConsent: { + consentString: 'BOQHk-4OSlWKFBoABBPLBd-AAAAgWAHAACAAsAPQBSACmgFTAOkA', + gdprApplies: true + } + }; + + it('should send two requests if slave is duplicated', () => { + const nrOfRequests = spec.buildRequests(bidRequests, bidderRequest).length; + expect(nrOfRequests).to.equal(2); }); - it('sends bid request to url via GET', () => { - const request = spec.buildRequests(bidRequests)[0]; + it('should add bidIdMap with correct slaveId => bidId mapping', () => { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (let i = 0; i < bidRequests.length; i++) { + expect(requests[i]).to.exist; + expect(requests[i].bidIdMap).to.exist; + expect(requests[i].bidIdMap[bidRequests[i].params.slaveId]).to.equal(bidRequests[i].bidId); + } + }); + + it('sends bid request to url via GET', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.method).to.equal('GET'); expect(request.url).to.match(new RegExp(`^https://${bidRequests[0].params.emiter}/ad.json`)); }); - it('should attach id to url', () => { - const request = spec.buildRequests(bidRequests)[0]; + it('should attach id to url', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.url).to.include('id=' + bidRequests[0].params.masterId); }); + + it('should attach consent information to url', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.url).to.include('gdpr=1'); + expect(request.url).to.include('gdpr_consent=' + bidderRequest.gdprConsent.consentString); + }); }) - describe('interpretResponse', () => { + describe('interpretResponse', function () { const response = { 'body': [ { @@ -115,7 +149,7 @@ describe('AdoceanAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const expectedResponse = [ { 'requestId': '30b31c1838de1e', @@ -143,7 +177,7 @@ describe('AdoceanAdapter', () => { }); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { response.body = [ { 'id': 'adoceanmyaolafpjwftbz', diff --git a/test/spec/modules/adomikAnalyticsAdapter_spec.js b/test/spec/modules/adomikAnalyticsAdapter_spec.js index 59e6dadec96..16480d746b7 100644 --- a/test/spec/modules/adomikAnalyticsAdapter_spec.js +++ b/test/spec/modules/adomikAnalyticsAdapter_spec.js @@ -1,7 +1,7 @@ import adomikAnalytics from 'modules/adomikAnalyticsAdapter'; import {expect} from 'chai'; let events = require('src/events'); -let adaptermanager = require('src/adaptermanager'); +let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); describe('Adomik Prebid Analytic', function () { @@ -9,26 +9,26 @@ describe('Adomik Prebid Analytic', function () { let sendWonEventStub; describe('enableAnalytics', function () { - beforeEach(() => { + beforeEach(function () { sinon.spy(adomikAnalytics, 'track'); sendEventStub = sinon.stub(adomikAnalytics, 'sendTypedEvent'); sendWonEventStub = sinon.stub(adomikAnalytics, 'sendWonEvent'); sinon.stub(events, 'getEvents').returns([]); }); - afterEach(() => { + afterEach(function () { adomikAnalytics.track.restore(); sendEventStub.restore(); sendWonEventStub.restore(); events.getEvents.restore(); }); - after(() => { + after(function () { adomikAnalytics.disableAnalytics(); }); it('should catch all events', function (done) { - adaptermanager.registerAnalyticsAdapter({ + adapterManager.registerAnalyticsAdapter({ code: 'adomik', adapter: adomikAnalytics }); @@ -55,7 +55,7 @@ describe('Adomik Prebid Analytic', function () { } // Step 1: Initialize adapter - adaptermanager.enableAnalytics({ + adapterManager.enableAnalytics({ provider: 'adomik', options: initOptions }); diff --git a/test/spec/modules/adpod_spec.js b/test/spec/modules/adpod_spec.js new file mode 100644 index 00000000000..8b7701c5631 --- /dev/null +++ b/test/spec/modules/adpod_spec.js @@ -0,0 +1,1232 @@ +import * as utils from 'src/utils'; +import { config } from 'src/config'; +import * as videoCache from 'src/videoCache'; +import * as auction from 'src/auction'; +import { ADPOD } from 'src/mediaTypes'; + +import { callPrebidCacheHook, checkAdUnitSetupHook, checkVideoBidSetupHook, adpodSetConfig, sortByPricePerSecond } from 'modules/adpod'; + +let expect = require('chai').expect; + +describe('adpod.js', function () { + let logErrorStub; + let logWarnStub; + let logInfoStub; + + describe('callPrebidCacheHook', function () { + let callbackResult; + let clock; + let addBidToAuctionStub; + let doCallbacksIfTimedoutStub; + let storeStub; + let afterBidAddedSpy; + let auctionBids = []; + + let callbackFn = function() { + callbackResult = true; + }; + + let auctionInstance = { + getAuctionStatus: function() { + return auction.AUCTION_IN_PROGRESS; + } + } + + const fakeStoreFn = function(bids, callback) { + let payload = []; + bids.forEach(bid => payload.push({uuid: bid.customCacheKey})); + callback(null, payload); + }; + + beforeEach(function() { + callbackResult = null; + afterBidAddedSpy = sinon.spy(); + storeStub = sinon.stub(videoCache, 'store'); + logWarnStub = sinon.stub(utils, 'logWarn'); + logInfoStub = sinon.stub(utils, 'logInfo'); + addBidToAuctionStub = sinon.stub(auction, 'addBidToAuction').callsFake(function (auctionInstance, bid) { + auctionBids.push(bid); + }); + doCallbacksIfTimedoutStub = sinon.stub(auction, 'doCallbacksIfTimedout'); + clock = sinon.useFakeTimers(); + config.setConfig({ + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } + }); + }); + + afterEach(function() { + storeStub.restore(); + logWarnStub.restore(); + logInfoStub.restore(); + addBidToAuctionStub.restore(); + doCallbacksIfTimedoutStub.restore(); + clock.restore(); + config.resetConfig(); + auctionBids = []; + }) + + it('should redirect back to the original function if bid is not an adpod video', function () { + let bid = { + adId: 'testAdId_123', + mediaType: 'video' + }; + + let bidderRequest = { + adUnitCode: 'adUnit_123', + mediaTypes: { + video: { + context: 'outstream' + } + } + } + + callPrebidCacheHook(callbackFn, auctionInstance, bid, function () {}, bidderRequest); + expect(callbackResult).to.equal(true); + }); + + it('should immediately add the adpod bid to auction if adpod.deferCaching in config is true', function() { + config.setConfig({ + adpod: { + deferCaching: true, + brandCategoryExclusion: true + } + }); + + let bidResponse1 = { + adId: 'adId01277', + auctionId: 'no_defer_123', + mediaType: 'video', + cpm: 5, + pbMg: '5.00', + adserverTargeting: { + hb_pb: '5.00' + }, + meta: { + adServerCatId: 'test' + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + + let bidResponse2 = { + adId: 'adId46547', + auctionId: 'no_defer_123', + mediaType: 'video', + cpm: 12, + pbMg: '12.00', + adserverTargeting: { + hb_pb: '12.00' + }, + meta: { + adServerCatId: 'value' + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + + let bidderRequest = { + adUnitCode: 'adpod_1', + auctionId: 'no_defer_123', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 300, + durationRangeSec: [15, 30, 45], + requireExactDuration: false + } + }, + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest); + + // check if bid adsereverTargeting is setup + expect(callbackResult).to.be.null; + expect(storeStub.called).to.equal(false); + expect(afterBidAddedSpy.calledTwice).to.equal(true); + expect(auctionBids.length).to.equal(2); + expect(auctionBids[0].adId).to.equal(bidResponse1.adId); + expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^5\.00_test_15s_.*/); + expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('5.00_test_15s'); + expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[0].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + expect(auctionBids[1].adId).to.equal(bidResponse2.adId); + expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^12\.00_value_15s_.*/); + expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('12.00_value_15s'); + expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[1].adserverTargeting.hb_cache_id).to.equal(auctionBids[0].adserverTargeting.hb_cache_id); + expect(auctionBids[1].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id); + }); + + it('should send prebid cache call once bid queue is full', function () { + storeStub.callsFake(fakeStoreFn); + + config.setConfig({ + adpod: { + bidQueueSizeLimit: 2, + deferCaching: false, + brandCategoryExclusion: true + } + }); + + let bidResponse1 = { + adId: 'adId123', + auctionId: 'full_abc123', + mediaType: 'video', + cpm: 10, + pbMg: '10.00', + adserverTargeting: { + hb_pb: '10.00' + }, + meta: { + adServerCatId: 'airline' + }, + video: { + context: ADPOD, + durationSeconds: 20, + durationBucket: 30 + } + }; + let bidResponse2 = { + adId: 'adId234', + auctionId: 'full_abc123', + mediaType: 'video', + cpm: 15, + pbMg: '15.00', + adserverTargeting: { + hb_pb: '15.00' + }, + meta: { + adServerCatId: 'airline' + }, + video: { + context: ADPOD, + durationSeconds: 25, + durationBucket: 30 + } + }; + let bidderRequest = { + adUnitCode: 'adpod_1', + auctionId: 'full_abc123', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 120, + durationRangeSec: [15, 30], + requireExactDuration: false + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest); + + expect(callbackResult).to.be.null; + expect(afterBidAddedSpy.calledTwice).to.equal(true); + expect(auctionBids.length).to.equal(2); + expect(auctionBids[0].adId).to.equal('adId123'); + expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^10\.00_airline_30s_.*/); + expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('10.00_airline_30s'); + expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[0].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + expect(auctionBids[1].adId).to.equal('adId234'); + expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^15\.00_airline_30s_.*/); + expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_airline_30s'); + expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[1].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + }); + + it('should send prebid cache call after set period of time (even if queue is not full)', function () { + storeStub.callsFake(fakeStoreFn); + + config.setConfig({ + adpod: { + bidQueueSizeLimit: 2, + bidQueueTimeDelay: 30, + deferCaching: false, + brandCategoryExclusion: true + } + }); + + let bidResponse = { + adId: 'adId234', + auctionId: 'timer_abc234', + mediaType: 'video', + cpm: 15, + pbMg: '15.00', + adserverTargeting: { + hb_pb: '15.00' + }, + meta: { + adServerCatId: 'airline' + }, + video: { + context: ADPOD, + durationSeconds: 30, + durationBucket: 30 + } + }; + let bidderRequest = { + adUnitCode: 'adpod_2', + auctionId: 'timer_abc234', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 120, + durationRangeSec: [15, 30], + requireExactDuration: true + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse, afterBidAddedSpy, bidderRequest); + clock.tick(31); + + expect(callbackResult).to.be.null; + expect(afterBidAddedSpy.calledOnce).to.equal(true); + expect(auctionBids.length).to.equal(1); + expect(auctionBids[0].adId).to.equal('adId234'); + expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^15\.00_airline_30s_.*/); + expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_airline_30s'); + expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[0].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + }); + + it('should execute multiple prebid cache calls when number of bids exceeds queue size', function () { + storeStub.callsFake(fakeStoreFn); + + config.setConfig({ + adpod: { + bidQueueSizeLimit: 2, + bidQueueTimeDelay: 30, + deferCaching: false, + brandCategoryExclusion: true + } + }); + + let bidResponse1 = { + adId: 'multi_ad1', + auctionId: 'multi_call_abc345', + mediaType: 'video', + cpm: 15, + pbMg: '15.00', + adserverTargeting: { + hb_pb: '15.00' + }, + meta: { + adServerCatId: 'airline' + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + let bidResponse2 = { + adId: 'multi_ad2', + auctionId: 'multi_call_abc345', + mediaType: 'video', + cpm: 15, + pbMg: '15.00', + adserverTargeting: { + hb_pb: '15.00' + }, + meta: { + adServerCatId: 'news' + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + let bidResponse3 = { + adId: 'multi_ad3', + auctionId: 'multi_call_abc345', + mediaType: 'video', + cpm: 10, + pbMg: '10.00', + adserverTargeting: { + hb_pb: '10.00' + }, + meta: { + adServerCatId: 'sports' + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + + let bidderRequest = { + adUnitCode: 'adpod_3', + auctionId: 'multi_call_abc345', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 45, + durationRangeSec: [15, 30], + requireExactDuration: false + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest); + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse3, afterBidAddedSpy, bidderRequest); + clock.next(); + + expect(callbackResult).to.be.null; + expect(afterBidAddedSpy.calledThrice).to.equal(true); + expect(storeStub.calledTwice).to.equal(true); + expect(auctionBids.length).to.equal(3); + expect(auctionBids[0].adId).to.equal('multi_ad1'); + expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^15\.00_airline_15s_.*/); + expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_airline_15s'); + expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[0].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + expect(auctionBids[1].adId).to.equal('multi_ad2'); + expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^15\.00_news_15s_.*/); + expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_news_15s'); + expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id); + expect(auctionBids[1].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + expect(auctionBids[2].adId).to.equal('multi_ad3'); + expect(auctionBids[2].customCacheKey).to.exist.and.to.match(/^10\.00_sports_15s_.*/); + expect(auctionBids[2].adserverTargeting.hb_pb_cat_dur).to.equal('10.00_sports_15s'); + expect(auctionBids[2].adserverTargeting.hb_cache_id).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id); + expect(auctionBids[2].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + }); + + it('should cache the bids with a shortened custom key when adpod.brandCategoryExclusion is false', function() { + storeStub.callsFake(fakeStoreFn); + + config.setConfig({ + adpod: { + bidQueueSizeLimit: 2, + bidQueueTimeDelay: 30, + deferCaching: false, + brandCategoryExclusion: false + } + }); + + let bidResponse1 = { + adId: 'nocat_ad1', + auctionId: 'no_category_abc345', + mediaType: 'video', + cpm: 10, + pbMg: '10.00', + adserverTargeting: { + hb_pb: '10.00' + }, + meta: { + adServerCatId: undefined + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + let bidResponse2 = { + adId: 'nocat_ad2', + auctionId: 'no_category_abc345', + mediaType: 'video', + cpm: 15, + pbMg: '15.00', + adserverTargeting: { + hb_pb: '15.00' + }, + meta: { + adServerCatId: undefined + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + + let bidderRequest = { + adUnitCode: 'adpod_4', + auctionId: 'no_category_abc345', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 45, + durationRangeSec: [15, 30], + requireExactDuration: false + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest); + + expect(callbackResult).to.be.null; + expect(afterBidAddedSpy.calledTwice).to.equal(true); + expect(storeStub.calledOnce).to.equal(true); + expect(auctionBids.length).to.equal(2); + expect(auctionBids[0].adId).to.equal('nocat_ad1'); + expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^10\.00_15s_.*/); + expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('10.00_15s'); + expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[0].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + expect(auctionBids[1].adId).to.equal('nocat_ad2'); + expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^15\.00_15s_.*/); + expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_15s'); + expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id); + expect(auctionBids[1].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + }); + + it('should not add bid to auction when config adpod.brandCategoryExclusion is true but bid is missing adServerCatId', function() { + storeStub.callsFake(fakeStoreFn); + + config.setConfig({ + adpod: { + bidQueueSizeLimit: 2, + bidQueueTimeDelay: 30, + deferCaching: false, + brandCategoryExclusion: true + } + }); + + let bidResponse1 = { + adId: 'missingCat_ad1', + auctionId: 'missing_category_abc345', + mediaType: 'video', + cpm: 10, + meta: { + adServerCatId: undefined + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + + let bidderRequest = { + adUnitCode: 'adpod_5', + auctionId: 'missing_category_abc345', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 45, + durationRangeSec: [15, 30], + requireExactDuration: false + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + + expect(callbackResult).to.be.null; + expect(afterBidAddedSpy.calledOnce).to.equal(true); + expect(storeStub.called).to.equal(false); + expect(logWarnStub.calledOnce).to.equal(true); + expect(auctionBids.length).to.equal(0); + }); + + it('should not add bid to auction when Prebid Cache detects an existing key', function () { + storeStub.callsFake(function(bids, callback) { + let payload = []; + bids.forEach(bid => payload.push({uuid: bid.customCacheKey})); + + // fake a duplicate bid response from PBC (sets an empty string for the uuid) + payload[1].uuid = ''; + callback(null, payload); + }); + + config.setConfig({ + adpod: { + bidQueueSizeLimit: 2, + deferCaching: false, + brandCategoryExclusion: true + } + }); + + let bidResponse1 = { + adId: 'dup_ad_1', + auctionId: 'duplicate_def123', + mediaType: 'video', + cpm: 5, + pbMg: '5.00', + adserverTargeting: { + hb_pb: '5.00' + }, + meta: { + adServerCatId: 'tech' + }, + video: { + context: ADPOD, + durationSeconds: 45, + durationBucket: 45 + } + }; + let bidResponse2 = { + adId: 'dup_ad_2', + auctionId: 'duplicate_def123', + mediaType: 'video', + cpm: 5, + pbMg: '5.00', + adserverTargeting: { + hb_pb: '5.00' + }, + meta: { + adServerCatId: 'tech' + }, + video: { + context: ADPOD, + durationSeconds: 45, + durationBucket: 45 + } + }; + let bidderRequest = { + adUnitCode: 'adpod_4', + auctionId: 'duplicate_def123', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 120, + durationRangeSec: [15, 30, 45], + requireExactDuration: false + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest); + + expect(callbackResult).to.be.null; + expect(afterBidAddedSpy.calledTwice).to.equal(true); + expect(storeStub.calledOnce).to.equal(true); + expect(logInfoStub.calledOnce).to.equal(true); + expect(auctionBids.length).to.equal(1); + expect(auctionBids[0].adId).to.equal('dup_ad_1'); + expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^5\.00_tech_45s_.*/); + expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('5.00_tech_45s'); + expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist; + expect(auctionBids[0].videoCacheKey).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id) + }); + + it('should not add bids to auction if PBC returns an error', function() { + storeStub.callsFake(function(bids, callback) { + let payload = []; + let errmsg = 'invalid json'; + + callback(errmsg, payload); + }); + + config.setConfig({ + adpod: { + bidQueueSizeLimit: 2, + deferCaching: false, + brandCategoryExclusion: true + } + }); + + let bidResponse1 = { + adId: 'err_ad_1', + auctionId: 'error_xyz123', + mediaType: 'video', + cpm: 5, + meta: { + adServerCatId: 'tech' + }, + video: { + context: ADPOD, + durationSeconds: 30, + durationBucket: 30 + } + }; + let bidResponse2 = { + adId: 'err_ad_2', + auctionId: 'error_xyz123', + mediaType: 'video', + cpm: 5, + meta: { + adServerCatId: 'tech' + }, + video: { + context: ADPOD, + durationSeconds: 30, + durationBucket: 30 + } + }; + let bidderRequest = { + adUnitCode: 'adpod_5', + auctionId: 'error_xyz123', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 120, + durationRangeSec: [15, 30, 45], + requireExactDuration: false + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest); + + expect(doCallbacksIfTimedoutStub.calledTwice).to.equal(true); + expect(logWarnStub.calledOnce).to.equal(true); + expect(auctionBids.length).to.equal(0); + }); + + it('should use bid.adserverTargeting.hb_pb when custom price granularity is configured', function() { + storeStub.callsFake(fakeStoreFn); + + const customConfigObject = { + 'buckets': [{ + 'precision': 2, // default is 2 if omitted - means 2.1234 rounded to 2 decimal places = 2.12 + 'min': 0, + 'max': 5, + 'increment': 0.01 // from $0 to $5, 1-cent increments + }, + { + 'precision': 2, + 'min': 5, + 'max': 8, + 'increment': 0.05 // from $5 to $8, round down to the previous 5-cent increment + }, + { + 'precision': 2, + 'min': 8, + 'max': 40, + 'increment': 0.5 // from $8 to $40, round down to the previous 50-cent increment + }] + }; + config.setConfig({ + priceGranularity: customConfigObject, + adpod: { + brandCategoryExclusion: true + } + }); + + let bidResponse1 = { + adId: 'cat_ad1', + auctionId: 'test_category_abc345', + mediaType: 'video', + cpm: 15, + pbAg: '15.00', + pbCg: '15.00', + pbDg: '15.00', + pbHg: '15.00', + pbLg: '5.00', + pbMg: '15.00', + adserverTargeting: { + hb_pb: '15.00', + }, + meta: { + adServerCatId: 'test' + }, + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + } + }; + + let bidderRequest = { + adUnitCode: 'adpod_5', + auctionId: 'test_category_abc345', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 45, + durationRangeSec: [15, 30], + requireExactDuration: false + } + } + }; + + callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest); + + expect(callbackResult).to.be.null; + expect(afterBidAddedSpy.calledOnce).to.equal(true); + expect(storeStub.called).to.equal(false); + expect(auctionBids.length).to.equal(1); + }); + }); + + describe('checkAdUnitSetupHook', function () { + let results; + let callbackFn = function (adUnits) { + results = adUnits; + }; + + beforeEach(function () { + logWarnStub = sinon.stub(utils, 'logWarn'); + results = null; + }); + + afterEach(function() { + utils.logWarn.restore(); + }); + + it('removes an incorrectly setup adpod adunit - required fields are missing', function() { + let adUnits = [{ + code: 'test1', + mediaTypes: { + video: { + context: ADPOD + } + } + }, { + code: 'test2', + mediaTypes: { + video: { + context: 'instream' + } + } + }]; + + checkAdUnitSetupHook(callbackFn, adUnits); + + expect(results).to.deep.equal([{ + code: 'test2', + mediaTypes: { + video: { + context: 'instream' + } + } + }]); + expect(logWarnStub.calledOnce).to.equal(true); + }); + + it('removes an incorrectly setup adpod adunit - required fields are using invalid values', function() { + let adUnits = [{ + code: 'test1', + mediaTypes: { + video: { + context: ADPOD, + durationRangeSec: [-5, 15, 30, 45], + adPodDurationSec: 300 + } + } + }]; + + checkAdUnitSetupHook(callbackFn, adUnits); + + expect(results).to.deep.equal([]); + expect(logWarnStub.calledOnce).to.equal(true); + + adUnits[0].mediaTypes.video.durationRangeSec = [15, 30, 45]; + adUnits[0].mediaTypes.video.adPodDurationSec = 0; + + checkAdUnitSetupHook(callbackFn, adUnits); + + expect(results).to.deep.equal([]); + expect(logWarnStub.calledTwice).to.equal(true); + }); + + it('removes an incorrectly setup adpod adunit - attempting to use multi-format adUnit', function() { + let adUnits = [{ + code: 'multi_test1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'adpod', + playerSize: [300, 250], + durationRangeSec: [15, 30, 45], + adPodDurationSec: 300 + } + } + }]; + + checkAdUnitSetupHook(callbackFn, adUnits); + + expect(results).to.deep.equal([]); + expect(logWarnStub.calledOnce).to.equal(true); + }); + + it('accepts mixed set of adunits', function() { + let adUnits = [{ + code: 'test3', + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 300], + adPodDurationSec: 360, + durationRangeSec: [15, 30, 45], + requireExactDuration: true + } + } + }, { + code: 'test4', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }]; + + checkAdUnitSetupHook(callbackFn, adUnits); + + expect(results).to.deep.equal(adUnits); + expect(logWarnStub.called).to.equal(false); + }); + }); + + describe('checkVideoBidSetupHook', function () { + let callbackResult; + let bailResult; + const callbackFn = { + call: function(context, bid) { + callbackResult = bid; + }, + bail: function(result) { + bailResult = result; + } + } + const adpodTestBid = { + video: { + context: ADPOD, + durationSeconds: 15, + durationBucket: 15 + }, + meta: { + iabSubCatId: 'testCategory_123' + }, + vastXml: 'test XML here' + }; + const bidderRequestNoExact = { + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 400], + durationRangeSec: [15, 45], + requireExactDuration: false, + adPodDurationSec: 300 + } + } + }; + const bidderRequestWithExact = { + mediaTypes: { + video: { + context: ADPOD, + playerSize: [300, 400], + durationRangeSec: [15, 30, 45, 60], + requireExactDuration: true, + adPodDurationSec: 300 + } + } + }; + + beforeEach(function() { + callbackResult = null; + bailResult = null; + config.setConfig({ + cache: { + url: 'http://test.cache.url/endpoint' + }, + adpod: { + brandCategoryExclusion: true + } + }); + logWarnStub = sinon.stub(utils, 'logWarn'); + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function() { + config.resetConfig(); + logWarnStub.restore(); + logErrorStub.restore(); + }) + + it('redirects to original function for non-adpod type video bids', function() { + let bannerTestBid = { + mediaType: 'video' + }; + checkVideoBidSetupHook(callbackFn, bannerTestBid, {}, {}, 'instream'); + expect(callbackResult).to.deep.equal(bannerTestBid); + expect(bailResult).to.be.null; + expect(logErrorStub.called).to.equal(false); + }); + + it('returns true when adpod bid is properly setup', function() { + config.setConfig({ + cache: { + url: 'http://test.cache.url/endpoint' + }, + adpod: { + brandCategoryExclusion: false + } + }); + + let goodBid = utils.deepClone(adpodTestBid); + goodBid.meta.iabSubCatId = undefined; + checkVideoBidSetupHook(callbackFn, goodBid, bidderRequestNoExact, {}, ADPOD); + expect(callbackResult).to.be.null; + expect(bailResult).to.equal(true); + expect(logErrorStub.called).to.equal(false); + }); + + it('returns true when adpod bid is missing iab category while brandCategoryExclusion in config is false', function() { + let goodBid = utils.deepClone(adpodTestBid); + checkVideoBidSetupHook(callbackFn, goodBid, bidderRequestNoExact, {}, ADPOD); + expect(callbackResult).to.be.null; + expect(bailResult).to.equal(true); + expect(logErrorStub.called).to.equal(false); + }); + + it('returns false when a required property from an adpod bid is missing', function() { + function testInvalidAdpodBid(badTestBid, shouldErrorBeLogged) { + checkVideoBidSetupHook(callbackFn, badTestBid, bidderRequestNoExact, {}, ADPOD); + expect(callbackResult).to.be.null; + expect(bailResult).to.equal(false); + expect(logErrorStub.called).to.equal(shouldErrorBeLogged); + } + + let noCatBid = utils.deepClone(adpodTestBid); + noCatBid.meta.iabSubCatId = undefined; + testInvalidAdpodBid(noCatBid, false); + + let noContextBid = utils.deepClone(adpodTestBid); + delete noContextBid.video.context; + testInvalidAdpodBid(noContextBid, false); + + let wrongContextBid = utils.deepClone(adpodTestBid); + wrongContextBid.video.context = 'instream'; + testInvalidAdpodBid(wrongContextBid, false); + + let noDurationBid = utils.deepClone(adpodTestBid); + delete noDurationBid.video.durationSeconds; + testInvalidAdpodBid(noDurationBid, false); + + config.resetConfig(); + let noCacheUrlBid = utils.deepClone(adpodTestBid); + testInvalidAdpodBid(noCacheUrlBid, true); + }); + + describe('checkBidDuration', function() { + const basicBid = { + video: { + context: ADPOD, + durationSeconds: 30 + }, + meta: { + iabSubCatId: 'testCategory_123' + }, + vastXml: '' + }; + + it('when requireExactDuration is true', function() { + let goodBid = utils.deepClone(basicBid); + checkVideoBidSetupHook(callbackFn, goodBid, bidderRequestWithExact, {}, ADPOD); + + expect(callbackResult).to.be.null; + expect(goodBid.video.durationBucket).to.equal(30); + expect(bailResult).to.equal(true); + expect(logWarnStub.called).to.equal(false); + + let badBid = utils.deepClone(basicBid); + badBid.video.durationSeconds = 14; + checkVideoBidSetupHook(callbackFn, badBid, bidderRequestWithExact, {}, ADPOD); + + expect(callbackResult).to.be.null; + expect(badBid.video.durationBucket).to.be.undefined; + expect(bailResult).to.equal(false); + expect(logWarnStub.calledOnce).to.equal(true); + }); + + it('when requireExactDuration is false and bids are bucketed properly', function() { + function testRoundingForGoodBId(bid, bucketValue) { + checkVideoBidSetupHook(callbackFn, bid, bidderRequestNoExact, {}, ADPOD); + expect(callbackResult).to.be.null; + expect(bid.video.durationBucket).to.equal(bucketValue); + expect(bailResult).to.equal(true); + expect(logWarnStub.called).to.equal(false); + } + + let goodBid45 = utils.deepClone(basicBid); + goodBid45.video.durationSeconds = 45; + testRoundingForGoodBId(goodBid45, 45); + + let goodBid30 = utils.deepClone(basicBid); + goodBid30.video.durationSeconds = 30; + testRoundingForGoodBId(goodBid30, 45); + + let goodBid10 = utils.deepClone(basicBid); + goodBid10.video.durationSeconds = 10; + testRoundingForGoodBId(goodBid10, 15); + + let goodBid16 = utils.deepClone(basicBid); + goodBid16.video.durationSeconds = 16; + testRoundingForGoodBId(goodBid16, 15); + + let goodBid47 = utils.deepClone(basicBid); + goodBid47.video.durationSeconds = 47; + testRoundingForGoodBId(goodBid47, 45); + }); + + it('when requireExactDuration is false and bid duration exceeds listed buckets', function() { + function testRoundingForBadBid(bid) { + checkVideoBidSetupHook(callbackFn, bid, bidderRequestNoExact, {}, ADPOD); + expect(callbackResult).to.be.null; + expect(bid.video.durationBucket).to.be.undefined; + expect(bailResult).to.equal(false); + expect(logWarnStub.called).to.equal(true); + } + + let badBid100 = utils.deepClone(basicBid); + badBid100.video.durationSeconds = 100; + testRoundingForBadBid(badBid100); + + let badBid48 = utils.deepClone(basicBid); + badBid48.video.durationSeconds = 48; + testRoundingForBadBid(badBid48); + }); + }); + }); + + describe('adpodSetConfig', function () { + let logWarnStub; + beforeEach(function() { + logWarnStub = sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + logWarnStub.restore(); + }); + + it('should log a warning when values other than numbers are used in setConfig', function() { + adpodSetConfig({ + bidQueueSizeLimit: '2', + bidQueueTimeDelay: '50' + }); + expect(logWarnStub.calledTwice).to.equal(true); + }); + + it('should log a warning when numbers less than or equal to zero are used in setConfig', function() { + adpodSetConfig({ + bidQueueSizeLimit: 0, + bidQueueTimeDelay: -2 + }); + expect(logWarnStub.calledTwice).to.equal(true); + }); + + it('should not log any warning when using a valid config', function() { + adpodSetConfig({ + bidQueueSizeLimit: 10 + }); + expect(logWarnStub.called).to.equal(false); + + adpodSetConfig({ + bidQueueTimeDelay: 100, + bidQueueSizeLimit: 20 + }); + expect(logWarnStub.called).to.equal(false); + }) + }); + + describe('adpod utils', function() { + it('should sort bids array', function() { + let bids = [{ + cpm: 10.12345, + adserverTargeting: { + hb_pb: '10.00', + }, + video: { + durationBucket: 15 + } + }, { + cpm: 15, + adserverTargeting: { + hb_pb: '15.00', + }, + video: { + durationBucket: 15 + } + }, { + cpm: 15.00, + adserverTargeting: { + hb_pb: '15.00', + }, + video: { + durationBucket: 30 + } + }, { + cpm: 5.45, + adserverTargeting: { + hb_pb: '5.00', + }, + video: { + durationBucket: 5 + } + }, { + cpm: 20.1234567, + adserverTargeting: { + hb_pb: '20.10', + }, + video: { + durationBucket: 60 + } + }] + bids.sort(sortByPricePerSecond); + let sortedBids = [{ + cpm: 15, + adserverTargeting: { + hb_pb: '15.00', + }, + video: { + durationBucket: 15 + } + }, { + cpm: 5.45, + adserverTargeting: { + hb_pb: '5.00', + }, + video: { + durationBucket: 5 + } + }, { + cpm: 10.12345, + adserverTargeting: { + hb_pb: '10.00', + }, + video: { + durationBucket: 15 + } + }, { + cpm: 15.00, + adserverTargeting: { + hb_pb: '15.00', + }, + video: { + durationBucket: 30 + } + }, { + cpm: 20.1234567, + adserverTargeting: { + hb_pb: '20.10', + }, + video: { + durationBucket: 60 + } + }] + expect(bids).to.include.deep.ordered.members(sortedBids); + }); + }) +}); diff --git a/test/spec/modules/adponeBidAdapter_spec.js b/test/spec/modules/adponeBidAdapter_spec.js new file mode 100644 index 00000000000..1c64c60bd5c --- /dev/null +++ b/test/spec/modules/adponeBidAdapter_spec.js @@ -0,0 +1,102 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adponeBidAdapter'; + +describe('adponeBidAdapter', function () { + let bid = { + bidder: 'adpone', + adUnitCode: 'adunit-code', + sizes: [[300, 250]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + params: { + placementId: '1', + } + }; + + describe('isBidRequestValid', function () { + it('should return true when necessary information is found', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('should return false when necessary information is not found', function () { + // empty bid + expect(spec.isBidRequestValid({bidId: '', params: {}})).to.be.false; + + // empty bidId + bid.bidId = ''; + expect(spec.isBidRequestValid(bid)).to.be.false; + + // empty placementId + bid.bidId = '30b31c1838de1e'; + bid.params.placementId = ''; + expect(spec.isBidRequestValid(bid)).to.be.false; + + bid.adUnitCode = 'adunit-code'; + }); + }); +}); + +describe('interpretResponse', function () { + let serverResponse; + let bidRequest = { data: {id: '1234'} }; + + beforeEach(function () { + serverResponse = { + body: { + id: '2579e20c0bb89', + seatbid: [ + { + bid: [ + { + id: '613673EF-A07C-4486-8EE9-3FC71A7DC73D', + impid: '2579e20c0bb89_0', + price: 1, + adm: '', + adomain: [ + 'www.addomain.com' + ], + iurl: 'http://localhost11', + crid: 'creative111', + h: 250, + w: 300, + ext: { + dspid: 6 + } + } + ], + seat: 'adpone' + } + ], + cur: 'USD' + }, + }; + }); + + it('should correctly reorder the server response', function () { + const newResponse = spec.interpretResponse(serverResponse, bidRequest); + expect(newResponse.length).to.be.equal(1); + expect(newResponse[0]).to.deep.equal({ + id: '613673EF-A07C-4486-8EE9-3FC71A7DC73D', + requestId: '1234', + cpm: 1, + width: 300, + height: 250, + creativeId: 'creative111', + currency: 'USD', + netRevenue: true, + ttl: 300, + ad: '' + }); + }); + + it('should not add responses if the cpm is 0 or null', function () { + serverResponse.body.seatbid[0].bid[0].price = 0; + let response = spec.interpretResponse(serverResponse, bidRequest); + expect(response).to.deep.equal([]); + + serverResponse.body.seatbid[0].bid[0].price = null; + response = spec.interpretResponse(serverResponse, bidRequest); + expect(response).to.deep.equal([]) + }); +}); diff --git a/test/spec/modules/adspiritBidAdapter_spec.js b/test/spec/modules/adspiritBidAdapter_spec.js new file mode 100644 index 00000000000..7f907612384 --- /dev/null +++ b/test/spec/modules/adspiritBidAdapter_spec.js @@ -0,0 +1,142 @@ +import {expect} from 'chai'; +import {spec} from 'modules/adspiritBidAdapter'; + +describe('Adspirit adapter tests', function () { + let bidRequests, serverResponses; + + beforeEach(function () { + bidRequests = [ + // valid for adspirit + { + bidder: 'adspirit', + placementCode: 'ad-1', + params: { + placementId: '1', + host: 'test.adspirit.de' + }, + }, + // valid for xapadsmedia + { + bidder: 'xapadsmedia', + placementCode: 'ad-1', + params: { + placementId: '1' + }, + }, + // valid for connectad + { + bidder: 'connectad', + placementCode: 'ad-1', + params: { + placementId: '1' + }, + }, + // invalid 1 + { + bidder: 'adspirit', + placementCode: 'ad-1', + params: { + }, + }, + // invalid 2 + { + bidder: 'adspirit', + placementCode: 'ad-1', + params: { + host: 'test.adspirit.de' + }, + }, + // invalid 3 + { + bidder: '-', + placementCode: 'ad-1', + params: { + host: 'test.adspirit.de' + }, + } + ]; + serverResponses = [ + { + headers: {}, + body: { + cpm: 1.5, + w: 300, + h: 250, + placement_id: 1, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 0, + w: 0, + h: 0, + placement_id: 1, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 0, + w: 0, + h: 0, + placement_id: 0, + adm: '' + } + } + ] + }); + + describe('test bid request', function () { + it('with valid data 1', function () { + expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + }); + it('with valid data 2', function () { + expect(spec.isBidRequestValid(bidRequests[1])).to.equal(true); + }); + it('with valid data 3', function () { + expect(spec.isBidRequestValid(bidRequests[2])).to.equal(true); + }); + it('with invalid data 1 (no host)', function () { + expect(spec.isBidRequestValid(bidRequests[3])).to.equal(false); + }); + it('with invalid data 2 (no placementId)', function () { + expect(spec.isBidRequestValid(bidRequests[4])).to.equal(false); + }); + it('with invalid data 3 (no bidder code)', function () { + expect(spec.isBidRequestValid(bidRequests[5])).to.equal(false); + }); + }); + + describe('test request build', function () { + it('normal', function () { + var requests = spec.buildRequests([bidRequests[0]]); + expect(requests).to.be.lengthOf(1); + }); + }); + + describe('test bid responses', function () { + it('success 1', function () { + var bids = spec.interpretResponse(serverResponses[0], {'bidRequest': bidRequests[0]}); + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(1.5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].ad).to.have.length.above(1); + }); + it('fail 1 (cpm=0)', function () { + var bids = spec.interpretResponse(serverResponses[1], {'bidRequest': bidRequests[0]}); + expect(bids).to.be.lengthOf(0); + }); + it('fail 2 (no response)', function () { + var bids = spec.interpretResponse([], {'bidRequest': bidRequests[0]}); + expect(bids).to.be.lengthOf(0); + }); + it('fail 3 (status fail)', function () { + var bids = spec.interpretResponse(serverResponses[2], {'bidRequest': bidRequests[0]}); + expect(bids).to.be.lengthOf(0); + }); + }); +}); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index fa3e0479372..28bb057dffe 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -46,6 +46,7 @@ const SERVER_VIDEO_RESPONSE = { } ] }; + const SERVER_DISPLAY_RESPONSE = { 'source': {'aid': 12345, 'pubId': 54321}, 'bids': [{ @@ -57,7 +58,23 @@ const SERVER_DISPLAY_RESPONSE = { 'cur': 'USD', 'width': 300, 'cpm': 0.9 - }] + }], + 'cookieURLs': ['link1', 'link2'] +}; +const SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS = { + 'source': {'aid': 12345, 'pubId': 54321}, + 'bids': [{ + 'ad': '', + 'requestId': '2e41f65424c87c', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 250, + 'cur': 'USD', + 'width': 300, + 'cpm': 0.9 + }], + 'cookieURLs': ['link1', 'link2'], + 'cookieURLSTypes': ['image', 'iframe'] }; const videoBidderRequest = { @@ -70,6 +87,15 @@ const displayBidderRequest = { bids: [{bidId: '2e41f65424c87c'}] }; +const displayBidderRequestWithGdpr = { + bidderCode: 'bidderCode', + bids: [{bidId: '2e41f65424c87c'}], + gdprConsent: { + gdprApplies: true, + consentString: 'test' + } +}; + const videoEqResponse = [{ vastUrl: 'http://rtb.adtelligent.com/vast/?adid=44F2AEB9BFC881B3', requestId: '2e41f65424c87c', @@ -96,28 +122,66 @@ const displayEqResponse = [{ cpm: 0.9 }]; -describe('adtelligentBidAdapter', () => { +describe('adtelligentBidAdapter', function () { // todo remove only const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('user syncs as image', function () { + it('should be returned if pixel enabled', function () { + const syncs = spec.getUserSyncs({pixelEnabled: true}, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs.map(s => s.url)).to.deep.equal([SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs[0]]); + expect(syncs.map(s => s.type)).to.deep.equal(['image']); + }) + }) + + describe('user syncs as iframe', function () { + it('should be returned if iframe enabled', function () { + const syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs.map(s => s.url)).to.deep.equal([SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs[1]]); + expect(syncs.map(s => s.type)).to.deep.equal(['iframe']); + }) + }) + + describe('user syncs with both types', function () { + it('should be returned if pixel and iframe enabled', function () { + const syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs.map(s => s.url)).to.deep.equal(SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs); + expect(syncs.map(s => s.type)).to.deep.equal(SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLSTypes); + }) + }) + + describe('user syncs', function () { + it('should not be returned if pixel not set', function () { + const syncs = spec.getUserSyncs({}, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs).to.be.empty; + }) + }) + + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - it('should return true when required params found', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(VIDEO_REQUEST)).to.equal(12345); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, VIDEO_REQUEST); delete bid.params; expect(spec.isBidRequestValid(bid)).to.equal(undefined); }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let videoBidRequests = [VIDEO_REQUEST]; let displayBidRequests = [DISPLAY_REQUEST]; let videoAndDisplayBidRequests = [DISPLAY_REQUEST, VIDEO_REQUEST]; @@ -126,19 +190,19 @@ describe('adtelligentBidAdapter', () => { const videoRequest = spec.buildRequests(videoBidRequests, {}); const videoAndDisplayRequests = spec.buildRequests(videoAndDisplayBidRequests, {}); - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { expect(videoRequest.method).to.equal('GET'); expect(displayRequest.method).to.equal('GET'); expect(videoAndDisplayRequests.method).to.equal('GET'); }); - it('sends bid request to correct ENDPOINT', () => { + it('sends bid request to correct ENDPOINT', function () { expect(videoRequest.url).to.equal(ENDPOINT); expect(displayRequest.url).to.equal(ENDPOINT); expect(videoAndDisplayRequests.url).to.equal(ENDPOINT); }); - it('sends correct video bid parameters', () => { + it('sends correct video bid parameters', function () { const bid = Object.assign({}, videoRequest.data); delete bid.domain; @@ -152,7 +216,7 @@ describe('adtelligentBidAdapter', () => { expect(bid).to.deep.equal(eq); }); - it('sends correct display bid parameters', () => { + it('sends correct display bid parameters', function () { const bid = Object.assign({}, displayRequest.data); delete bid.domain; @@ -166,7 +230,7 @@ describe('adtelligentBidAdapter', () => { expect(bid).to.deep.equal(eq); }); - it('sends correct video and display bid parameters', () => { + it('sends correct video and display bid parameters', function () { const bid = Object.assign({}, videoAndDisplayRequests.data); delete bid.domain; @@ -185,18 +249,18 @@ describe('adtelligentBidAdapter', () => { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let serverResponse; let bidderRequest; let eqResponse; - afterEach(() => { + afterEach(function () { serverResponse = null; bidderRequest = null; eqResponse = null; }); - it('should get correct video bid response', () => { + it('should get correct video bid response', function () { serverResponse = SERVER_VIDEO_RESPONSE; bidderRequest = videoBidderRequest; eqResponse = videoEqResponse; @@ -204,7 +268,7 @@ describe('adtelligentBidAdapter', () => { bidServerResponseCheck(); }); - it('should get correct display bid response', () => { + it('should get correct display bid response', function () { serverResponse = SERVER_DISPLAY_RESPONSE; bidderRequest = displayBidderRequest; eqResponse = displayEqResponse; @@ -212,6 +276,13 @@ describe('adtelligentBidAdapter', () => { bidServerResponseCheck(); }); + it('should set gdpr data correctly', function () { + const builtRequestData = spec.buildRequests([DISPLAY_REQUEST], displayBidderRequestWithGdpr); + + expect(builtRequestData.data.gdpr).to.be.equal(1); + expect(builtRequestData.data.gdpr_consent).to.be.equal(displayBidderRequestWithGdpr.gdprConsent.consentString); + }); + function bidServerResponseCheck() { const result = spec.interpretResponse({body: serverResponse}, {bidderRequest}); @@ -225,13 +296,13 @@ describe('adtelligentBidAdapter', () => { expect(noBidResult.length).to.equal(0); } - it('handles video nobid responses', () => { + it('handles video nobid responses', function () { bidderRequest = videoBidderRequest; nobidServerResponseCheck(); }); - it('handles display nobid responses', () => { + it('handles display nobid responses', function () { bidderRequest = displayBidderRequest; nobidServerResponseCheck(); diff --git a/test/spec/modules/aduptechBidAdapter_spec.js b/test/spec/modules/aduptechBidAdapter_spec.js new file mode 100644 index 00000000000..da0b603ebfc --- /dev/null +++ b/test/spec/modules/aduptechBidAdapter_spec.js @@ -0,0 +1,261 @@ +import { expect } from 'chai'; +import { BIDDER_CODE, PUBLISHER_PLACEHOLDER, ENDPOINT_URL, ENDPOINT_METHOD, spec } from 'modules/aduptechBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; + +describe('AduptechBidAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when necessary information is given', function () { + expect(spec.isBidRequestValid({ + sizes: [[100, 200]], + params: { + publisher: 'test', + placement: '1234' + } + })).to.be.true; + }); + + it('should return false on empty bid', function () { + expect(spec.isBidRequestValid({})).to.be.false; + }); + + it('should return false on missing sizes', function () { + expect(spec.isBidRequestValid({ + params: { + publisher: 'test', + placement: '1234' + } + })).to.be.false; + }); + + it('should return false on empty sizes', function () { + expect(spec.isBidRequestValid({ + sizes: [], + params: { + publisher: 'test', + placement: '1234' + } + })).to.be.false; + }); + + it('should return false on missing params', function () { + expect(spec.isBidRequestValid({ + sizes: [[100, 200]], + })).to.be.false; + }); + + it('should return false on invalid params', function () { + expect(spec.isBidRequestValid({ + sizes: [[100, 200]], + params: 'bar' + })).to.be.false; + }); + + it('should return false on empty params', function () { + expect(spec.isBidRequestValid({ + sizes: [[100, 200]], + params: {} + })).to.be.false; + }); + + it('should return false on missing publisher', function () { + expect(spec.isBidRequestValid({ + sizes: [[100, 200]], + params: { + placement: '1234' + } + })).to.be.false; + }); + + it('should return false on missing placement', function () { + expect(spec.isBidRequestValid({ + sizes: [[100, 200]], + params: { + publisher: 'test' + } + })).to.be.false; + }); + }); + + describe('buildRequests', function () { + it('should send one bid request per ad unit to the endpoint via POST', function () { + const bidRequests = [ + { + bidder: BIDDER_CODE, + bidId: 'bidId1', + adUnitCode: 'adUnitCode1', + transactionId: 'transactionId1', + auctionId: 'auctionId1', + sizes: [[100, 200], [300, 400]], + params: { + publisher: 'publisher1', + placement: 'placement1' + } + }, + { + bidder: BIDDER_CODE, + bidId: 'bidId2', + adUnitCode: 'adUnitCode2', + transactionId: 'transactionId2', + auctionId: 'auctionId2', + sizes: [[500, 600]], + params: { + publisher: 'publisher2', + placement: 'placement2' + } + } + ]; + + const result = spec.buildRequests(bidRequests); + expect(result.length).to.equal(2); + + expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[0].params.publisher)); + expect(result[0].method).to.equal(ENDPOINT_METHOD); + expect(result[0].data).to.deep.equal({ + pageUrl: utils.getTopWindowUrl(), + referrer: utils.getTopWindowReferrer(), + bidId: bidRequests[0].bidId, + auctionId: bidRequests[0].auctionId, + transactionId: bidRequests[0].transactionId, + adUnitCode: bidRequests[0].adUnitCode, + sizes: bidRequests[0].sizes, + params: bidRequests[0].params, + gdpr: null + }); + + expect(result[1].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[1].params.publisher)); + expect(result[1].method).to.equal(ENDPOINT_METHOD); + expect(result[1].data).to.deep.equal({ + pageUrl: utils.getTopWindowUrl(), + referrer: utils.getTopWindowReferrer(), + bidId: bidRequests[1].bidId, + auctionId: bidRequests[1].auctionId, + transactionId: bidRequests[1].transactionId, + adUnitCode: bidRequests[1].adUnitCode, + sizes: bidRequests[1].sizes, + params: bidRequests[1].params, + gdpr: null + }); + }); + + it('should pass gdpr informations', function () { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString', + gdprApplies: true + } + }; + const bidRequests = [ + { + bidder: BIDDER_CODE, + bidId: 'bidId3', + adUnitCode: 'adUnitCode3', + transactionId: 'transactionId3', + auctionId: 'auctionId3', + sizes: [[100, 200], [300, 400]], + params: { + publisher: 'publisher3', + placement: 'placement3' + } + } + ]; + + const result = spec.buildRequests(bidRequests, bidderRequest); + expect(result.length).to.equal(1); + expect(result[0].data.gdpr).to.exist; + expect(result[0].data.gdpr.consentRequired).to.exist.and.to.equal(bidderRequest.gdprConsent.gdprApplies); + expect(result[0].data.gdpr.consentString).to.exist.and.to.equal(bidderRequest.gdprConsent.consentString); + }); + + it('should encode publisher param in endpoint url', function () { + const bidRequests = [ + { + bidder: BIDDER_CODE, + bidId: 'bidId1', + adUnitCode: 'adUnitCode1', + transactionId: 'transactionId1', + auctionId: 'auctionId1', + sizes: [[100, 200]], + params: { + publisher: 'crazy publisher key äÖÜ', + placement: 'placement1' + } + }, + ]; + + const result = spec.buildRequests(bidRequests); + + expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, encodeURIComponent(bidRequests[0].params.publisher))); + }); + + it('should handle empty bidRequests', function () { + expect(spec.buildRequests([])).to.deep.equal([]); + }); + }); + + describe('interpretResponse', function () { + it('should correctly interpret the server response', function () { + const serverResponse = { + body: { + bid: { + bidId: 'bidId1', + price: 0.12, + net: true, + currency: 'EUR', + ttl: 123 + }, + creative: { + id: 'creativeId1', + width: 100, + height: 200, + html: '
    Hello World
    ' + } + } + }; + + const result = spec.interpretResponse(serverResponse); + + expect(result).to.deep.equal([ + { + requestId: serverResponse.body.bid.bidId, + cpm: serverResponse.body.bid.price, + netRevenue: serverResponse.body.bid.net, + currency: serverResponse.body.bid.currency, + ttl: serverResponse.body.bid.ttl, + creativeId: serverResponse.body.creative.id, + width: serverResponse.body.creative.width, + height: serverResponse.body.creative.height, + ad: serverResponse.body.creative.html + } + ]); + }); + + it('should handle empty serverResponse', function () { + expect(spec.interpretResponse({})).to.deep.equal([]); + }); + + it('should handle missing bid', function () { + expect(spec.interpretResponse({ + body: { + creative: {} + } + })).to.deep.equal([]); + }); + + it('should handle missing creative', function () { + expect(spec.interpretResponse({ + body: { + bid: {} + } + })).to.deep.equal([]); + }); + }); +}); diff --git a/test/spec/modules/advangelistsBidAdapter_spec.js b/test/spec/modules/advangelistsBidAdapter_spec.js new file mode 100755 index 00000000000..fbdfc9f30ee --- /dev/null +++ b/test/spec/modules/advangelistsBidAdapter_spec.js @@ -0,0 +1,137 @@ +import { expect } from 'chai'; +import { spec } from 'modules/advangelistsBidAdapter'; +import { BANNER, VIDEO } from 'src/mediaTypes'; + +describe('advangelistsBidAdapter', function () { + let bidRequests; + let bidRequestsVid; + + beforeEach(function () { + bidRequests = [{'bidder': 'advangelists', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'placement': 1234}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; + + bidRequestsVid = [{'bidder': 'advangelists', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'placement': 1234, 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; + }); + + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed for banner', function () { + const bidRequest = bidRequests[0]; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when the required params are passed for video', function () { + const bidRequests = bidRequestsVid[0]; + expect(spec.isBidRequestValid(bidRequests)).to.equal(true); + }); + + it('should return false when no pub id params are passed', function () { + const bidRequest = bidRequests[0]; + bidRequest.params.pubid = ''; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when no placement params are passed', function () { + const bidRequest = bidRequests[0]; + bidRequest.params.placement = ''; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when a bid request is not passed', function () { + expect(spec.isBidRequestValid()).to.equal(false); + expect(spec.isBidRequestValid({})).to.equal(false); + }); + }); + + describe('spec.buildRequests', function () { + it('should create a POST request for each bid', function () { + const bidRequest = bidRequests[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].method).to.equal('POST'); + }); + + it('should create a POST request for each bid in video request', function () { + const bidRequest = bidRequestsVid[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].method).to.equal('POST'); + }); + + it('should have domain in request', function () { + const bidRequest = bidRequests[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].data.site.domain).length !== 0; + }); + }); + + describe('spec.interpretResponse', function () { + describe('for banner bids', function () { + it('should return no bids if the response is not valid', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + + if (typeof bidResponse !== 'undefined') { + expect(bidResponse.length).to.equal(0); + } else { + expect(true).to.equal(true); + } + }); + + it('should return no bids if the response is empty', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); + if (typeof bidResponse !== 'undefined') { + expect(bidResponse.length).to.equal(0); + } else { expect(true).to.equal(true); } + }); + + it('should return valid video bid responses', function () { + let _mediaTypes = VIDEO; + const advangelistsbidreqVid = {'bidRequest': {'mediaTypes': {'video': {'w': 320, 'h': 480}}}}; + const serverResponseVid = {'cur': 'USD', 'id': '25c6ab92aa0e81', 'seatbid': [{'seat': '3', 'bid': [{'crid': '1855', 'h': 480, 'protocol': 2, 'nurl': 'http://nep.advangelists.com/xp/evt?pp=1MO1wiaMhhq7wLRzZZwwwPkJxxKpYEnM5k5MH4qSGm1HR8rp3Nl7vDocvzZzSAvE4pnREL9mQ1kf5PDjk6E8em6DOk7vVrYUH1TYQyqCucd58PFpJNN7h30RXKHHFg3XaLuQ3PKfMuI1qZATBJ6WHcu875y0hqRdiewn0J4JsCYF53M27uwmcV0HnQxARQZZ72mPqrW95U6wgkZljziwKrICM3aBV07TU6YK5R5AyzJRuD6mtrQ2xtHlQ3jXVYKE5bvWFiUQd90t0jOGhPtYBNoOfP7uQ4ZZj4pyucxbr96orHe9PSOn9UpCSWArdx7s8lOfDpwOvbMuyGxynbStDWm38sDgd4bMHnIt762m5VMDNJfiUyX0vWzp05OsufJDVEaWhAM62i40lQZo7mWP4ipoOWLkmlaAzFIMsTcNaHAHiKKqGEOZLkCEhFNM0SLcvgN2HFRULOOIZvusq7TydOKxuXgCS91dLUDxDDDFUK83BFKlMkTxnCzkLbIR1bd9GKcr1TRryOrulyvRWAKAIhEsUzsc5QWFUhmI2dZ1eqnBQJ0c89TaPcnoaP2WipF68UgyiOstf2CBy0M34858tC5PmuQwQYwXscg6zyqDwR0i9MzGH4FkTyU5yeOlPcsA0ht6UcoCdFpHpumDrLUwAaxwGk1Nj8S6YlYYT5wNuTifDGbg22QKXzZBkUARiyVvgPn9nRtXnrd7WmiMYq596rya9RQj7LC0auQW8bHVQLEe49shsZDnAwZTWr4QuYKqgRGZcXteG7RVJe0ryBZezOq11ha9C0Lv0siNVBahOXE35Wzoq4c4BDaGpqvhaKN7pjeWLGlQR04ufWekwxiMWAvjmfgAfexBJ7HfbYNZpq__', 'adid': '61_1855', 'adomain': ['chevrolet.com.ar'], 'price': 2, 'w': 320, 'iurl': 'https://daf37cpxaja7f.cloudfront.net/c61/creative_url_14922301369663_1.png', 'cat': ['IAB2'], 'id': '7f570b40-aca1-4806-8ea8-818ea679c82b_0', 'attr': [], 'impid': '0', 'cid': '61'}]}], 'bidid': '7f570b40-aca1-4806-8ea8-818ea679c82b'} + const bidResponseVid = spec.interpretResponse({ body: serverResponseVid }, advangelistsbidreqVid); + delete bidResponseVid['vastUrl']; + delete bidResponseVid['ad']; + expect(bidResponseVid).to.deep.equal({ + requestId: bidRequestsVid[0].bidId, + bidderCode: 'advangelists', + creativeId: serverResponseVid.seatbid[0].bid[0].crid, + cpm: serverResponseVid.seatbid[0].bid[0].price, + width: serverResponseVid.seatbid[0].bid[0].w, + height: serverResponseVid.seatbid[0].bid[0].h, + mediaType: 'video', + currency: 'USD', + netRevenue: true, + ttl: 60 + }); + }); + + it('should return valid banner bid responses', function () { + const advangelistsbidreq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + advangelistsbidreq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], + h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] + + }; + }); + const serverResponse = {'id': '2aa73f571eaf29', 'seatbid': [{'bid': [{'id': '2c5e8a1a84522d', 'impid': '2c5e8a1a84522d', 'price': 0.81, 'adid': 'abcde-12345', 'nurl': '', 'adm': '
    ', 'adomain': ['advertiserdomain.com'], 'iurl': '', 'cid': 'campaign1', 'crid': 'abcde-12345', 'w': 300, 'h': 250}], 'seat': '19513bcfca8006'}], 'bidid': '19513bcfca8006', 'cur': 'USD', 'w': 300, 'h': 250}; + + const bidResponse = spec.interpretResponse({ body: serverResponse }, advangelistsbidreq); + expect(bidResponse).to.deep.equal({ + requestId: bidRequests[0].bidId, + ad: serverResponse.seatbid[0].bid[0].adm, + bidderCode: 'advangelists', + creativeId: serverResponse.seatbid[0].bid[0].crid, + cpm: serverResponse.seatbid[0].bid[0].price, + width: serverResponse.seatbid[0].bid[0].w, + height: serverResponse.seatbid[0].bid[0].h, + mediaType: 'banner', + currency: 'USD', + netRevenue: true, + ttl: 60 + }); + }); + }); + }); +}); diff --git a/test/spec/modules/advenueBidAdapter_spec.js b/test/spec/modules/advenueBidAdapter_spec.js new file mode 100644 index 00000000000..f6ffb277bf9 --- /dev/null +++ b/test/spec/modules/advenueBidAdapter_spec.js @@ -0,0 +1,107 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/advenueBidAdapter'; + +describe('AdvenueAdapter', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'advenue', + bidderRequestId: '145e1d6a7837c9', + params: { + placementId: 123, + traffic: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + describe('isBidRequestValid', function () { + it('Should return true when placementId can be cast to a number', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when placementId is not a number', function () { + bid.params.placementId = 'aaa'; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('//ssp.advenuemedia.co.uk/?c=o&m=multi'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + let placements = data['placements']; + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.all.keys('placementId', 'bidId', 'traffic', 'sizes'); + expect(placement.placementId).to.be.a('number'); + expect(placement.bidId).to.be.a('string'); + expect(placement.traffic).to.be.a('string'); + expect(placement.sizes).to.be.an('array'); + } + }); + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

    Hello ad

    ', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); +}); diff --git a/test/spec/modules/adxcgAnalyticsAdapter_spec.js b/test/spec/modules/adxcgAnalyticsAdapter_spec.js index edd09fdf54e..605da3bd6bc 100644 --- a/test/spec/modules/adxcgAnalyticsAdapter_spec.js +++ b/test/spec/modules/adxcgAnalyticsAdapter_spec.js @@ -1,71 +1,181 @@ import adxcgAnalyticsAdapter from 'modules/adxcgAnalyticsAdapter'; import { expect } from 'chai'; -let adaptermanager = require('src/adaptermanager'); +import adapterManager from 'src/adapterManager.js'; + let events = require('src/events'); let constants = require('src/constants.json'); -describe('adxcg analytics adapter', () => { +describe('adxcg analytics adapter', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = request => requests.push(request); sinon.stub(events, 'getEvents').returns([]); }); - afterEach(() => { + afterEach(function () { xhr.restore(); events.getEvents.restore(); }); - describe('track', () => { + describe('track', function () { let initOptions = { publisherId: '42' }; - adaptermanager.registerAnalyticsAdapter({ + let auctionTimestamp = 1496510254313; + + // prepare general auction - request and response + let bidRequest = { + 'bidderCode': 'appnexus', + 'bids': [{ + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', + 'sizes': '300x250,300x600', + 'bidId': '2eddfdc0c791dc', + 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7' + } + ] + }; + + let bidResponse = { + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '2eddfdc0c791dc', + 'mediaType': 'banner', + 'source': 'client', + 'requestId': '2eddfdc0c791dc', + 'cpm': 0.5, + 'creativeId': 29681110, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7', + 'responseTimestamp': 1522265866110, + 'requestTimestamp': 1522265863600, + 'bidder': 'appnexus', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'timeToRespond': 2510, + 'size': '300x250' + }; + + // what we expect after general auction + let expectedAfterBid = { + 'bidRequests': [ + { + 'bidderCode': 'appnexus', + 'bids': [ + { + 'transactionId': '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'bidId': '2eddfdc0c791dc', + 'sizes': '300x250,300x600', + 'params': { + 'placementId': '10433394' + } + } + ] + } + ], + 'bidResponses': [ + { + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'bidderCode': 'appnexus', + 'statusMessage': 'Bid available', + 'mediaType': 'banner', + 'renderedSize': '300x250', + 'cpm': 0.5, + 'currency': 'USD', + 'netRevenue': true, + 'timeToRespond': 2510, + 'bidId': '2eddfdc0c791dc', + 'creativeId': '29681110' + } + ], + 'auctionInit': {}, + 'bidTimeout': [ + 'bidderOne', + 'bidderTwo' + ] + }; + + // lets simulate that some bidders timeout + let bidTimeoutArgsV1 = [ + { + bidId: '2baa51527bd015', + bidder: 'bidderOne', + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + }, + { + bidId: '6fe3b4c2c23092', + bidder: 'bidderTwo', + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + } + ]; + + // now simulate some WIN and RENDERING + let wonRequest = { + 'adId': '4587fec4900b81', + 'mediaType': 'banner', + 'requestId': '4587fec4900b81', + 'cpm': 1.962, + 'creativeId': 2126, + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 302, + 'auctionId': '914bedad-b145-4e46-ba58-51365faea6cb', + 'statusMessage': 'Bid available', + 'responseTimestamp': 1530628534437, + 'requestTimestamp': 1530628534219, + 'bidder': 'testbidder4', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'timeToRespond': 218, + 'size': '300x250', + 'status': 'rendered' + }; + + let wonExpect = { + 'bidWons': [{ + 'bidderCode': 'testbidder4', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'mediaType': 'banner', + 'renderedSize': '300x250', + 'cpm': 1.962, + 'currency': 'EUR', + 'netRevenue': true, + 'timeToRespond': 218, + 'bidId': '4587fec4900b81', + 'statusMessage': 'Bid available', + 'status': 'rendered', + 'creativeId': '2126' + }] + }; + + adapterManager.registerAnalyticsAdapter({ code: 'adxcg', adapter: adxcgAnalyticsAdapter }); - beforeEach(() => { - adaptermanager.enableAnalytics({ + beforeEach(function () { + adapterManager.enableAnalytics({ provider: 'adxcg', options: initOptions }); }); - afterEach(() => { + afterEach(function () { adxcgAnalyticsAdapter.disableAnalytics(); }); - it('builds and sends auction data', () => { - let auctionTimestamp = 1496510254313; - let bidRequest = { - auctionId: 'requestIdData' - }; - let bidResponse = { - adId: 'adIdData', - ad: 'adContent' - }; - - let bidTimeoutArgsV1 = [ - { - bidId: '2baa51527bd015', - bidder: 'bidderOne', - adUnitCode: '/19968336/header-bid-tag-0', - auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' - }, - { - bidId: '6fe3b4c2c23092', - bidder: 'bidderTwo', - adUnitCode: '/19968336/header-bid-tag-0', - auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' - } - ]; - + it('builds and sends auction data', function () { // Step 1: Send auction init event events.emit(constants.EVENTS.AUCTION_INIT, { timestamp: auctionTimestamp @@ -85,35 +195,19 @@ describe('adxcg analytics adapter', () => { expect(requests.length).to.equal(1); - let auctionEventData = JSON.parse(requests[0].requestBody); - - expect(auctionEventData.bidRequests.length).to.equal(1); - expect(auctionEventData.bidRequests[0]).to.deep.equal(bidRequest); - - expect(auctionEventData.bidResponses.length).to.equal(1); - expect(auctionEventData.bidResponses[0].adId).to.equal(bidResponse.adId); - expect(auctionEventData.bidResponses[0]).to.not.have.property('ad'); + let realAfterBid = JSON.parse(requests[0].requestBody); - expect(auctionEventData.initOptions).to.deep.equal(initOptions); + expect(realAfterBid).to.deep.equal(expectedAfterBid); - expect(auctionEventData.auctionTimestamp).to.equal(auctionTimestamp); - - expect(auctionEventData.bidTimeout).to.deep.equal(['bidderOne', 'bidderTwo']); + expect(realAfterBid.bidTimeout).to.deep.equal(['bidderOne', 'bidderTwo']); // Step 6: Send auction bid won event - events.emit(constants.EVENTS.BID_WON, { - adId: 'adIdData', - ad: 'adContent' - }); + events.emit(constants.EVENTS.BID_WON, wonRequest); expect(requests.length).to.equal(2); - let winEventData = JSON.parse(requests[1].requestBody); - expect(winEventData.bidWon.adId).to.equal(bidResponse.adId); - expect(winEventData.bidWon).to.not.have.property('ad'); - expect(winEventData.initOptions).to.deep.equal(initOptions); - expect(winEventData.auctionTimestamp).to.equal(auctionTimestamp); + expect(winEventData).to.deep.equal(wonExpect); }); }); }); diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 99f07aa4d53..5bac9523b18 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -1,9 +1,69 @@ -import {expect} from 'chai'; -import * as url from 'src/url'; -import {spec} from 'modules/adxcgBidAdapter'; +import { expect } from 'chai' +import * as url from 'src/url' +import { spec } from 'modules/adxcgBidAdapter' -describe('AdxcgAdapter', () => { - describe('isBidRequestValid', () => { +describe('AdxcgAdapter', function () { + describe('isBidRequestValid', function () { + let bidBanner = { + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + } + + let bidVideo = { + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1', + 'api': [2], + 'protocols': [1, 2], + 'mimes': ['video/mp4', 'video/x-flv'], + 'maxduration': 30 + }, + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + } + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bidBanner)).to.equal(true) + }) + + it('should return true when required params not found', function () { + expect(spec.isBidRequestValid({})).to.be.false + }) + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bidBanner) + delete bid.params + bid.params = {} + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + + it('should return true when required video params not found', function () { + const simpleVideo = JSON.parse(JSON.stringify(bidVideo)) + simpleVideo.params.adzoneid = 123 + expect(spec.isBidRequestValid(simpleVideo)).to.be.false + 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 + }) + }) + + describe('request function http', function () { let bid = { 'bidder': 'adxcg', 'params': { @@ -14,21 +74,29 @@ describe('AdxcgAdapter', () => { 'bidId': '84ab500420319d', 'bidderRequestId': '7101db09af0db2', 'auctionId': '1d1a030790a475', - }; + } - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); + it('creates a valid adxcg request url', function () { + let request = spec.buildRequests([bid]) + expect(request).to.exist + expect(request.method).to.equal('GET') + let parsedRequestUrl = url.parse(request.url) + expect(parsedRequestUrl.hostname).to.equal('hbp.adxcg.net') + expect(parsedRequestUrl.pathname).to.equal('/get/adi') - it('should return false when required params are not passed', () => { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); + let query = parsedRequestUrl.search + expect(query.renderformat).to.equal('javascript') + expect(query.ver).to.equal('r20180703PB10') + expect(query.source).to.equal('pbjs10') + expect(query.pbjs).to.equal('$prebid.version$') + expect(query.adzoneid).to.equal('1') + expect(query.format).to.equal('300x250|640x360|1x1') + expect(query.jsonp).to.be.undefined + expect(query.prebidBidIds).to.equal('84ab500420319d') + }) + }) - describe('request function http', () => { + describe('gdpr compliance', function () { let bid = { 'bidder': 'adxcg', 'params': { @@ -39,32 +107,83 @@ describe('AdxcgAdapter', () => { 'bidId': '84ab500420319d', 'bidderRequestId': '7101db09af0db2', 'auctionId': '1d1a030790a475', - }; - - it('creates a valid adxcg request url', () => { - let request = spec.buildRequests([bid]); - expect(request).to.exist; - // console.log('IS:' + JSON.stringify(request)); - - expect(request.method).to.equal('GET'); - let parsedRequestUrl = url.parse(request.url); - - expect(parsedRequestUrl.hostname).to.equal('hbp.adxcg.net'); - expect(parsedRequestUrl.pathname).to.equal('/get/adi'); - - let query = parsedRequestUrl.search; - expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20171102PB10'); - expect(query.source).to.equal('pbjs10'); - expect(query.pbjs).to.equal('$prebid.version$'); - expect(query.adzoneid).to.equal('1'); - expect(query.format).to.equal('300x250|640x360|1x1'); - expect(query.jsonp).to.be.empty; - expect(query.prebidBidIds).to.equal('84ab500420319d'); - }); - }); - - describe('response handler', () => { + } + + it('should send GDPR Consent data if gdprApplies', function () { + let request = spec.buildRequests([bid], {gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}}) + let parsedRequestUrl = url.parse(request.url) + let query = parsedRequestUrl.search + + expect(query.gdpr).to.equal('1') + expect(query.gdpr_consent).to.equal('consentDataString') + }) + + it('should not send GDPR Consent data if gdprApplies is false or undefined', function () { + let request = spec.buildRequests([bid], { + gdprConsent: { + gdprApplies: false, + consentString: 'consentDataString' + } + }) + let parsedRequestUrl = url.parse(request.url) + let query = parsedRequestUrl.search + + expect(query.gdpr).to.be.undefined + expect(query.gdpr_consent).to.be.undefined + }) + }) + + describe('userid pubcid should be passed to querystring', function () { + let bid = [{ + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + }] + + let bidderRequests = {}; + + bid[0].userId = {'pubcid': 'pubcidabcd'}; + + it('should send pubcid if available', function () { + let request = spec.buildRequests(bid, bidderRequests) + let parsedRequestUrl = url.parse(request.url) + let query = parsedRequestUrl.search + expect(query.pubcid).to.equal('pubcidabcd') + }) + }) + + describe('userid tdid should be passed to querystring', function () { + let bid = [{ + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + }] + + let bidderRequests = {}; + + bid[0].userId = {'tdid': 'tdidabcd'}; + + it('should send pubcid if available', function () { + let request = spec.buildRequests(bid, bidderRequests) + let parsedRequestUrl = url.parse(request.url) + let query = parsedRequestUrl.search + expect(query.tdid).to.equal('tdidabcd'); + }) + }) + + describe('response handler', function () { let BIDDER_REQUEST = { 'bidder': 'adxcg', 'params': { @@ -75,7 +194,7 @@ describe('AdxcgAdapter', () => { 'bidId': '84ab500420319d', 'bidderRequestId': '7101db09af0db2', 'auctionId': '1d1a030790a475', - }; + } let BANNER_RESPONSE = { @@ -166,6 +285,14 @@ describe('AdxcgAdapter', () => { 'label': 'SPONSORED', 'value': 'sponsoredByContent' } + }, { + 'id': 5, + 'required': 0, + 'icon': { + 'url': 'iconContent', + 'w': 400, + 'h': 400 + } }], 'link': { 'url': 'linkContent' @@ -176,90 +303,98 @@ describe('AdxcgAdapter', () => { header: {'someheader': 'fakedata'} } - it('handles regular responses', () => { - let result = spec.interpretResponse(BANNER_RESPONSE, BIDDER_REQUEST); - - expect(result).to.have.lengthOf(1); - - expect(result[0]).to.exist; - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); - expect(result[0].ad).to.equal(''); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); - expect(result[0].dealId).to.not.exist; - }); - - it('handles regular responses with dealid', () => { - let result = spec.interpretResponse(BANNER_RESPONSE_WITHDEALID, BIDDER_REQUEST); - - expect(result).to.have.lengthOf(1); - - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); - expect(result[0].ad).to.equal(''); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); - }); - - it('handles video responses', () => { - let result = spec.interpretResponse(VIDEO_RESPONSE, BIDDER_REQUEST); - expect(result).to.have.lengthOf(1); - - expect(result[0].width).to.equal(640); - expect(result[0].height).to.equal(360); - expect(result[0].mediaType).to.equal('video'); - expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); - expect(result[0].vastUrl).to.equal('vastContentUrl'); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); - }); - - it('handles native responses', () => { - let result = spec.interpretResponse(NATIVE_RESPONSE, BIDDER_REQUEST); - - expect(result[0].width).to.equal(0); - expect(result[0].height).to.equal(0); - expect(result[0].mediaType).to.equal('native'); - expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); - - expect(result[0].native.clickUrl).to.equal('linkContent'); - expect(result[0].native.impressionTrackers).to.deep.equal(['impressionTracker1', 'impressionTracker2']); - expect(result[0].native.title).to.equal('titleContent'); - expect(result[0].native.image).to.equal('imageContent'); - expect(result[0].native.body).to.equal('descriptionContent'); - expect(result[0].native.sponsoredBy).to.equal('sponsoredByContent'); - }); - - it('handles nobid responses', () => { - let response = []; - let bidderRequest = BIDDER_REQUEST; - - let result = spec.interpretResponse(response, bidderRequest); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs', () => { + it('handles regular responses', function () { + let result = spec.interpretResponse(BANNER_RESPONSE, BIDDER_REQUEST) + + expect(result).to.have.lengthOf(1) + + expect(result[0]).to.exist + expect(result[0].width).to.equal(300) + expect(result[0].height).to.equal(250) + expect(result[0].creativeId).to.equal(42) + expect(result[0].cpm).to.equal(0.45) + expect(result[0].ad).to.equal('') + expect(result[0].currency).to.equal('USD') + expect(result[0].netRevenue).to.equal(true) + expect(result[0].ttl).to.equal(300) + expect(result[0].dealId).to.not.exist + }) + + it('handles regular responses with dealid', function () { + let result = spec.interpretResponse(BANNER_RESPONSE_WITHDEALID, BIDDER_REQUEST) + + expect(result).to.have.lengthOf(1) + + expect(result[0].width).to.equal(300) + expect(result[0].height).to.equal(250) + expect(result[0].creativeId).to.equal(42) + expect(result[0].cpm).to.equal(0.45) + expect(result[0].ad).to.equal('') + expect(result[0].currency).to.equal('USD') + expect(result[0].netRevenue).to.equal(true) + expect(result[0].ttl).to.equal(300) + }) + + it('handles video responses', function () { + let result = spec.interpretResponse(VIDEO_RESPONSE, BIDDER_REQUEST) + expect(result).to.have.lengthOf(1) + + expect(result[0].width).to.equal(640) + expect(result[0].height).to.equal(360) + expect(result[0].mediaType).to.equal('video') + expect(result[0].creativeId).to.equal(42) + expect(result[0].cpm).to.equal(0.45) + expect(result[0].vastUrl).to.equal('vastContentUrl') + expect(result[0].currency).to.equal('USD') + expect(result[0].netRevenue).to.equal(true) + expect(result[0].ttl).to.equal(300) + }) + + it('handles native responses', function () { + let result = spec.interpretResponse(NATIVE_RESPONSE, BIDDER_REQUEST) + + expect(result[0].width).to.equal(0) + expect(result[0].height).to.equal(0) + expect(result[0].mediaType).to.equal('native') + expect(result[0].creativeId).to.equal(42) + expect(result[0].cpm).to.equal(0.45) + expect(result[0].currency).to.equal('USD') + expect(result[0].netRevenue).to.equal(true) + expect(result[0].ttl).to.equal(300) + + expect(result[0].native.clickUrl).to.equal('linkContent') + expect(result[0].native.impressionTrackers).to.deep.equal(['impressionTracker1', 'impressionTracker2']) + expect(result[0].native.title).to.equal('titleContent') + + expect(result[0].native.image.url).to.equal('imageContent') + expect(result[0].native.image.height).to.equal(600) + expect(result[0].native.image.width).to.equal(600) + + expect(result[0].native.icon.url).to.equal('iconContent') + expect(result[0].native.icon.height).to.equal(400) + expect(result[0].native.icon.width).to.equal(400) + + expect(result[0].native.body).to.equal('descriptionContent') + expect(result[0].native.sponsoredBy).to.equal('sponsoredByContent') + }) + + it('handles nobid responses', function () { + let response = [] + let bidderRequest = BIDDER_REQUEST + + let result = spec.interpretResponse(response, bidderRequest) + expect(result.length).to.equal(0) + }) + }) + + describe('getUserSyncs', function () { let syncoptionsIframe = { 'iframeEnabled': 'true' - }; - - it('should return iframe sync option', () => { - expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.equal('iframe'); - expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal('//cdn.adxcg.net/pb-sync.html'); - }); - }); -}); + } + + it('should return iframe sync option', function () { + expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.equal('iframe') + expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal('//cdn.adxcg.net/pb-sync.html') + }) + }) +}) diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index d7a5b1a87b7..7edb9416b03 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -4,10 +4,9 @@ import { parse } from '../../../src/url'; import { spec } from 'modules/adyoulikeBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('Adyoulike Adapter', () => { +describe('Adyoulike Adapter', function () { const canonicalUrl = 'http://canonical.url/?t=%26'; const defaultDC = 'hb-api'; - const bidderCode = 'adyoulike'; const bidRequestWithEmptyPlacement = [ { 'bidId': 'bid_id_0', @@ -18,7 +17,6 @@ describe('Adyoulike Adapter', () => { } ]; const bidRequestWithEmptySizes = { - 'bidderCode': 'adyoulike', 'bids': [ { 'bidId': 'bid_id_0', @@ -134,15 +132,15 @@ describe('Adyoulike Adapter', () => { ]; const adapter = newBidder(spec); - let getEndpoint = (dc = defaultDC) => `http://${dc}.omnitagjs.com/hb-api/prebid`; + let getEndpoint = (dc = defaultDC) => `https://${dc}.omnitagjs.com/hb-api/prebid`; - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidId': 'bid_id_1', 'bidder': 'adyoulike', @@ -154,18 +152,18 @@ describe('Adyoulike Adapter', () => { 'transactionId': 'bid_id_1_transaction_id' }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.size; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -175,10 +173,10 @@ describe('Adyoulike Adapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let canonicalQuery; - beforeEach(() => { + beforeEach(function () { let canonical = document.createElement('link'); canonical.rel = 'canonical'; canonical.href = canonicalUrl; @@ -186,14 +184,13 @@ describe('Adyoulike Adapter', () => { canonicalQuery.withArgs('link[rel="canonical"][href]').returns(canonical); }); - afterEach(() => { + afterEach(function () { canonicalQuery.restore(); }); - it('should add gdpr consent information to the request', () => { + it('should add gdpr consent information to the request', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { - 'bidderCode': 'adyoulike', 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', 'timeout': 3000, @@ -212,7 +209,7 @@ describe('Adyoulike Adapter', () => { expect(payload.gdprConsent.consentRequired).to.exist.and.to.be.true; }); - it('sends bid request to endpoint with single placement', () => { + it('sends bid request to endpoint with single placement', function () { const request = spec.buildRequests(bidRequestWithSinglePlacement); const payload = JSON.parse(request.data); @@ -226,7 +223,7 @@ describe('Adyoulike Adapter', () => { expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); }); - it('sends bid request to endpoint with single placement without canonical', () => { + it('sends bid request to endpoint with single placement without canonical', function () { canonicalQuery.restore(); const request = spec.buildRequests(bidRequestWithSinglePlacement); const payload = JSON.parse(request.data); @@ -241,7 +238,7 @@ describe('Adyoulike Adapter', () => { expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); }); - it('sends bid request to endpoint with multiple placements', () => { + it('sends bid request to endpoint with multiple placements', function () { const request = spec.buildRequests(bidRequestMultiPlacements); const payload = JSON.parse(request.data); expect(request.url).to.contain(getEndpoint()); @@ -261,7 +258,7 @@ describe('Adyoulike Adapter', () => { expect(payload.PageRefreshed).to.equal(false); }); - it('sends bid request to endpoint setted by parameters', () => { + it('sends bid request to endpoint setted by parameters', function () { const request = spec.buildRequests(bidRequestWithDCPlacement); const payload = JSON.parse(request.data); @@ -269,16 +266,16 @@ describe('Adyoulike Adapter', () => { }); }); // - describe('interpretResponse', () => { + describe('interpretResponse', function () { let serverResponse; - beforeEach(() => { + beforeEach(function () { serverResponse = { body: {} } }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = [{ BidID: '123dfsdf', Attempt: '32344fdse1', @@ -289,7 +286,7 @@ describe('Adyoulike Adapter', () => { expect(result).deep.equal([]); }); - it('receive reponse with single placement', () => { + it('receive reponse with single placement', function () { serverResponse.body = responseWithSinglePlacement; let result = spec.interpretResponse(serverResponse, bidRequestWithSinglePlacement); @@ -300,19 +297,17 @@ describe('Adyoulike Adapter', () => { expect(result[0].height).to.equal(300); }); - it('receive reponse with multiple placement', () => { + it('receive reponse with multiple placement', function () { serverResponse.body = responseWithMultiplePlacements; let result = spec.interpretResponse(serverResponse, bidRequestMultiPlacements); expect(result.length).to.equal(2); - expect(result[0].bidderCode).to.equal(bidderCode); expect(result[0].cpm).to.equal(0.5); expect(result[0].ad).to.equal('placement_0'); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(300); - expect(result[1].bidderCode).to.equal(bidderCode); expect(result[1].cpm).to.equal(0.6); expect(result[1].ad).to.equal('placement_1'); expect(result[1].width).to.equal(300); diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js new file mode 100644 index 00000000000..642ac2f0df4 --- /dev/null +++ b/test/spec/modules/ajaBidAdapter_spec.js @@ -0,0 +1,311 @@ +import { spec } from 'modules/ajaBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//ad.as.amanad.adtdp.com/v2/prebid'; + +describe('AjaAdapter', function () { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'aja', + 'params': { + 'asi': '123456' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'asi': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'aja', + 'params': { + 'asi': '123456' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + let bidderRequest = { + refererInfo: { + referer: 'http://hoge.com' + } + }; + + it('sends bid request to ENDPOINT via GET', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].method).to.equal('GET'); + expect(requests[0].data).to.equal('asi=123456&skt=5&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=http%3A%2F%2Fhoge.com&'); + }); + }); + + describe('interpretResponse', function () { + it('should get correct banner bid response', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 1, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'USD', + 'creative_id': '123abc', + 'banner': { + 'w': 300, + 'h': 250, + 'tag': '
    ', + 'imps': [ + '//as.amanad.adtdp.com/v1/imp' + ] + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let expectedResponse = [ + { + 'requestId': '51ef8751f9aead', + 'cpm': 12.34, + 'creativeId': '123abc', + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    ', + 'mediaType': 'banner', + 'currency': 'USD', + 'ttl': 300, + 'netRevenue': true + } + ]; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles video responses', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 3, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'JPY', + 'creative_id': '123abc', + 'video': { + 'w': 300, + 'h': 250, + 'vtag': '', + 'purl': 'http://cdn/player', + 'progress': true, + 'loop': false, + 'inread': false + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('renderer'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles native response', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 2, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'JPY', + 'creative_id': '123abc', + 'native': { + 'template_and_ads': { + 'head': '', + 'body_wrapper': '', + 'body': '', + 'ads': [ + { + 'ad_format_id': 10, + 'assets': { + 'ad_spot_id': '123abc', + 'index': 0, + 'adchoice_url': 'https://aja-kk.co.jp/optout', + 'cta_text': 'cta', + 'img_icon': 'https://example.com/img_icon', + 'img_icon_width': '50', + 'img_icon_height': '50', + 'img_main': 'https://example.com/img_main', + 'img_main_width': '200', + 'img_main_height': '100', + 'lp_link': 'https://example.com/lp?k=v', + 'sponsor': 'sponsor', + 'title': 'ad_title', + 'description': 'ad_desc' + }, + 'imps': [ + 'https://example.com/imp' + ], + 'inviews': [ + 'https://example.com/inview' + ], + 'jstracker': '', + 'disable_trimming': false + } + ] + } + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let expectedResponse = [ + { + 'requestId': '51ef8751f9aead', + 'cpm': 12.34, + 'creativeId': '123abc', + 'dealId': undefined, + 'mediaType': 'native', + 'currency': 'JPY', + 'ttl': 300, + 'netRevenue': true, + 'native': { + 'title': 'ad_title', + 'body': 'ad_desc', + 'cta': 'cta', + 'sponsoredBy': 'sponsor', + 'image': { + 'url': 'https://example.com/img_main', + 'width': 200, + 'height': 100 + }, + 'icon': { + 'url': 'https://example.com/img_icon', + 'width': 50, + 'height': 50 + }, + 'clickUrl': 'https://example.com/lp?k=v', + 'impressionTrackers': [ + 'https://example.com/imp' + ], + 'privacyLink': 'https://aja-kk.co.jp/optout', + } + } + ]; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}) + expect(result).to.deep.equal(expectedResponse) + }); + + it('handles nobid responses', function () { + let response = { + 'is_ad_return': false, + 'ad': {} + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + const bidResponse1 = { + body: { + 'is_ad_return': true, + 'ad': { /* ad body */ }, + 'syncs': [ + 'https://example.test/pixel/1' + ], + 'sync_htmls': [ + 'https://example.test/iframe/1' + ] + } + }; + + const bidResponse2 = { + body: { + 'is_ad_return': true, + 'ad': { /* ad body */ }, + 'syncs': [ + 'https://example.test/pixel/2' + ] + } + }; + + it('should use a sync url from first response (pixel and iframe)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [bidResponse1, bidResponse2]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://example.test/pixel/1' + }, + { + type: 'iframe', + url: 'https://example.test/iframe/1' + } + ]); + }); + + it('handle empty response (e.g. timeout)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, []); + expect(syncs).to.deep.equal([]); + }); + + it('returns empty syncs when not pixel enabled and not iframe enabled', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([]); + }); + + it('returns pixel syncs when pixel enabled and not iframe enabled', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://example.test/pixel/1' + } + ]); + }); + + it('returns iframe syncs when not pixel enabled and iframe enabled', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://example.test/iframe/1' + } + ]); + }); + }); +}); diff --git a/test/spec/modules/andbeyondBidAdapter_spec.js b/test/spec/modules/andbeyondBidAdapter_spec.js new file mode 100644 index 00000000000..f9b0758749b --- /dev/null +++ b/test/spec/modules/andbeyondBidAdapter_spec.js @@ -0,0 +1,208 @@ +import {expect} from 'chai'; +import {spec} from 'modules/andbeyondBidAdapter'; +import * as utils from 'src/utils'; + +describe('andbeyond adapter', function () { + const bid1_zone1 = { + bidder: 'andbeyond', + bidId: 'Bid_01', + params: {zoneId: 1, host: 'rtb.andbeyond.com'}, + placementCode: 'ad-unit-1', + sizes: [[300, 250], [300, 200]] + }, bid2_zone2 = { + bidder: 'andbeyond', + bidId: 'Bid_02', + params: {zoneId: 2, host: 'rtb.andbeyond.com'}, + placementCode: 'ad-unit-2', + sizes: [[728, 90]] + }, bid3_host2 = { + bidder: 'andbeyond', + bidId: 'Bid_02', + params: {zoneId: 1, host: 'rtb-private.andbeyond.com'}, + placementCode: 'ad-unit-2', + sizes: [[728, 90]] + }, bid_without_zone = { + bidder: 'andbeyond', + bidId: 'Bid_W', + params: {host: 'rtb-private.andbeyond.com'}, + placementCode: 'ad-unit-1', + sizes: [[728, 90]] + }, bid_without_host = { + bidder: 'andbeyond', + bidId: 'Bid_W', + params: {zoneId: 1}, + placementCode: 'ad-unit-1', + sizes: [[728, 90]] + }, bid_with_wrong_zoneId = { + bidder: 'andbeyond', + bidId: 'Bid_02', + params: {zoneId: 'wrong id', host: 'rtb.andbeyond.com'}, + placementCode: 'ad-unit-2', + sizes: [[728, 90]] + }, usersyncOnlyResponse = { + id: 'nobid1', + ext: { + adk_usersync: ['http://adk.sync.com/sync'] + } + }; + + const bidResponse1 = { + id: 'bid1', + seatbid: [{ + bid: [{ + id: '1', + impid: 'Bid_01', + crid: '100_001', + price: 3.01, + nurl: 'https://rtb.com/win?i=ZjKoPYSFI3Y_0', + adm: '', + w: 300, + h: 250 + }] + }], + cur: 'USD', + ext: { + adk_usersync: ['http://adk.sync.com/sync'] + } + }, bidResponse2 = { + id: 'bid2', + seatbid: [{ + bid: [{ + id: '2', + impid: 'Bid_02', + crid: '100_002', + price: 1.31, + adm: '', + w: 300, + h: 250 + }] + }], + cur: 'USD' + }; + + describe('input parameters validation', function () { + it('empty request shouldn\'t generate exception', function () { + expect(spec.isBidRequestValid({ + bidderCode: 'andbeyond' + })).to.be.equal(false); + }); + + it('request without zone shouldn\'t issue a request', function () { + expect(spec.isBidRequestValid(bid_without_zone)).to.be.equal(false); + }); + + it('request without host shouldn\'t issue a request', function () { + expect(spec.isBidRequestValid(bid_without_host)).to.be.equal(false); + }); + + it('empty request shouldn\'t generate exception', function () { + expect(spec.isBidRequestValid(bid_with_wrong_zoneId)).to.be.equal(false); + }); + }); + + describe('banner request building', function () { + let bidRequest; + before(function () { + let wmock = sinon.stub(utils, 'getTopWindowLocation').callsFake(() => ({ + protocol: 'https:', + hostname: 'example.com', + host: 'example.com', + pathname: '/index.html', + href: 'https://example.com/index.html' + })); + let dntmock = sinon.stub(utils, 'getDNT').callsFake(() => true); + let request = spec.buildRequests([bid1_zone1])[0]; + bidRequest = JSON.parse(request.data.r); + wmock.restore(); + dntmock.restore(); + }); + + it('should be a first-price auction', function () { + expect(bidRequest).to.have.property('at', 1); + }); + + it('should have banner object', function () { + expect(bidRequest.imp[0]).to.have.property('banner'); + }); + + it('should have w/h', function () { + expect(bidRequest.imp[0].banner).to.have.property('format'); + expect(bidRequest.imp[0].banner.format).to.be.eql([{w: 300, h: 250}, {w: 300, h: 200}]); + }); + + it('should respect secure connection', function () { + expect(bidRequest.imp[0]).to.have.property('secure', 1); + }); + + it('should have tagid', function () { + expect(bidRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); + }); + + it('should create proper site block', function () { + expect(bidRequest.site).to.have.property('domain', 'example.com'); + expect(bidRequest.site).to.have.property('page', 'https://example.com/index.html'); + }); + + it('should fill device with caller macro', function () { + expect(bidRequest).to.have.property('device'); + expect(bidRequest.device).to.have.property('ip', 'caller'); + expect(bidRequest.device).to.have.property('ua', 'caller'); + expect(bidRequest.device).to.have.property('dnt', 1); + }); + }); + + describe('requests routing', function () { + it('should issue a request for each host', function () { + let pbRequests = spec.buildRequests([bid1_zone1, bid3_host2]); + expect(pbRequests).to.have.length(2); + expect(pbRequests[0].url).to.have.string(`//${bid1_zone1.params.host}/`); + expect(pbRequests[1].url).to.have.string(`//${bid3_host2.params.host}/`); + }); + + it('should issue a request for each zone', function () { + let pbRequests = spec.buildRequests([bid1_zone1, bid2_zone2]); + expect(pbRequests).to.have.length(2); + expect(pbRequests[0].data.zone).to.be.equal(bid1_zone1.params.zoneId); + expect(pbRequests[1].data.zone).to.be.equal(bid2_zone2.params.zoneId); + }); + }); + + describe('responses processing', function () { + it('should return fully-initialized banner bid-response', function () { + let request = spec.buildRequests([bid1_zone1])[0]; + let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; + expect(resp).to.have.property('requestId', 'Bid_01'); + expect(resp).to.have.property('cpm', 3.01); + expect(resp).to.have.property('width', 300); + expect(resp).to.have.property('height', 250); + expect(resp).to.have.property('creativeId', '100_001'); + expect(resp).to.have.property('currency'); + expect(resp).to.have.property('ttl'); + expect(resp).to.have.property('mediaType', 'banner'); + expect(resp).to.have.property('ad'); + expect(resp.ad).to.have.string(''); + }); + + it('should add nurl as pixel for banner response', function () { + let request = spec.buildRequests([bid1_zone1])[0]; + let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; + let expectedNurl = bidResponse1.seatbid[0].bid[0].nurl + '&px=1'; + expect(resp.ad).to.have.string(expectedNurl); + }); + + it('should handle bidresponse with user-sync only', function () { + let request = spec.buildRequests([bid1_zone1])[0]; + let resp = spec.interpretResponse({body: usersyncOnlyResponse}, request); + expect(resp).to.have.length(0); + }); + + it('should perform usersync', function () { + let syncs = spec.getUserSyncs({iframeEnabled: false}, [{body: bidResponse1}]); + expect(syncs).to.have.length(0); + syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: bidResponse1}]); + expect(syncs).to.have.length(1); + expect(syncs[0]).to.have.property('type', 'iframe'); + expect(syncs[0]).to.have.property('url', 'http://adk.sync.com/sync'); + }); + }); +}); diff --git a/test/spec/modules/aniviewBidAdapter_spec.js b/test/spec/modules/aniviewBidAdapter_spec.js new file mode 100644 index 00000000000..ce8a34509c4 --- /dev/null +++ b/test/spec/modules/aniviewBidAdapter_spec.js @@ -0,0 +1,183 @@ +import { spec } from 'modules/aniviewBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +const { expect } = require('chai'); + +describe('ANIVIEW Bid Adapter Test', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'aniview', + 'params': { + 'AV_PUBLISHERID': '123456', + 'AV_CHANNELID': '123456' + }, + 'adUnitCode': 'video1', + 'sizes': [[300, 250], [640, 480]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a', + 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + something: 'is wrong' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const ENDPOINT = 'https://v.lkqd.net/ad'; + let bid2Requests = [ + { + 'bidder': 'aniview', + 'params': { + 'AV_PUBLISHERID': '123456', + 'AV_CHANNELID': '123456' + }, + 'adUnitCode': 'test1', + 'sizes': [[300, 250], [640, 480]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a', + 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d', + } + ]; + let bid1Request = [ + { + 'bidder': 'aniview', + 'params': { + 'AV_PUBLISHERID': '123456', + 'AV_CHANNELID': '123456' + }, + 'adUnitCode': 'test1', + 'sizes': [640, 480], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a', + 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d', + } + ]; + + it('Test 2 requests', function () { + const requests = spec.buildRequests(bid2Requests); + expect(requests.length).to.equal(2); + const r1 = requests[0]; + const d1 = requests[0].data; + expect(d1).to.have.property('AV_PUBLISHERID'); + expect(d1.AV_PUBLISHERID).to.equal('123456'); + expect(d1).to.have.property('AV_CHANNELID'); + expect(d1.AV_CHANNELID).to.equal('123456'); + expect(d1).to.have.property('AV_WIDTH'); + expect(d1.AV_WIDTH).to.equal(300); + expect(d1).to.have.property('AV_HEIGHT'); + expect(d1.AV_HEIGHT).to.equal(250); + expect(d1).to.have.property('AV_URL'); + expect(d1).to.have.property('cb'); + expect(d1).to.have.property('s2s'); + expect(d1.s2s).to.equal('1'); + expect(d1).to.have.property('pbjs'); + expect(d1.pbjs).to.equal(1); + expect(r1).to.have.property('url'); + expect(r1.url).to.contain('https://gov.aniview.com/api/adserver/vast3/'); + const r2 = requests[1]; + const d2 = requests[1].data; + expect(d2).to.have.property('AV_PUBLISHERID'); + expect(d2.AV_PUBLISHERID).to.equal('123456'); + expect(d2).to.have.property('AV_CHANNELID'); + expect(d2.AV_CHANNELID).to.equal('123456'); + expect(d2).to.have.property('AV_WIDTH'); + expect(d2.AV_WIDTH).to.equal(640); + expect(d2).to.have.property('AV_HEIGHT'); + expect(d2.AV_HEIGHT).to.equal(480); + expect(d2).to.have.property('AV_URL'); + expect(d2).to.have.property('cb'); + expect(d2).to.have.property('s2s'); + expect(d2.s2s).to.equal('1'); + expect(d2).to.have.property('pbjs'); + expect(d2.pbjs).to.equal(1); + expect(r2).to.have.property('url'); + expect(r2.url).to.contain('https://gov.aniview.com/api/adserver/vast3/'); + }); + + it('Test 1 request', function () { + const requests = spec.buildRequests(bid1Request); + expect(requests.length).to.equal(1); + const r = requests[0]; + const d = requests[0].data; + expect(d).to.have.property('AV_PUBLISHERID'); + expect(d.AV_PUBLISHERID).to.equal('123456'); + expect(d).to.have.property('AV_CHANNELID'); + expect(d.AV_CHANNELID).to.equal('123456'); + expect(d).to.have.property('AV_WIDTH'); + expect(d.AV_WIDTH).to.equal(640); + expect(d).to.have.property('AV_HEIGHT'); + expect(d.AV_HEIGHT).to.equal(480); + expect(d).to.have.property('AV_URL'); + expect(d).to.have.property('cb'); + expect(d).to.have.property('s2s'); + expect(d.s2s).to.equal('1'); + expect(d).to.have.property('pbjs'); + expect(d.pbjs).to.equal(1); + expect(r).to.have.property('url'); + expect(r.url).to.contain('https://gov.aniview.com/api/adserver/vast3/'); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = { + 'url': 'https://gov.aniview.com/api/adserver/vast3/', + 'data': { + 'bidId': '253dcb69fb2577', + AV_PUBLISHERID: '55b78633181f4603178b4568', + AV_CHANNELID: '55b7904d181f46410f8b4568', + } + }; + let serverResponse = {}; + serverResponse.body = 'FORDFORD00:00:15'; + + it('Check bid interpretResponse', function () { + const BIDDER_CODE = 'aniview'; + let bidResponses = spec.interpretResponse(serverResponse, bidRequest); + expect(bidResponses.length).to.equal(1); + let bidResponse = bidResponses[0]; + expect(bidResponse.requestId).to.equal(bidRequest.data.bidId); + expect(bidResponse.bidderCode).to.equal(BIDDER_CODE); + expect(bidResponse.cpm).to.equal('2'); + expect(bidResponse.ttl).to.equal(600); + expect(bidResponse.currency).to.equal('USD'); + expect(bidResponse.netRevenue).to.equal(true); + expect(bidResponse.mediaType).to.equal('video'); + }); + + it('safely handles XML parsing failure from invalid bid response', function () { + let invalidServerResponse = {}; + invalidServerResponse.body = ''; + + let result = spec.interpretResponse(invalidServerResponse, bidRequest); + expect(result.length).to.equal(0); + }); + + it('handles nobid responses', function () { + let nobidResponse = {}; + nobidResponse.body = ''; + + let result = spec.interpretResponse(nobidResponse, bidRequest); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index d69b9e6e3d8..8386c2c2462 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -75,9 +75,9 @@ let getPixels = () => { '\');'; }; -describe('AolAdapter', () => { - const MARKETPLACE_URL = '//adserver-us.adtech.advertising.com/pubapi/3.0/'; - const NEXAGE_URL = '//hb.nexage.com/bidRequest?'; +describe('AolAdapter', function () { + const MARKETPLACE_URL = 'https://adserver-us.adtech.advertising.com/pubapi/3.0/'; + const NEXAGE_URL = 'https://c2shb.ssp.yahoo.com/bidRequest?'; const ONE_DISPLAY_TTL = 60; const ONE_MOBILE_TTL = 3600; @@ -92,15 +92,14 @@ describe('AolAdapter', () => { return bidderRequest; } - describe('interpretResponse()', () => { + describe('interpretResponse()', function () { let bidderSettingsBackup; let bidResponse; let bidRequest; let logWarnSpy; - let formatPixelsStub; let isOneMobileBidderStub; - beforeEach(() => { + beforeEach(function () { bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; bidRequest = { bidderCode: 'test-bidder-code', @@ -111,18 +110,16 @@ describe('AolAdapter', () => { body: getDefaultBidResponse() }; logWarnSpy = sinon.spy(utils, 'logWarn'); - formatPixelsStub = sinon.stub(spec, 'formatPixels'); isOneMobileBidderStub = sinon.stub(spec, 'isOneMobileBidder'); }); - afterEach(() => { + afterEach(function () { $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; logWarnSpy.restore(); - formatPixelsStub.restore(); isOneMobileBidderStub.restore(); }); - it('should return formatted bid response with required properties', () => { + it('should return formatted bid response with required properties', function () { let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); expect(formattedBidResponse).to.deep.equal({ bidderCode: bidRequest.bidderCode, @@ -139,47 +136,26 @@ describe('AolAdapter', () => { ttl: bidRequest.ttl }); }); - - it('should add pixels to ad content when pixels are present in the response', () => { - bidResponse.body.ext = { - pixels: 'pixels-content' - }; - - formatPixelsStub.returns('pixels-content'); - let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); - - expect(formattedBidResponse.ad).to.equal(DEFAULT_AD_CONTENT + 'pixels-content'); - }); - - it('should show warning in the console', function() { - $$PREBID_GLOBAL$$.bidderSettings = { - aol: { - bidCpmAdjustment: function() {} - } - }; - spec.interpretResponse(bidResponse, bidRequest); - expect(utils.logWarn.calledOnce).to.be.true; - }); }); - describe('buildRequests()', () => { - it('method exists and is a function', () => { + describe('buildRequests()', function () { + it('method exists and is a function', function () { expect(spec.buildRequests).to.exist.and.to.be.a('function'); }); - describe('Marketplace', () => { - it('should not return request when no bids are present', () => { + describe('Marketplace', function () { + it('should not return request when no bids are present', function () { let [request] = spec.buildRequests([]); - expect(request).to.be.empty; + expect(request).to.be.undefined; }); - it('should return request for Marketplace endpoint', () => { + it('should return request for Marketplace endpoint', function () { let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(MARKETPLACE_URL); + expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); }); - it('should return request for Marketplace via onedisplay bidder code', () => { + it('should return request for Marketplace via onedisplay bidder code', function () { let bidRequest = createCustomBidRequest({ bids: [{ bidder: 'onedisplay' @@ -188,7 +164,7 @@ describe('AolAdapter', () => { }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(MARKETPLACE_URL); + expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); }); it('should return Marketplace request via onedisplay bidder code when' + @@ -202,7 +178,7 @@ describe('AolAdapter', () => { }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(MARKETPLACE_URL); + expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); }); it('should return Marketplace request via onedisplay bidder code when' + @@ -216,7 +192,7 @@ describe('AolAdapter', () => { }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(MARKETPLACE_URL); + expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); }); it('should not resolve endpoint for onedisplay bidder code ' + @@ -230,10 +206,10 @@ describe('AolAdapter', () => { }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request).to.be.empty; + expect(request).to.be.undefined; }); - it('should return Marketplace URL for eu region', () => { + it('should return Marketplace URL for eu region', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -242,10 +218,37 @@ describe('AolAdapter', () => { } }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('adserver-eu.adtech.advertising.com/pubapi/3.0/'); + expect(request.url.indexOf('https://adserver-eu.adtech.advertising.com/pubapi/3.0/')) + .to.equal(0); + }); + + it('should return insecure MP URL if insecure server option is present', function () { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + server: 'http://adserver-eu.adtech.advertising.com' + } + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url.indexOf('http://adserver-eu.adtech.advertising.com/pubapi/3.0/')) + .to.equal(0); + }); + + it('should return a secure MP URL if relative proto server option is present', function () { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + server: '//adserver-eu.adtech.advertising.com' + } + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url.indexOf('https://adserver-eu.adtech.advertising.com/pubapi/3.0/')) + .to.equal(0); }); - it('should return Marketplace URL for eu region when server option is present', () => { + it('should return a secure MP URL when server option without protocol is present', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -254,10 +257,11 @@ describe('AolAdapter', () => { } }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('adserver-eu.adtech.advertising.com/pubapi/3.0/'); + expect(request.url.indexOf('https://adserver-eu.adtech.advertising.com/pubapi/3.0/')) + .to.equal(0); }); - it('should return default Marketplace URL in case of unknown region config option', () => { + it('should return default Marketplace URL in case of unknown region config option', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -266,28 +270,28 @@ describe('AolAdapter', () => { } }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(MARKETPLACE_URL); + expect(request.url.indexOf(MARKETPLACE_URL)).to.equal(0); }); - it('should return url with pubapi bid option', () => { + it('should return url with pubapi bid option', function () { let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); expect(request.url).to.contain('cmd=bid;'); }); - it('should return url with version 2 of pubapi', () => { + it('should return url with version 2 of pubapi', function () { let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); expect(request.url).to.contain('v=2;'); }); - it('should return url with cache busting option', () => { + it('should return url with cache busting option', function () { let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); expect(request.url).to.match(/misc=\d+/); }); - it('should return url with default pageId and sizeId', () => { + it('should return url with default pageId and sizeId', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -298,7 +302,7 @@ describe('AolAdapter', () => { expect(request.url).to.contain('/pubapi/3.0/9599.1/1234567/0/0/ADTECH;'); }); - it('should return url with custom pageId and sizeId when options are present', () => { + it('should return url with custom pageId and sizeId when options are present', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -311,13 +315,13 @@ describe('AolAdapter', () => { expect(request.url).to.contain('/pubapi/3.0/9599.1/1234567/1111/2222/ADTECH;'); }); - it('should return url with default alias if alias param is missing', () => { + it('should return url with default alias if alias param is missing', function () { let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); expect(request.url).to.match(/alias=\w+?;/); }); - it('should return url with custom alias if it is present', () => { + it('should return url with custom alias if it is present', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -329,13 +333,13 @@ describe('AolAdapter', () => { expect(request.url).to.contain('alias=desktop_articlepage_something_box_300_250'); }); - it('should return url without bidfloor option if is is missing', () => { + it('should return url without bidfloor option if is is missing', function () { let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); expect(request.url).not.to.contain('bidfloor='); }); - it('should return url with bidFloor option if it is present', () => { + it('should return url with bidFloor option if it is present', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -347,7 +351,7 @@ describe('AolAdapter', () => { expect(request.url).to.contain('bidfloor=0.8'); }); - it('should return url with key values if keyValues param is present', () => { + it('should return url with key values if keyValues param is present', function () { let bidRequest = createCustomBidRequest({ params: { placement: 1234567, @@ -363,7 +367,7 @@ describe('AolAdapter', () => { expect(request.url).to.contain('kvage=25;kvheight=3.42;kvtest=key'); }); - it('should return request object for One Display when configuration is present', () => { + it('should return request object for One Display when configuration is present', function () { let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); expect(request.method).to.equal('GET'); @@ -371,8 +375,8 @@ describe('AolAdapter', () => { }); }); - describe('One Mobile', () => { - it('should return One Mobile url when One Mobile get params are present', () => { + describe('One Mobile', function () { + it('should return One Mobile url when One Mobile get params are present', function () { let bidRequest = createCustomBidRequest({ params: getNexageGetBidParams() }); @@ -380,18 +384,18 @@ describe('AolAdapter', () => { expect(request.url).to.contain(NEXAGE_URL); }); - it('should return One Mobile url with different host when host option is present', () => { + it('should return One Mobile url with different host when host option is present', function () { let bidParams = Object.assign({ - host: 'qa-hb.nexage.com' + host: 'http://qa-hb.nexage.com' }, getNexageGetBidParams()); let bidRequest = createCustomBidRequest({ params: bidParams }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('qa-hb.nexage.com/bidRequest?'); + expect(request.url).to.contain('http://qa-hb.nexage.com/bidRequest?'); }); - it('should return One Mobile url when One Mobile and Marketplace params are present', () => { + it('should return One Mobile url when One Mobile and Marketplace params are present', function () { let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); let bidRequest = createCustomBidRequest({ params: bidParams @@ -422,10 +426,10 @@ describe('AolAdapter', () => { params: getMarketplaceBidParams() }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request).to.be.empty; + expect(request).to.be.undefined; }); - it('should return One Mobile url with required params - dcn & pos', () => { + it('should return One Mobile url with required params - dcn & pos', function () { let bidRequest = createCustomBidRequest({ params: getNexageGetBidParams() }); @@ -433,7 +437,7 @@ describe('AolAdapter', () => { expect(request.url).to.contain(NEXAGE_URL + 'dcn=2c9d2b50015c5ce9db6aeeed8b9500d6&pos=header'); }); - it('should return One Mobile url with cmd=bid option', () => { + it('should return One Mobile url with cmd=bid option', function () { let bidRequest = createCustomBidRequest({ params: getNexageGetBidParams() }); @@ -441,7 +445,7 @@ describe('AolAdapter', () => { expect(request.url).to.contain('cmd=bid'); }); - it('should return One Mobile url with generic params if ext option is present', () => { + it('should return One Mobile url with generic params if ext option is present', function () { let bidRequest = createCustomBidRequest({ params: { dcn: '54321123', @@ -455,11 +459,11 @@ describe('AolAdapter', () => { } }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('hb.nexage.com/bidRequest?dcn=54321123&pos=footer-2324&cmd=bid' + + expect(request.url).to.equal('https://c2shb.ssp.yahoo.com/bidRequest?dcn=54321123&pos=footer-2324&cmd=bid' + '¶m1=val1¶m2=val2¶m3=val3¶m4=val4'); }); - it('should return request object for One Mobile POST endpoint when POST configuration is present', () => { + it('should return request object for One Mobile POST endpoint when POST configuration is present', function () { let bidConfig = getNexagePostBidParams(); let bidRequest = createCustomBidRequest({ params: bidConfig @@ -479,113 +483,83 @@ describe('AolAdapter', () => { }); it('should not return request object for One Mobile POST endpoint' + - 'if required parameterers are missed', () => { + 'if required parameters are missed', () => { let bidRequest = createCustomBidRequest({ params: { imp: [] } }); let [request] = spec.buildRequests(bidRequest.bids); - expect(request).to.be.empty; + expect(request).to.be.undefined; }); }); }); - describe('getUserSyncs()', () => { + describe('getUserSyncs()', function () { + let serverResponses; let bidResponse; - let bidRequest; - beforeEach(() => { - $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = false; - config.setConfig({ - aol: { - userSyncOn: 'bidResponse' - }, - }); + beforeEach(function () { bidResponse = getDefaultBidResponse(); bidResponse.ext = { pixels: getPixels() }; + + serverResponses = [ + {body: bidResponse} + ]; }); - it('should return user syncs only if userSyncOn equals to "bidResponse"', () => { - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + it('should return user syncs if pixels are present in the response', function () { + let userSyncs = spec.getUserSyncs({}, serverResponses); - expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; expect(userSyncs).to.deep.equal([ {type: 'image', url: 'img.org'}, {type: 'iframe', url: 'pixels1.org'} ]); }); - it('should not return user syncs if it has already been returned', () => { - $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; - - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); - - expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; - expect(userSyncs).to.deep.equal([]); - }); - - it('should not return user syncs if pixels are not present', () => { + it('should not return user syncs if pixels are not present', function () { bidResponse.ext.pixels = null; + let userSyncs = spec.getUserSyncs({}, serverResponses); - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); - - expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.false; expect(userSyncs).to.deep.equal([]); }); }); - describe('formatPixels()', () => { - it('should return pixels wrapped for dropping them once and within nested frames ', () => { - let pixels = ''; - let formattedPixels = spec.formatPixels(pixels); - - expect(formattedPixels).to.equal( - ''); - }); - }); - - describe('isOneMobileBidder()', () => { + describe('isOneMobileBidder()', function () { it('should return false when when bidderCode is not present', () => { expect(spec.isOneMobileBidder(null)).to.be.false; }); - it('should return false for unknown bidder code', () => { + it('should return false for unknown bidder code', function () { expect(spec.isOneMobileBidder('unknownBidder')).to.be.false; }); - it('should return true for aol bidder code', () => { + it('should return true for aol bidder code', function () { expect(spec.isOneMobileBidder('aol')).to.be.true; }); - it('should return true for one mobile bidder code', () => { + it('should return true for one mobile bidder code', function () { expect(spec.isOneMobileBidder('onemobile')).to.be.true; }); }); - describe('isConsentRequired()', () => { - it('should return false when consentData object is not present', () => { + describe('isConsentRequired()', function () { + it('should return false when consentData object is not present', function () { expect(spec.isConsentRequired(null)).to.be.false; }); - it('should return false when gdprApplies equals true and consentString is not present', () => { + it('should return true when gdprApplies equals true and consentString is not present', function () { let consentData = { consentString: null, gdprApplies: true }; - expect(spec.isConsentRequired(consentData)).to.be.false; + expect(spec.isConsentRequired(consentData)).to.be.true; }); - it('should return false when consentString is present and gdprApplies equals false', () => { + it('should return false when consentString is present and gdprApplies equals false', function () { let consentData = { consentString: 'consent-string', gdprApplies: false @@ -594,7 +568,7 @@ describe('AolAdapter', () => { expect(spec.isConsentRequired(consentData)).to.be.false; }); - it('should return true when consentString is present and gdprApplies equals true', () => { + it('should return true when consentString is present and gdprApplies equals true', function () { let consentData = { consentString: 'consent-string', gdprApplies: true @@ -604,50 +578,68 @@ describe('AolAdapter', () => { }); }); - describe('formatMarketplaceConsentData()', () => { - let consentRequiredStub; + describe('formatMarketplaceDynamicParams()', function () { + let formatConsentDataStub; + let formatKeyValuesStub; - beforeEach(() => { - consentRequiredStub = sinon.stub(spec, 'isConsentRequired'); + beforeEach(function () { + formatConsentDataStub = sinon.stub(spec, 'formatConsentData'); + formatKeyValuesStub = sinon.stub(spec, 'formatKeyValues'); }); - afterEach(() => { - consentRequiredStub.restore(); + afterEach(function () { + formatConsentDataStub.restore(); + formatKeyValuesStub.restore(); }); - it('should return empty string when consent is not required', () => { - consentRequiredStub.returns(false); - expect(spec.formatMarketplaceConsentData()).to.be.equal(''); + it('should return empty string when params are not present', function () { + expect(spec.formatMarketplaceDynamicParams()).to.be.equal(''); }); - it('should return formatted consent data when consent is required', () => { - consentRequiredStub.returns(true); - let formattedConsentData = spec.formatMarketplaceConsentData({ - consentString: 'test-consent' + it('should return formatted params when formatConsentData returns data', function () { + formatConsentDataStub.returns({ + euconsent: 'test-consent', + gdpr: 1 + }); + expect(spec.formatMarketplaceDynamicParams()).to.be.equal('euconsent=test-consent;gdpr=1;'); + }); + + it('should return formatted params when formatKeyValues returns data', function () { + formatKeyValuesStub.returns({ + param1: 'val1', + param2: 'val2', + param3: 'val3' }); - expect(formattedConsentData).to.be.equal(';euconsent=test-consent;gdpr=1'); + expect(spec.formatMarketplaceDynamicParams()).to.be.equal('param1=val1;param2=val2;param3=val3;'); + }); + + it('should return formatted bid floor param when it is present', function () { + let params = { + bidFloor: 0.45 + }; + expect(spec.formatMarketplaceDynamicParams(params)).to.be.equal('bidfloor=0.45;'); }); }); - describe('formatOneMobileDynamicParams()', () => { + describe('formatOneMobileDynamicParams()', function () { let consentRequiredStub; let secureProtocolStub; - beforeEach(() => { + beforeEach(function () { consentRequiredStub = sinon.stub(spec, 'isConsentRequired'); secureProtocolStub = sinon.stub(spec, 'isSecureProtocol'); }); - afterEach(() => { + afterEach(function () { consentRequiredStub.restore(); secureProtocolStub.restore(); }); - it('should return empty string when params are not present', () => { + it('should return empty string when params are not present', function () { expect(spec.formatOneMobileDynamicParams()).to.be.equal(''); }); - it('should return formatted params when params are present', () => { + it('should return formatted params when params are present', function () { let params = { param1: 'val1', param2: 'val2', @@ -656,15 +648,15 @@ describe('AolAdapter', () => { expect(spec.formatOneMobileDynamicParams(params)).to.contain('¶m1=val1¶m2=val2¶m3=val3'); }); - it('should return formatted gdpr params when isConsentRequired returns true', () => { + it('should return formatted gdpr params when isConsentRequired returns true', function () { let consentData = { consentString: 'test-consent' }; consentRequiredStub.returns(true); - expect(spec.formatOneMobileDynamicParams({}, consentData)).to.be.equal('&euconsent=test-consent&gdpr=1'); + expect(spec.formatOneMobileDynamicParams({}, consentData)).to.be.equal('&gdpr=1&euconsent=test-consent'); }); - it('should return formatted secure param when isSecureProtocol returns true', () => { + it('should return formatted secure param when isSecureProtocol returns true', function () { secureProtocolStub.returns(true); expect(spec.formatOneMobileDynamicParams()).to.be.equal('&secure=1'); }); diff --git a/test/spec/modules/appierAnalyticsAdapter_spec.js b/test/spec/modules/appierAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..07b9796a4cb --- /dev/null +++ b/test/spec/modules/appierAnalyticsAdapter_spec.js @@ -0,0 +1,709 @@ +import { + appierAnalyticsAdapter, getCpmInUsd, parseBidderCode, parseAdUnitCode, + ANALYTICS_VERSION, BIDDER_STATUS +} from 'modules/appierAnalyticsAdapter'; +import {expect} from 'chai'; +const events = require('src/events'); +const constants = require('src/constants.json'); + +const affiliateId = 'WhctHaViHtI'; +const configId = 'd9cc9a9be9b240eda17cf1c9a8a4b29c'; +const serverUrl = 'https://analytics.server.url/v1'; +const autoPick = 'none'; +const predictionId = '2a91ca5de54a4a2e89950af439f7a27f'; +const auctionId = 'b0b39610-b941-4659-a87c-de9f62d3e13e'; + +describe('Appier Prebid AnalyticsAdapter Testing', function () { + describe('event tracking and message cache manager', function () { + let xhr; + + beforeEach(function () { + const configOptions = { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + predictionId: predictionId, + sampling: 0, + adSampling: 1, + }; + + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + xhr = sinon.useFakeXMLHttpRequest(); + }); + + afterEach(function () { + appierAnalyticsAdapter.disableAnalytics(); + xhr.restore(); + }); + + describe('#getCpmInUsd()', function() { + it('should get bid cpm as currency is USD', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'APPIER', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ] + const result = getCpmInUsd(receivedBids[0]); + expect(result).to.equal(0.1); + }); + }); + + describe('#parseBidderCode()', function() { + it('should get lower case bidder code from bidderCode field value', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'APPIER', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ]; + const result = parseBidderCode(receivedBids[0]); + expect(result).to.equal('appier'); + }); + it('should get lower case bidder code from bidder field value as bidderCode field is missing', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'APPIER', + bidderCode: '', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ]; + const result = parseBidderCode(receivedBids[0]); + expect(result).to.equal('appier'); + }); + }); + + describe('#parseAdUnitCode()', function() { + it('should get lower case adUnit code from adUnitCode field value', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'ADUNIT', + bidder: 'appier', + bidderCode: 'APPIER', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ]; + const result = parseAdUnitCode(receivedBids[0]); + expect(result).to.equal('adunit'); + }); + }); + + describe('#getCachedAuction()', function() { + const existing = {timeoutBids: [{}]}; + appierAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing; + + it('should get the existing cached object if it exists', function() { + const result = appierAnalyticsAdapter.getCachedAuction('test_auction_id'); + + expect(result).to.equal(existing); + }); + + it('should create a new object and store it in the cache on cache miss', function() { + const result = appierAnalyticsAdapter.getCachedAuction('no_such_id'); + + expect(result).to.deep.include({ + timeoutBids: [], + }); + }); + }); + + describe('when formatting JSON payload sent to backend', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'reippa', + bidderCode: 'reippa', + requestId: 'b2c3d4e5', + timeToRespond: 100, + cpm: 0.08, + currency: 'USD', + originalCpm: 0.08, + originalCurrency: 'USD', + ad: 'fake ad2' + }, + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: 'USD', + ad: 'fake ad3' + }, + ]; + const highestCpmBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'appier', + // No requestId + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + } + ]; + const noBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + bidId: 'a1b2c3d4', + } + ]; + const timeoutBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'reippa', + bidderCode: 'reippa', + bidId: '00123d4c', + } + ]; + const withoutOriginalCpmBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.29, + currency: 'USD', + originalCpm: '', + originalCurrency: 'USD', + ad: 'fake ad3' + }, + ]; + const withoutOriginalCurrencyBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: '', + ad: 'fake ad3' + }, + ]; + function assertHavingRequiredMessageFields(message) { + expect(message).to.include({ + version: ANALYTICS_VERSION, + auctionId: auctionId, + affiliateId: affiliateId, + configId: configId, + // referrer: window.location.href, + sampling: 0, + adSampling: 1, + prebid: '$prebid.version$', + // autoPick: 'manual', + }); + } + + describe('#createCommonMessage', function() { + it('should correctly serialize some common fields', function() { + const message = appierAnalyticsAdapter.createCommonMessage(auctionId); + + assertHavingRequiredMessageFields(message); + }); + }); + + describe('#serializeBidResponse', function() { + it('should handle BID properly and serialize bid price related fields', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID); + + expect(result).to.include({ + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + }); + }); + + it('should handle NO_BID properly and set status to noBid', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.NO_BID); + + expect(result).to.include({ + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.NO_BID, + }); + }); + + it('should handle BID_WON properly and serialize bid price related fields', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID_WON); + + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + }); + }); + + it('should handle TIMEOUT properly and set status to timeout and isTimeout to true', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.TIMEOUT); + + expect(result).to.include({ + prebidWon: false, + isTimeout: true, + status: BIDDER_STATUS.TIMEOUT, + }); + }); + + it('should handle BID_WON properly and fill originalCpm field with cpm in missing originalCpm case', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(withoutOriginalCpmBids[0], BIDDER_STATUS.BID_WON); + + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 120, + cpm: 0.29, + currency: 'USD', + originalCpm: 0.29, + originalCurrency: 'USD', + cpmUsd: 0.29, + }); + }); + + it('should handle BID_WON properly and fill originalCurrency field with currency in missing originalCurrency case', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(withoutOriginalCurrencyBids[0], BIDDER_STATUS.BID_WON); + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: 'USD', + cpmUsd: 0.09, + }); + }); + }); + + describe('#addBidResponseToMessage()', function() { + it('should add a bid response in the output message, grouped by adunit_id and bidder', function() { + const message = { + adUnits: {} + }; + appierAnalyticsAdapter.addBidResponseToMessage(message, noBids[0], BIDDER_STATUS.NO_BID); + + expect(message.adUnits).to.deep.include({ + 'adunit_2': { + 'appier': { + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.NO_BID, + } + } + }); + }); + }); + + describe('#createBidMessage()', function() { + it('should format auction message sent to the backend', function() { + const args = { + auctionId: auctionId, + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + adUnitCodes: ['adunit_1', 'adunit_2'], + bidsReceived: receivedBids, + noBids: noBids + }; + + const result = appierAnalyticsAdapter.createBidMessage(args, highestCpmBids, timeoutBids); + + assertHavingRequiredMessageFields(result); + expect(result).to.deep.include({ + auctionElapsed: 100, + timeout: 3000, + adUnits: { + 'adunit_1': { + 'appier': { + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + }, + 'reippa': { + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 100, + cpm: 0.08, + currency: 'USD', + originalCpm: 0.08, + originalCurrency: 'USD', + cpmUsd: 0.08, + } + }, + 'adunit_2': { + // this bid result exists in both bid and noBid arrays and should be treated as bid + 'appier': { + prebidWon: false, + isTimeout: false, + time: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: 'USD', + cpmUsd: 0.09, + status: BIDDER_STATUS.BID, + }, + 'reippa': { + prebidWon: false, + isTimeout: true, + status: BIDDER_STATUS.TIMEOUT, + } + } + } + }); + }); + }); + + describe('#createImpressionMessage()', function() { + it('should format message sent to the backend with the bid result', function() { + const bid = receivedBids[0]; + const result = appierAnalyticsAdapter.createImpressionMessage(bid); + + assertHavingRequiredMessageFields(result); + expect(result.adUnits).to.deep.include({ + 'adunit_1': { + 'appier': { + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + } + } + }); + }); + }); + + describe('#createCreativeMessage()', function() { + it('should generate message sent to the backend with ad html grouped by adunit and bidder', function() { + const result = appierAnalyticsAdapter.createCreativeMessage(auctionId, receivedBids); + + assertHavingRequiredMessageFields(result); + expect(result.adUnits).to.deep.include({ + 'adunit_1': { + 'appier': { + ad: 'fake ad1' + }, + 'reippa': { + ad: 'fake ad2' + }, + }, + 'adunit_2': { + 'appier': { + ad: 'fake ad3' + } + } + }); + }); + }); + describe('#handleBidTimeout()', function() { + it('should cached the timeout bid as BID_TIMEOUT event was triggered', function() { + appierAnalyticsAdapter.cachedAuctions['test_timeout_auction_id'] = { 'timeoutBids': [] }; + const args = [{ + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + bidsReceived: receivedBids, + noBids: noBids + }]; + + appierAnalyticsAdapter.handleBidTimeout(args); + const result = appierAnalyticsAdapter.getCachedAuction('test_timeout_auction_id'); + expect(result).to.deep.include({ + timeoutBids: [{ + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + bidsReceived: receivedBids, + noBids: noBids + }] + }); + }); + }); + describe('#handleBidWon()', function() { + it('should call createImpressionMessage once as BID_WON event was triggered', function() { + sinon.spy(appierAnalyticsAdapter, 'createImpressionMessage'); + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ] + + appierAnalyticsAdapter.handleBidWon(receivedBids[0]); + sinon.assert.callCount(appierAnalyticsAdapter.createImpressionMessage, 1); + appierAnalyticsAdapter.createImpressionMessage.restore(); + }); + }); + }); + }); + + describe('Appier Analytics Adapter track handler ', function () { + const configOptions = { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + sampling: 1, + adSampling: 1, + }; + + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + }); + + afterEach(function () { + appierAnalyticsAdapter.disableAnalytics(); + events.getEvents.restore(); + }); + + it('should call handleBidWon as BID_WON trigger event', function() { + sinon.spy(appierAnalyticsAdapter, 'handleBidWon'); + events.emit(constants.EVENTS.BID_WON, {}); + sinon.assert.callCount(appierAnalyticsAdapter.handleBidWon, 1); + appierAnalyticsAdapter.handleBidWon.restore(); + }); + + it('should call handleBidTimeout as BID_TIMEOUT trigger event', function() { + sinon.spy(appierAnalyticsAdapter, 'handleBidTimeout'); + events.emit(constants.EVENTS.BID_TIMEOUT, {}); + sinon.assert.callCount(appierAnalyticsAdapter.handleBidTimeout, 1); + appierAnalyticsAdapter.handleBidTimeout.restore(); + }); + + it('should call handleAuctionEnd as AUCTION_END trigger event', function() { + sinon.spy(appierAnalyticsAdapter, 'handleAuctionEnd'); + events.emit(constants.EVENTS.AUCTION_END, {}); + sinon.assert.callCount(appierAnalyticsAdapter.handleAuctionEnd, 1); + appierAnalyticsAdapter.handleAuctionEnd.restore(); + }); + + it('should call createCreativeMessage as AUCTION_END trigger event in adSampled is true', function() { + const configOptions = { + options: { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + sampling: 1, + adSampling: 1, + adSampled: true, + } + }; + const args = { + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + }; + appierAnalyticsAdapter.initConfig(configOptions); + sinon.stub(appierAnalyticsAdapter, 'sendEventMessage').returns({}); + sinon.stub(appierAnalyticsAdapter, 'createBidMessage').returns({}); + sinon.spy(appierAnalyticsAdapter, 'createCreativeMessage'); + events.emit(constants.EVENTS.AUCTION_END, args); + sinon.assert.callCount(appierAnalyticsAdapter.createCreativeMessage, 1); + appierAnalyticsAdapter.sendEventMessage.restore(); + appierAnalyticsAdapter.createBidMessage.restore(); + appierAnalyticsAdapter.createCreativeMessage.restore(); + }); + }); + + describe('enableAnalytics and config parser', function () { + const configOptions = { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + sampling: 0, + adSampling: 1, + }; + + beforeEach(function () { + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + }); + + afterEach(function () { + appierAnalyticsAdapter.disableAnalytics(); + }); + + it('should parse config correctly with optional values', function () { + expect(appierAnalyticsAdapter.getAnalyticsOptions().options).to.deep.equal(configOptions); + expect(appierAnalyticsAdapter.getAnalyticsOptions().affiliateId).to.equal(configOptions.affiliateId); + expect(appierAnalyticsAdapter.getAnalyticsOptions().configId).to.equal(configOptions.configId); + expect(appierAnalyticsAdapter.getAnalyticsOptions().server).to.equal(configOptions.server); + expect(appierAnalyticsAdapter.getAnalyticsOptions().autoPick).to.equal(configOptions.autoPick); + expect(appierAnalyticsAdapter.getAnalyticsOptions().sampled).to.equal(false); + expect(appierAnalyticsAdapter.getAnalyticsOptions().adSampled).to.equal(true); + }); + + it('should not enable Analytics when affiliateId is missing', function () { + const configOptions = { + options: { + configId: configId + } + }; + const validConfig = appierAnalyticsAdapter.initConfig(configOptions); + expect(validConfig).to.equal(false); + }); + + it('should use DEFAULT_SERVER when server is missing', function () { + const configOptions = { + options: { + configId: configId, + affiliateId: affiliateId + } + }; + appierAnalyticsAdapter.initConfig(configOptions); + expect(appierAnalyticsAdapter.getAnalyticsOptions().server).to.equal('https://prebid-analytics.c.appier.net/v1'); + }); + + it('should use null when autoPick is missing', function () { + const configOptions = { + options: { + configId: configId, + affiliateId: affiliateId + } + }; + appierAnalyticsAdapter.initConfig(configOptions); + expect(appierAnalyticsAdapter.getAnalyticsOptions().autoPick).to.equal(null); + }); + + it('should not enable Analytics when configId is missing', function () { + const configOptions = { + options: { + affiliateId: affiliateId + } + }; + const validConfig = appierAnalyticsAdapter.initConfig(configOptions); + expect(validConfig).to.equal(false); + }); + + it('should fall back to default value when sampling factor is not number', function () { + const configOptions = { + options: { + affiliateId: affiliateId, + configId: configId, + sampling: 'string', + adSampling: 'string' + } + }; + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + + expect(appierAnalyticsAdapter.getAnalyticsOptions().sampled).to.equal(false); + expect(appierAnalyticsAdapter.getAnalyticsOptions().adSampled).to.equal(true); + }); + }); +}); diff --git a/test/spec/modules/appierBidAdapter_spec.js b/test/spec/modules/appierBidAdapter_spec.js new file mode 100644 index 00000000000..c7fc5744d1c --- /dev/null +++ b/test/spec/modules/appierBidAdapter_spec.js @@ -0,0 +1,174 @@ +import { expect } from 'chai'; +import { spec, API_SERVERS_MAP, ADAPTER_VERSION } from 'modules/appierBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; + +describe('AppierAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'appier', + 'params': { + 'hzid': 'abcd' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params zoneId found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required param zoneId is missing', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required param zoneId has wrong type', function () { + let bid = Object.assign({}, bid); + bid.params = { + 'hzid': null + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + it('should return an empty list when there are no bid requests', function() { + const fakeBidRequests = []; + const fakeBidderRequest = {}; + expect(spec.buildRequests(fakeBidRequests, fakeBidderRequest)).to.be.an('array').that.is.empty; + }); + + it('should generate a POST bid request with method, url, and data fields', function() { + const bid = { + 'bidder': 'appier', + 'params': { + 'hzid': 'abcd' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + const fakeBidRequests = [bid]; + const fakeBidderRequest = {refererInfo: { + 'referer': 'fakeReferer', + 'reachedTop': true, + 'numIframes': 1, + 'stack': [] + }}; + + const builtRequests = spec.buildRequests(fakeBidRequests, fakeBidderRequest); + expect(builtRequests.length).to.equal(1); + expect(builtRequests[0].method).to.equal('POST'); + expect(builtRequests[0].url).match(/v1\/prebid\/bid/); + expect(builtRequests[0].data).deep.equal({ + 'bids': fakeBidRequests, + 'refererInfo': fakeBidderRequest.refererInfo, + 'version': ADAPTER_VERSION + }); + }); + }); + + describe('interpretResponse', function() { + const bid = { + 'bidder': 'appier', + 'params': { + 'hzid': 'abcd' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + const fakeBidRequests = [bid]; + + it('should return an empty aray to indicate no valid bids', function() { + const fakeServerResponse = {}; + + const bidResponses = spec.interpretResponse(fakeServerResponse, fakeBidRequests); + + expect(bidResponses).is.an('array').that.is.empty; + }); + + it('should generate correct response array for bidder', function() { + const fakeBidResult = { + 'requestId': '30b31c1838de1e', + 'cpm': 0.0029346001, + 'creativeId': 'Idl0P0d5S3Ca5kVWcia-wQ', + 'width': 300, + 'height': 250, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'ad': '
    fake html
    ', + 'appierParams': { + 'hzid': 'test_hzid' + } + }; + const fakeServerResponse = { + headers: [], + body: [fakeBidResult] + }; + + const bidResponses = spec.interpretResponse(fakeServerResponse, fakeBidRequests); + expect(bidResponses).deep.equal([fakeBidResult]); + }); + }); + + describe('getApiServer', function() { + it('should use the server specified by setConfig(appier.server)', function() { + config.setConfig({ + 'appier': {'server': 'fake_server'} + }); + + const server = spec.getApiServer(); + + expect(server).equals('fake_server'); + }); + + it('should retrieve a farm specific hostname if server is not specpfied', function() { + config.setConfig({ + 'appier': {'farm': 'tw'} + }); + + const server = spec.getApiServer(); + + expect(server).equals(API_SERVERS_MAP['tw']); + }); + + it('if farm is not recognized, use the default farm', function() { + config.setConfig({ + 'appier': {'farm': 'no_this_farm'} + }); + + const server = spec.getApiServer(); + + expect(server).equals(API_SERVERS_MAP['default']); + }); + + it('if farm is not specified, use the default farm', function() { + config.setConfig({ + 'appier': {} + }); + + const server = spec.getApiServer(); + + expect(server).equals(API_SERVERS_MAP['default']); + }); + }); +}); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index abfd50d1746..e35560ac1d0 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1,20 +1,23 @@ import { expect } from 'chai'; import { spec } from 'modules/appnexusBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; +import * as bidderFactory from 'src/adapters/bidderFactory'; +import { auctionManager } from 'src/auctionManager'; import { deepClone } from 'src/utils'; +import { config } from 'src/config'; const ENDPOINT = '//ib.adnxs.com/ut/v3/prebid'; -describe('AppNexusAdapter', () => { +describe('AppNexusAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'appnexus', 'params': { @@ -27,11 +30,11 @@ describe('AppNexusAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return true when required params found', () => { + it('should return true when required params found', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -42,7 +45,7 @@ describe('AppNexusAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -52,7 +55,8 @@ describe('AppNexusAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { + let getAdUnitsStub; let bidRequests = [ { 'bidder': 'appnexus', @@ -64,10 +68,21 @@ describe('AppNexusAdapter', () => { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + 'transactionId': '04f2659e-c005-4eb1-a57c-fa93145e3843' } ]; - it('should parse out private sizes', () => { + beforeEach(function() { + getAdUnitsStub = sinon.stub(auctionManager, 'getAdUnits').callsFake(function() { + return []; + }); + }); + + afterEach(function() { + getAdUnitsStub.restore(); + }); + + it('should parse out private sizes', function () { let bidRequest = Object.assign({}, bidRequests[0], { @@ -85,7 +100,7 @@ describe('AppNexusAdapter', () => { expect(payload.tags[0].private_sizes).to.deep.equal([{width: 300, height: 250}]); }); - it('should add source and verison to the tag', () => { + it('should add source and verison to the tag', function () { const request = spec.buildRequests(bidRequests); const payload = JSON.parse(request.data); expect(payload.sdk).to.exist; @@ -95,8 +110,28 @@ describe('AppNexusAdapter', () => { }); }); - it('should populate the ad_types array on all requests', () => { + it('should populate the ad_types array on all requests', function () { + let adUnits = [{ + code: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: '10433394' + } + }], + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' + }]; + ['banner', 'video', 'native'].forEach(type => { + getAdUnitsStub.callsFake(function(...args) { + return adUnits; + }); + const bidRequest = Object.assign({}, bidRequests[0]); bidRequest.mediaTypes = {}; bidRequest.mediaTypes[type] = {}; @@ -105,10 +140,22 @@ describe('AppNexusAdapter', () => { const payload = JSON.parse(request.data); expect(payload.tags[0].ad_types).to.deep.equal([type]); + + if (type === 'banner') { + delete adUnits[0].mediaTypes; + } }); }); - it('should populate the ad_types array on outstream requests', () => { + it('should not populate the ad_types array when adUnit.mediaTypes is undefined', function() { + const bidRequest = Object.assign({}, bidRequests[0]); + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.not.exist; + }); + + it('should populate the ad_types array on outstream requests', function () { const bidRequest = Object.assign({}, bidRequests[0]); bidRequest.mediaTypes = {}; bidRequest.mediaTypes.video = {context: 'outstream'}; @@ -119,13 +166,13 @@ describe('AppNexusAdapter', () => { expect(payload.tags[0].ad_types).to.deep.equal(['video']); }); - it('sends bid request to ENDPOINT via POST', () => { + it('sends bid request to ENDPOINT via POST', function () { const request = spec.buildRequests(bidRequests); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('POST'); }); - it('should attach valid video params to the tag', () => { + it('should attach valid video params to the tag', function () { let bidRequest = Object.assign({}, bidRequests[0], { @@ -148,14 +195,56 @@ describe('AppNexusAdapter', () => { }); }); - it('should attach valid user params to the tag', () => { + it('should add video property when adUnit includes a renderer', function () { + const videoData = { + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + }, + params: { + placementId: '10433394', + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + } + } + }; + + let bidRequest1 = deepClone(bidRequests[0]); + bidRequest1 = Object.assign({}, bidRequest1, videoData, { + renderer: { + url: 'http://test.renderer.url', + render: function () {} + } + }); + + let bidRequest2 = deepClone(bidRequests[0]); + bidRequest2.adUnitCode = 'adUnit_code_2'; + bidRequest2 = Object.assign({}, bidRequest2, videoData); + + const request = spec.buildRequests([bidRequest1, bidRequest2]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ + skippable: true, + playback_method: ['auto_play_sound_off'], + custom_renderer_present: true + }); + expect(payload.tags[1].video).to.deep.equal({ + skippable: true, + playback_method: ['auto_play_sound_off'] + }); + }); + + it('should attach valid user params to the tag', function () { let bidRequest = Object.assign({}, bidRequests[0], { params: { placementId: '10433394', user: { - external_uid: '123', + externalUid: '123', foobar: 'invalid' } } @@ -167,69 +256,204 @@ describe('AppNexusAdapter', () => { expect(payload.user).to.exist; expect(payload.user).to.deep.equal({ - external_uid: '123', + externalUid: '123', }); }); - it('should attach native params to the request', () => { + it('should duplicate adpod placements into batches and set correct maxduration', function() { let bidRequest = Object.assign({}, bidRequests[0], { - mediaType: 'native', - nativeParams: { - title: {required: true}, - body: {required: true}, - image: {required: true, sizes: [{ width: 100, height: 100 }]}, - cta: {required: false}, - sponsoredBy: {required: true} + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 300, + durationRangeSec: [15, 30], + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload1 = JSON.parse(request[0].data); + const payload2 = JSON.parse(request[1].data); + + // 300 / 15 = 20 total + expect(payload1.tags.length).to.equal(15); + expect(payload2.tags.length).to.equal(5); + + expect(payload1.tags[0]).to.deep.equal(payload1.tags[1]); + expect(payload1.tags[0].video.maxduration).to.equal(30); + + expect(payload2.tags[0]).to.deep.equal(payload1.tags[1]); + expect(payload2.tags[0].video.maxduration).to.equal(30); + }); + + it('should round down adpod placements when numbers are uneven', function() { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 123, + durationRangeSec: [45], + } } } ); const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); + expect(payload.tags.length).to.equal(2); + }); - expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - title: {required: true}, - description: {required: true}, - main_image: {required: true, sizes: [{ width: 100, height: 100 }]}, - ctatext: {required: false}, - sponsored_by: {required: true} - }); + it('should duplicate adpod placements when requireExactDuration is set', function() { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 300, + durationRangeSec: [15, 30], + requireExactDuration: true, + } + } + } + ); + + // 20 total placements with 15 max impressions = 2 requests + const request = spec.buildRequests([bidRequest]); + expect(request.length).to.equal(2); + + // 20 spread over 2 requests = 15 in first request, 5 in second + const payload1 = JSON.parse(request[0].data); + const payload2 = JSON.parse(request[1].data); + expect(payload1.tags.length).to.equal(15); + expect(payload2.tags.length).to.equal(5); + + // 10 placements should have max/min at 15 + // 10 placemenst should have max/min at 30 + const payload1tagsWith15 = payload1.tags.filter(tag => tag.video.maxduration === 15); + const payload1tagsWith30 = payload1.tags.filter(tag => tag.video.maxduration === 30); + expect(payload1tagsWith15.length).to.equal(10); + expect(payload1tagsWith30.length).to.equal(5); + + // 5 placemenst with min/max at 30 were in the first request + // so 5 remaining should be in the second + const payload2tagsWith30 = payload2.tags.filter(tag => tag.video.maxduration === 30); + expect(payload2tagsWith30.length).to.equal(5); }); - it('sets minimum native asset params when not provided on adunit', () => { + it('should set durations for placements when requireExactDuration is set and numbers are uneven', function() { let bidRequest = Object.assign({}, bidRequests[0], { - mediaType: 'native', - nativeParams: { - image: {required: true}, + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 105, + durationRangeSec: [15, 30, 60], + requireExactDuration: true, + } } } ); const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); + expect(payload.tags.length).to.equal(7); - expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - main_image: {required: true, sizes: [{}]}, - }); + const tagsWith15 = payload.tags.filter(tag => tag.video.maxduration === 15); + const tagsWith30 = payload.tags.filter(tag => tag.video.maxduration === 30); + const tagsWith60 = payload.tags.filter(tag => tag.video.maxduration === 60); + expect(tagsWith15.length).to.equal(3); + expect(tagsWith30.length).to.equal(3); + expect(tagsWith60.length).to.equal(1); + }); + + it('should break adpod request into batches', function() { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 225, + durationRangeSec: [5], + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload1 = JSON.parse(request[0].data); + const payload2 = JSON.parse(request[1].data); + const payload3 = JSON.parse(request[2].data); + + expect(payload1.tags.length).to.equal(15); + expect(payload2.tags.length).to.equal(15); + expect(payload3.tags.length).to.equal(15); }); - it('does not overwrite native ad unit params with mimimum params', () => { + it('adds brand_category_exclusion to request when set', function() { + let bidRequest = Object.assign({}, bidRequests[0]); + sinon + .stub(config, 'getConfig') + .withArgs('adpod.brandCategoryExclusion') + .returns(true); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.brand_category_uniqueness).to.equal(true); + + config.getConfig.restore(); + }); + + it('should attach native params to the request', function () { let bidRequest = Object.assign({}, bidRequests[0], { mediaType: 'native', nativeParams: { - image: { - aspect_ratios: [{ - min_width: 100, - ratio_width: 2, - ratio_height: 3, - }] - } + title: {required: true}, + body: {required: true}, + body2: {required: true}, + image: {required: true, sizes: [100, 100]}, + icon: {required: true}, + cta: {required: false}, + rating: {required: true}, + sponsoredBy: {required: true}, + privacyLink: {required: true}, + displayUrl: {required: true}, + address: {required: true}, + downloads: {required: true}, + likes: {required: true}, + phone: {required: true}, + price: {required: true}, + salePrice: {required: true} } } ); @@ -238,18 +462,51 @@ describe('AppNexusAdapter', () => { const payload = JSON.parse(request.data); expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - main_image: { - required: true, - aspect_ratios: [{ - min_width: 100, - ratio_width: 2, - ratio_height: 3, - }] - }, + title: {required: true}, + description: {required: true}, + desc2: {required: true}, + main_image: {required: true, sizes: [{ width: 100, height: 100 }]}, + icon: {required: true}, + ctatext: {required: false}, + rating: {required: true}, + sponsored_by: {required: true}, + privacy_link: {required: true}, + displayurl: {required: true}, + address: {required: true}, + downloads: {required: true}, + likes: {required: true}, + phone: {required: true}, + price: {required: true}, + saleprice: {required: true}, + privacy_supported: true }); }); - it('should convert keyword params to proper form and attaches to request', () => { + it('should always populated tags[].sizes with 1,1 for native if otherwise not defined', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + image: { required: true } + } + } + ); + bidRequest.sizes = [[150, 100], [300, 250]]; + + let request = spec.buildRequests([bidRequest]); + let payload = JSON.parse(request.data); + expect(payload.tags[0].sizes).to.deep.equal([{width: 150, height: 100}, {width: 300, height: 250}]); + + delete bidRequest.sizes; + + request = spec.buildRequests([bidRequest]); + payload = JSON.parse(request.data); + + expect(payload.tags[0].sizes).to.deep.equal([{width: 1, height: 1}]); + }); + + it('should convert keyword params to proper form and attaches to request', function () { let bidRequest = Object.assign({}, bidRequests[0], { @@ -261,6 +518,8 @@ describe('AppNexusAdapter', () => { singleArrNum: [5], multiValMixed: ['value1', 2, 'value3'], singleValNum: 123, + emptyStr: '', + emptyArr: [''], badValue: {'foo': 'bar'} // should be dropped } } @@ -285,10 +544,14 @@ describe('AppNexusAdapter', () => { }, { 'key': 'singleValNum', 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' }]); }); - it('should add payment rules to the request', () => { + it('should add payment rules to the request', function () { let bidRequest = Object.assign({}, bidRequests[0], { @@ -305,7 +568,7 @@ describe('AppNexusAdapter', () => { expect(payload.tags[0].use_pmt_rule).to.equal(true); }); - it('should add gdpr consent information to the request', () => { + it('should add gdpr consent information to the request', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { 'bidderCode': 'appnexus', @@ -326,9 +589,134 @@ describe('AppNexusAdapter', () => { expect(payload.gdpr_consent.consent_string).to.exist.and.to.equal(consentString); expect(payload.gdpr_consent.consent_required).to.exist.and.to.be.true; }); + + it('supports sending hybrid mobile app parameters', function () { + let appRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + app: { + id: 'B1O2W3M4AN.com.prebid.webview', + geo: { + lat: 40.0964439, + lng: -75.3009142 + }, + device_id: { + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', // Apple advertising identifier + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', // Android advertising identifier + md5udid: '5756ae9022b2ea1e47d84fead75220c8', // MD5 hash of the ANDROID_ID + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', // SHA1 hash of the ANDROID_ID + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' // Windows advertising identifier + } + } + } + } + ); + const request = spec.buildRequests([appRequest]); + const payload = JSON.parse(request.data); + expect(payload.app).to.exist; + expect(payload.app).to.deep.equal({ + appid: 'B1O2W3M4AN.com.prebid.webview' + }); + expect(payload.device.device_id).to.exist; + expect(payload.device.device_id).to.deep.equal({ + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', + md5udid: '5756ae9022b2ea1e47d84fead75220c8', + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' + }); + expect(payload.device.geo).to.exist; + expect(payload.device.geo).to.deep.equal({ + lat: 40.0964439, + lng: -75.3009142 + }); + }); + + it('should add referer info to payload', function () { + const bidRequest = Object.assign({}, bidRequests[0]) + const bidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2, + stack: [ + 'http://example.com/page.html', + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ] + } + } + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.referrer_detection).to.exist; + expect(payload.referrer_detection).to.deep.equal({ + rd_ref: 'http%3A%2F%2Fexample.com%2Fpage.html', + rd_top: true, + rd_ifs: 2, + rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') + }); + }); + + it('should populate tpids array when userId is available', function () { + const bidRequest = Object.assign({}, bidRequests[0], { + userId: { + criteortus: { + appnexus: { + userid: 'sample-userid' + } + } + } + }); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tpuids).to.deep.equal([{provider: 'criteo', user_id: 'sample-userid'}]); + }); + + it('should populate schain if available', function () { + const bidRequest = Object.assign({}, bidRequests[0], { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'blob.com', + 'sid': '001', + 'hp': 1 + } + ] + } + }); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.schain).to.deep.equal({ + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'blob.com', + 'sid': '001', + 'hp': 1 + } + ] + }); + }); }) - describe('interpretResponse', () => { + describe('interpretResponse', function () { + let bfStub; + before(function() { + bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); + }); + + after(function() { + bfStub.restore(); + }); + let response = { 'version': '3.0.0', 'tags': [ @@ -352,6 +740,9 @@ describe('AppNexusAdapter', () => { 'cpm_publisher_currency': 0.5, 'publisher_currency_code': '$', 'client_initiated_ad_counting': true, + 'viewability': { + 'config': '' + }, 'rtb': { 'banner': { 'content': '', @@ -373,7 +764,7 @@ describe('AppNexusAdapter', () => { ] }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { let expectedResponse = [ { 'requestId': '3db3773286ee59', @@ -387,17 +778,23 @@ describe('AppNexusAdapter', () => { 'currency': 'USD', 'ttl': 300, 'netRevenue': true, + 'adUnitCode': 'code', 'appnexus': { 'buyerMemberId': 958 } } ]; - let bidderRequest; + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = { 'version': '0.0.1', 'tags': [{ @@ -413,7 +810,7 @@ describe('AppNexusAdapter', () => { expect(result.length).to.equal(0); }); - it('handles non-banner media responses', () => { + it('handles outstream video responses', function () { let response = { 'tags': [{ 'uuid': '84ab500420319d', @@ -423,13 +820,59 @@ describe('AppNexusAdapter', () => { 'notify_url': 'imptracker.com', 'rtb': { 'video': { - 'content': '' + 'content': '' } + }, + 'javascriptTrackers': '' + }] + }] + }; + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'outstream' } + } + }] + } + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('vastImpUrl'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles instream video responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'asset_url': 'http://sample.vastURL.com/here/vid' + } + }, + 'javascriptTrackers': '' }] }] }; - let bidderRequest; + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'instream' + } + } + }] + } let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(result[0]).to.have.property('vastUrl'); @@ -437,12 +880,54 @@ describe('AppNexusAdapter', () => { expect(result[0]).to.have.property('mediaType', 'video'); }); - it('handles native responses', () => { + it('handles adpod responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'brand_category_id': 10, + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'asset_url': 'http://sample.vastURL.com/here/adpod', + 'duration_ms': 30000, + } + }, + 'viewability': { + 'config': '' + } + }] + }] + }; + + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'adpod' + } + } + }] + }; + bfStub.returns('1'); + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastUrl'); + expect(result[0].video.context).to.equal('adpod'); + expect(result[0].video.durationSeconds).to.equal(30); + }); + + it('handles native responses', function () { let response1 = deepClone(response); response1.tags[0].ads[0].ad_type = 'native'; response1.tags[0].ads[0].rtb.native = { 'title': 'Native Creative', 'desc': 'Cool description great stuff', + 'desc2': 'Additional body text', 'ctatext': 'Do it', 'sponsored': 'AppNexus', 'icon': { @@ -461,8 +946,23 @@ describe('AppNexusAdapter', () => { 'click_trackers': ['http://nym1-ib.adnxs.com/click'] }, 'impression_trackers': ['http://example.com'], + 'rating': '5', + 'displayurl': 'http://AppNexus.com/?url=display_url', + 'likes': '38908320', + 'downloads': '874983', + 'price': '9.99', + 'saleprice': 'FREE', + 'phone': '1234567890', + 'address': '28 W 23rd St, New York, NY 10010', + 'privacy_link': 'http://appnexus.com/?url=privacy_url', + 'javascriptTrackers': '' }; - let bidderRequest; + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); expect(result[0].native.title).to.equal('Native Creative'); @@ -471,17 +971,23 @@ describe('AppNexusAdapter', () => { expect(result[0].native.image.url).to.equal('http://cdn.adnxs.com/img.png'); }); - it('supports configuring outstream renderers', () => { + it('supports configuring outstream renderers', function () { const outstreamResponse = deepClone(response); outstreamResponse.tags[0].ads[0].rtb.video = {}; outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js'; const bidderRequest = { bids: [{ + bidId: '3db3773286ee59', renderer: { options: { adText: 'configured' } + }, + mediaTypes: { + video: { + context: 'outstream' + } } }] }; @@ -491,5 +997,34 @@ describe('AppNexusAdapter', () => { bidderRequest.bids[0].renderer.options ); }); + + it('should add deal_priority and deal_code', function() { + let responseWithDeal = deepClone(response); + responseWithDeal.tags[0].ads[0].deal_priority = 'high'; + responseWithDeal.tags[0].ads[0].deal_code = '123'; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseWithDeal }, {bidderRequest}); + expect(Object.keys(result[0].appnexus)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); + }); + + it('should add advertiser id', function() { + let responseAdvertiserId = deepClone(response); + responseAdvertiserId.tags[0].ads[0].advertiser_id = '123'; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); + }) }); }); diff --git a/test/spec/modules/arteebeeBidAdapter_spec.js b/test/spec/modules/arteebeeBidAdapter_spec.js index fe5bbf7ff25..013e1bd6c0c 100644 --- a/test/spec/modules/arteebeeBidAdapter_spec.js +++ b/test/spec/modules/arteebeeBidAdapter_spec.js @@ -1,9 +1,9 @@ import {expect} from 'chai'; import {spec} from 'modules/arteebeeBidAdapter'; -describe('Arteebee adapater', () => { - describe('Test validate req', () => { - it('should accept minimum valid bid', () => { +describe('Arteebee adapater', function () { + describe('Test validate req', function () { + it('should accept minimum valid bid', function () { let bid = { bidder: 'arteebee', params: { @@ -16,7 +16,7 @@ describe('Arteebee adapater', () => { expect(isValid).to.equal(true); }); - it('should reject missing pub', () => { + it('should reject missing pub', function () { let bid = { bidder: 'arteebee', params: { @@ -28,7 +28,7 @@ describe('Arteebee adapater', () => { expect(isValid).to.equal(false); }); - it('should reject missing source', () => { + it('should reject missing source', function () { let bid = { bidder: 'arteebee', params: { @@ -41,8 +41,8 @@ describe('Arteebee adapater', () => { }); }); - describe('Test build request', () => { - it('minimum request', () => { + describe('Test build request', function () { + it('minimum request', function () { let bid = { bidder: 'arteebee', params: { @@ -59,7 +59,7 @@ describe('Arteebee adapater', () => { expect(req.imp[0]).to.not.have.property('secure'); }); - it('make test request', () => { + it('make test request', function () { let bid = { bidder: 'arteebee', params: { @@ -77,7 +77,7 @@ describe('Arteebee adapater', () => { expect(req.imp[0]).to.not.have.property('secure'); }); - it('test coppa', () => { + it('test coppa', function () { let bid = { bidder: 'arteebee', params: { @@ -94,10 +94,38 @@ describe('Arteebee adapater', () => { expect(req).to.not.have.property('test'); expect(req.imp[0]).to.not.have.property('secure'); }); + + it('test gdpr', function () { + let bid = { + bidder: 'arteebee', + params: { + pub: 'prebidtest', + source: 'prebidtest' + }, + sizes: [[300, 250]] + }; + let consentString = 'ABCD'; + let bidderRequest = { + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + + const req = JSON.parse(spec.buildRequests([bid], bidderRequest)[0].data); + + expect(req.regs).to.exist; + expect(req.regs.ext).to.exist; + expect(req.regs.ext).to.have.property('gdpr', 1); + + expect(req.user).to.exist; + expect(req.user.ext).to.exist; + expect(req.user.ext).to.have.property('consent', consentString); + }); }); - describe('Test interpret response', () => { - it('General banner response', () => { + describe('Test interpret response', function () { + it('General banner response', function () { let resp = spec.interpretResponse({ body: { id: 'abcd', diff --git a/test/spec/modules/atomxBidAdapter_spec.js b/test/spec/modules/atomxBidAdapter_spec.js index fdbb01a1838..fdcdb55ec7f 100644 --- a/test/spec/modules/atomxBidAdapter_spec.js +++ b/test/spec/modules/atomxBidAdapter_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { spec } from 'modules/atomxBidAdapter'; -describe('atomxAdapterTest', () => { - describe('bidRequestValidity', () => { - it('bidRequest with id param', () => { +describe('atomxAdapterTest', function () { + describe('bidRequestValidity', function () { + it('bidRequest with id param', function () { expect(spec.isBidRequestValid({ bidder: 'atomx', params: { @@ -12,7 +12,7 @@ describe('atomxAdapterTest', () => { })).to.equal(true); }); - it('bidRequest with no id param', () => { + it('bidRequest with no id param', function () { expect(spec.isBidRequestValid({ bidder: 'atomx', params: { @@ -21,7 +21,7 @@ describe('atomxAdapterTest', () => { }); }); - describe('bidRequest', () => { + describe('bidRequest', function () { const bidRequests = [{ 'bidder': 'atomx', 'params': { @@ -47,21 +47,21 @@ describe('atomxAdapterTest', () => { 'auctionId': 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' }]; - it('bidRequest HTTP method', () => { + it('bidRequest HTTP method', function () { const requests = spec.buildRequests(bidRequests); requests.forEach(function(requestItem) { expect(requestItem.method).to.equal('GET'); }); }); - it('bidRequest url', () => { + it('bidRequest url', function () { const requests = spec.buildRequests(bidRequests); requests.forEach(function(requestItem) { expect(requestItem.url).to.match(new RegExp('p\\.ato\\.mx/placement')); }); }); - it('bidRequest data', () => { + it('bidRequest data', function () { const requests = spec.buildRequests(bidRequests); expect(requests[0].data.id).to.equal('123'); expect(requests[0].data.size).to.equal('300x250'); @@ -72,7 +72,7 @@ describe('atomxAdapterTest', () => { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const bidRequest = { 'method': 'GET', 'url': 'https://p.ato.mx/placement', @@ -103,7 +103,7 @@ describe('atomxAdapterTest', () => { headers: {} }; - it('result is correct', () => { + it('result is correct', function () { const result = spec.interpretResponse(bidResponse, bidRequest); expect(result[0].requestId).to.equal('22aidtbx5eabd9'); diff --git a/test/spec/modules/audienceNetworkBidAdapter_spec.js b/test/spec/modules/audienceNetworkBidAdapter_spec.js index f9d46e100b1..a495b33438c 100644 --- a/test/spec/modules/audienceNetworkBidAdapter_spec.js +++ b/test/spec/modules/audienceNetworkBidAdapter_spec.js @@ -19,36 +19,37 @@ const placementId = 'test-placement-id'; const playerwidth = 320; const playerheight = 180; const requestId = 'test-request-id'; -const pbv = '$prebid.version$'; +const debug = 'adapterver=1.3.0&platform=241394079772386&platver=$prebid.version$&cb=test-uuid'; +const pageUrl = encodeURIComponent(utils.getTopWindowUrl()); -describe('AudienceNetwork adapter', () => { - describe('Public API', () => { - it('code', () => { +describe('AudienceNetwork adapter', function () { + describe('Public API', function () { + it('code', function () { expect(code).to.equal(bidder); }); - it('supportedMediaTypes', () => { + it('supportedMediaTypes', function () { expect(supportedMediaTypes).to.deep.equal(['banner', 'video']); }); - it('isBidRequestValid', () => { + it('isBidRequestValid', function () { expect(isBidRequestValid).to.be.a('function'); }); - it('buildRequests', () => { + it('buildRequests', function () { expect(buildRequests).to.be.a('function'); }); - it('interpretResponse', () => { + it('interpretResponse', function () { expect(interpretResponse).to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - it('missing placementId parameter', () => { + describe('isBidRequestValid', function () { + it('missing placementId parameter', function () { expect(isBidRequestValid({ bidder, sizes: [[300, 250]] })).to.equal(false); }); - it('invalid sizes parameter', () => { + it('invalid sizes parameter', function () { expect(isBidRequestValid({ bidder, sizes: ['', undefined, null, '300x100', [300, 100], [300], {}], @@ -56,7 +57,7 @@ describe('AudienceNetwork adapter', () => { })).to.equal(false); }); - it('valid when at least one valid size', () => { + it('valid when at least one valid size', function () { expect(isBidRequestValid({ bidder, sizes: [[1, 1], [300, 250]], @@ -64,7 +65,7 @@ describe('AudienceNetwork adapter', () => { })).to.equal(true); }); - it('valid parameters', () => { + it('valid parameters', function () { expect(isBidRequestValid({ bidder, sizes: [[300, 250], [320, 50]], @@ -72,7 +73,7 @@ describe('AudienceNetwork adapter', () => { })).to.equal(true); }); - it('fullwidth', () => { + it('fullwidth', function () { expect(isBidRequestValid({ bidder, sizes: [[300, 250], [336, 280]], @@ -83,7 +84,7 @@ describe('AudienceNetwork adapter', () => { })).to.equal(true); }); - it('native', () => { + it('native', function () { expect(isBidRequestValid({ bidder, sizes: [[300, 250]], @@ -94,7 +95,7 @@ describe('AudienceNetwork adapter', () => { })).to.equal(true); }); - it('native with non-IAB size', () => { + it('native with non-IAB size', function () { expect(isBidRequestValid({ bidder, sizes: [[728, 90]], @@ -105,7 +106,7 @@ describe('AudienceNetwork adapter', () => { })).to.equal(true); }); - it('video', () => { + it('video', function () { expect(isBidRequestValid({ bidder, sizes: [[playerwidth, playerheight]], @@ -117,21 +118,22 @@ describe('AudienceNetwork adapter', () => { }); }); - describe('buildRequests', () => { - let isSafariBrowserStub; - before(() => { - isSafariBrowserStub = sinon.stub(utils, 'isSafariBrowser'); + describe('buildRequests', function () { + before(function () { + sinon + .stub(utils, 'generateUUID') + .returns('test-uuid'); }); - after(() => { - isSafariBrowserStub.restore(); + after(function () { + utils.generateUUID.restore(); }); - it('can build URL for IAB unit', () => { + it('can build URL for IAB unit', function () { expect(buildRequests([{ bidder, bidId: requestId, - sizes: [[300, 250], [320, 50]], + sizes: [[300, 50], [300, 250], [320, 50]], params: { placementId } }])).to.deep.equal([{ adformats: ['300x250'], @@ -139,11 +141,11 @@ describe('AudienceNetwork adapter', () => { requestIds: [requestId], sizes: ['300x250'], url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=&sdk[]=5.5.web&pbv=${pbv}` + data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=${pageUrl}&sdk[]=6.0.web&${debug}` }]); }); - it('can build URL for video unit', () => { + it('can build URL for video unit', function () { expect(buildRequests([{ bidder, bidId: requestId, @@ -158,11 +160,11 @@ describe('AudienceNetwork adapter', () => { requestIds: [requestId], sizes: ['640x480'], url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=&sdk[]=&pbv=${pbv}&playerwidth=640&playerheight=480` + data: `placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=${pageUrl}&sdk[]=&${debug}&playerwidth=640&playerheight=480` }]); }); - it('can build URL for native unit in non-IAB size', () => { + it('can build URL for native unit in non-IAB size', function () { expect(buildRequests([{ bidder, bidId: requestId, @@ -177,42 +179,36 @@ describe('AudienceNetwork adapter', () => { requestIds: [requestId], sizes: ['728x90'], url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=native&testmode=false&pageurl=&sdk[]=5.5.web&pbv=${pbv}` + data: `placementids[]=test-placement-id&adformats[]=native&testmode=false&pageurl=${pageUrl}&sdk[]=6.0.web&${debug}` }]); }); - it('can build URL for fullwidth 300x250 unit', () => { + it('can build URL for deprecated fullwidth unit, overriding platform', function () { + const platform = 'test-platform'; + const debugPlatform = debug.replace('241394079772386', platform); + expect(buildRequests([{ bidder, bidId: requestId, sizes: [[300, 250]], params: { placementId, + platform, format: 'fullwidth' } }])).to.deep.equal([{ - adformats: ['fullwidth'], + adformats: ['300x250'], method: 'GET', requestIds: [requestId], sizes: ['300x250'], url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=fullwidth&testmode=false&pageurl=&sdk[]=5.5.web&pbv=${pbv}` + data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=${pageUrl}&sdk[]=6.0.web&${debugPlatform}` }]); }); - - it('can build URL on Safari that includes a cachebuster param', () => { - isSafariBrowserStub.returns(true); - expect(buildRequests([{ - bidder, - bidId: requestId, - sizes: [[300, 250]], - params: { placementId } - }])[0].data).to.contain('&cb='); - }); }); - describe('interpretResponse', () => { - it('error in response', () => { + describe('interpretResponse', function () { + it('error in response', function () { expect(interpretResponse({ body: { errors: ['test-error-message'] @@ -220,7 +216,7 @@ describe('AudienceNetwork adapter', () => { }, {})).to.deep.equal([]); }); - it('valid native bid in response', () => { + it('valid native bid in response', function () { const [bidResponse] = interpretResponse({ body: { errors: [], @@ -245,9 +241,12 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); expect(bidResponse.ad) - .to.contain(`placementid:'${placementId}',format:'native',bidid:'test-bid-id'`, 'ad missing parameters') + .to.contain(`placementid: '${placementId}',`) + .and.to.contain(`format: 'native',`) + .and.to.contain(`bidid: 'test-bid-id',`) .and.to.contain('getElementsByTagName("style")', 'ad missing native styles') .and.to.contain('
    ', 'ad missing native container'); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); @@ -258,7 +257,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.fb_placementid).to.equal(placementId); }); - it('valid IAB bid in response', () => { + it('valid IAB bid in response', function () { const [bidResponse] = interpretResponse({ body: { errors: [], @@ -283,9 +282,12 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); expect(bidResponse.ad) - .to.contain(`placementid:'${placementId}',format:'300x250',bidid:'test-bid-id'`, 'ad missing parameters') + .to.contain(`placementid: '${placementId}',`) + .and.to.contain(`format: '300x250',`) + .and.to.contain(`bidid: 'test-bid-id',`) .and.not.to.contain('getElementsByTagName("style")', 'ad should not contain native styles') .and.not.to.contain('
    ', 'ad should not contain native container'); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); @@ -295,7 +297,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.fb_placementid).to.equal(placementId); }); - it('filters invalid slot sizes', () => { + it('filters invalid slot sizes', function () { const [bidResponse] = interpretResponse({ body: { errors: [], @@ -319,6 +321,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.requestId).to.equal(requestId); expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); @@ -328,7 +331,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.fb_placementid).to.equal(placementId); }); - it('valid multiple bids in response', () => { + it('valid multiple bids in response', function () { const placementIdNative = 'test-placement-id-native'; const placementIdIab = 'test-placement-id-iab'; @@ -362,7 +365,11 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseNative.requestId).to.equal(requestId); expect(bidResponseNative.width).to.equal(300); expect(bidResponseNative.height).to.equal(250); - expect(bidResponseNative.ad).to.contain(`placementid:'${placementIdNative}',format:'native',bidid:'test-bid-id-native'`, 'ad missing parameters'); + expect(bidResponseNative.ad) + .to.contain(`placementid: '${placementIdNative}',`) + .and.to.contain(`format: 'native',`) + .and.to.contain(`bidid: 'test-bid-id-native',`); + expect(bidResponseNative.ttl).to.equal(600); expect(bidResponseNative.creativeId).to.equal(placementIdNative); expect(bidResponseNative.netRevenue).to.equal(true); expect(bidResponseNative.currency).to.equal('USD'); @@ -375,7 +382,11 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseIab.requestId).to.equal(requestId); expect(bidResponseIab.width).to.equal(300); expect(bidResponseIab.height).to.equal(250); - expect(bidResponseIab.ad).to.contain(`placementid:'${placementIdIab}',format:'300x250',bidid:'test-bid-id-iab'`, 'ad missing parameters'); + expect(bidResponseIab.ad) + .to.contain(`placementid: '${placementIdIab}',`) + .and.to.contain(`format: '300x250',`) + .and.to.contain(`bidid: 'test-bid-id-iab',`); + expect(bidResponseIab.ttl).to.equal(600); expect(bidResponseIab.creativeId).to.equal(placementIdIab); expect(bidResponseIab.netRevenue).to.equal(true); expect(bidResponseIab.currency).to.equal('USD'); @@ -385,7 +396,7 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseIab.fb_placementid).to.equal(placementIdIab); }); - it('valid video bid in response', () => { + it('valid video bid in response', function () { const bidId = 'test-bid-id-video'; const [bidResponse] = interpretResponse({ @@ -409,13 +420,14 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.cpm).to.equal(1.23); expect(bidResponse.requestId).to.equal(requestId); + expect(bidResponse.ttl).to.equal(3600); expect(bidResponse.mediaType).to.equal('video'); - expect(bidResponse.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${placementId}&pageurl=&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${bidId}`); + expect(bidResponse.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${placementId}&pageurl=${pageUrl}&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${bidId}`); expect(bidResponse.width).to.equal(playerwidth); expect(bidResponse.height).to.equal(playerheight); }); - it('mixed video and native bids', () => { + it('mixed video and native bids', function () { const videoPlacementId = 'test-video-placement-id'; const videoBidId = 'test-video-bid-id'; const nativePlacementId = 'test-native-placement-id'; @@ -449,19 +461,24 @@ describe('AudienceNetwork adapter', () => { expect(bidResponseVideo.cpm).to.equal(1.23); expect(bidResponseVideo.requestId).to.equal(requestId); + expect(bidResponseVideo.ttl).to.equal(3600); expect(bidResponseVideo.mediaType).to.equal('video'); - expect(bidResponseVideo.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${videoPlacementId}&pageurl=&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${videoBidId}`); + expect(bidResponseVideo.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${videoPlacementId}&pageurl=${pageUrl}&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${videoBidId}`); expect(bidResponseVideo.width).to.equal(playerwidth); expect(bidResponseVideo.height).to.equal(playerheight); expect(bidResponseNative.cpm).to.equal(4.56); expect(bidResponseNative.requestId).to.equal(requestId); + expect(bidResponseNative.ttl).to.equal(600); expect(bidResponseNative.width).to.equal(300); expect(bidResponseNative.height).to.equal(250); - expect(bidResponseNative.ad).to.contain(`placementid:'${nativePlacementId}',format:'native',bidid:'${nativeBidId}'`); + expect(bidResponseNative.ad) + .to.contain(`placementid: '${nativePlacementId}',`) + .and.to.contain(`format: 'native',`) + .and.to.contain(`bidid: '${nativeBidId}',`); }); - it('mixture of valid native bid and error in response', () => { + it('mixture of valid native bid and error in response', function () { const [bidResponse] = interpretResponse({ body: { errors: ['test-error-message'], @@ -486,9 +503,12 @@ describe('AudienceNetwork adapter', () => { expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); expect(bidResponse.ad) - .to.contain(`placementid:'${placementId}',format:'native',bidid:'test-bid-id'`, 'ad missing parameters') + .to.contain(`placementid: '${placementId}',`) + .and.to.contain(`format: 'native',`) + .and.to.contain(`bidid: 'test-bid-id',`) .and.to.contain('getElementsByTagName("style")', 'ad missing native styles') .and.to.contain('
    ', 'ad missing native container'); + expect(bidResponse.ttl).to.equal(600); expect(bidResponse.creativeId).to.equal(placementId); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.currency).to.equal('USD'); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index a3ecccba6cb..4598a4e00c7 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -1,11 +1,12 @@ import { expect } from 'chai'; +import sinon from 'sinon'; import { spec, VIDEO_ENDPOINT, BANNER_ENDPOINT, OUTSTREAM_SRC, DEFAULT_MIMES } from 'modules/beachfrontBidAdapter'; -import * as utils from 'src/utils'; +import { parse as parseUrl } from 'src/url'; -describe('BeachfrontAdapter', () => { +describe('BeachfrontAdapter', function () { let bidRequests; - beforeEach(() => { + beforeEach(function () { bidRequests = [ { bidder: 'beachfront', @@ -31,13 +32,13 @@ describe('BeachfrontAdapter', () => { ]; }); - describe('spec.isBidRequestValid', () => { - it('should return true when the required params are passed', () => { + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed', function () { const bidRequest = bidRequests[0]; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the "bidfloor" param is missing', () => { + it('should return false when the "bidfloor" param is missing', function () { const bidRequest = bidRequests[0]; bidRequest.params = { appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76' @@ -45,7 +46,7 @@ describe('BeachfrontAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when the "appId" param is missing', () => { + it('should return false when the "appId" param is missing', function () { const bidRequest = bidRequests[0]; bidRequest.params = { bidfloor: 5.00 @@ -53,19 +54,19 @@ describe('BeachfrontAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when no bid params are passed', () => { + it('should return false when no bid params are passed', function () { const bidRequest = bidRequests[0]; bidRequest.params = {}; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when a bid request is not passed', () => { + it('should return false when a bid request is not passed', function () { expect(spec.isBidRequestValid()).to.equal(false); expect(spec.isBidRequestValid({})).to.equal(false); }); - describe('for multi-format bids', () => { - it('should return true when the required params are passed for video', () => { + describe('for multi-format bids', function () { + it('should return true when the required params are passed for video', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} @@ -79,7 +80,7 @@ describe('BeachfrontAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the required params are missing for video', () => { + it('should return false when the required params are missing for video', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} @@ -93,7 +94,7 @@ describe('BeachfrontAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return true when the required params are passed for banner', () => { + it('should return true when the required params are passed for banner', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} @@ -107,7 +108,7 @@ describe('BeachfrontAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the required params are missing for banner', () => { + it('should return false when the required params are missing for banner', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} @@ -123,9 +124,9 @@ describe('BeachfrontAdapter', () => { }); }); - describe('spec.buildRequests', () => { - describe('for video bids', () => { - it('should attach the bid request object', () => { + describe('spec.buildRequests', function () { + describe('for video bids', function () { + it('should attach the bid request object', function () { bidRequests[0].mediaTypes = { video: {} }; bidRequests[1].mediaTypes = { video: {} }; const requests = spec.buildRequests(bidRequests); @@ -133,7 +134,7 @@ describe('BeachfrontAdapter', () => { expect(requests[1].bidRequest).to.equal(bidRequests[1]); }); - it('should create a POST request for each bid', () => { + it('should create a POST request for each bid', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; const requests = spec.buildRequests([ bidRequest ]); @@ -141,7 +142,7 @@ describe('BeachfrontAdapter', () => { expect(requests[0].url).to.equal(VIDEO_ENDPOINT + bidRequest.params.appId); }); - it('should attach request data', () => { + it('should attach request data', function () { const width = 640; const height = 480; const bidRequest = bidRequests[0]; @@ -150,9 +151,14 @@ describe('BeachfrontAdapter', () => { playerSize: [ width, height ] } }; - const requests = spec.buildRequests([ bidRequest ]); + const topLocation = parseUrl('http://www.example.com?foo=bar', { decodeSearchAsString: true }); + const bidderRequest = { + refererInfo: { + referer: topLocation.href + } + }; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); const data = requests[0].data; - const topLocation = utils.getTopWindowLocation(); expect(data.isPrebid).to.equal(true); expect(data.appId).to.equal(bidRequest.params.appId); expect(data.domain).to.equal(document.location.hostname); @@ -164,7 +170,7 @@ describe('BeachfrontAdapter', () => { expect(data.cur).to.deep.equal(['USD']); }); - it('must parse bid size from a nested array', () => { + it('must parse bid size from a nested array', function () { const width = 640; const height = 480; const bidRequest = bidRequests[0]; @@ -178,7 +184,7 @@ describe('BeachfrontAdapter', () => { expect(data.imp[0].video).to.deep.contain({ w: width, h: height }); }); - it('must parse bid size from a string', () => { + it('must parse bid size from a string', function () { const width = 640; const height = 480; const bidRequest = bidRequests[0]; @@ -192,7 +198,7 @@ describe('BeachfrontAdapter', () => { expect(data.imp[0].video).to.deep.contain({ w: width, h: height }); }); - it('must handle an empty bid size', () => { + it('must handle an empty bid size', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { @@ -204,7 +210,7 @@ describe('BeachfrontAdapter', () => { expect(data.imp[0].video).to.deep.contain({ w: undefined, h: undefined }); }); - it('must fall back to the size on the bid object', () => { + it('must fall back to the size on the bid object', function () { const width = 640; const height = 480; const bidRequest = bidRequests[0]; @@ -215,23 +221,26 @@ describe('BeachfrontAdapter', () => { expect(data.imp[0].video).to.deep.contain({ w: width, h: height }); }); - it('must override video targeting params', () => { + it('must override video targeting params', function () { const bidRequest = bidRequests[0]; const mimes = ['video/webm']; + const playbackmethod = 2; + const maxduration = 30; + const placement = 4; bidRequest.mediaTypes = { video: {} }; - bidRequest.params.video = { mimes }; + bidRequest.params.video = { mimes, playbackmethod, maxduration, placement }; const requests = spec.buildRequests([ bidRequest ]); const data = requests[0].data; - expect(data.imp[0].video).to.deep.contain({ mimes }); + expect(data.imp[0].video).to.deep.contain({ mimes, playbackmethod, maxduration, placement }); }); - it('must add GDPR consent data to the request', () => { + it('must add GDPR consent data to the request', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; const bidderRequest = { gdprConsent: { - consentRequired: true, + gdprApplies: true, consentString } }; @@ -240,17 +249,35 @@ describe('BeachfrontAdapter', () => { expect(data.regs.ext.gdpr).to.equal(1); expect(data.user.ext.consent).to.equal(consentString); }); + + it('must add the Trade Desk User ID to the request', () => { + const tdid = '4321'; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + bidRequest.userId = { tdid }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.user.ext.eids[0]).to.deep.equal({ + source: 'adserver.org', + uids: [{ + id: tdid, + ext: { + rtiPartner: 'TDID' + } + }] + }); + }); }); - describe('for banner bids', () => { - it('should attach the bid requests array', () => { + describe('for banner bids', function () { + it('should attach the bid requests array', function () { bidRequests[0].mediaTypes = { banner: {} }; bidRequests[1].mediaTypes = { banner: {} }; const requests = spec.buildRequests(bidRequests); expect(requests[0].bidRequest).to.deep.equal(bidRequests); }); - it('should create a single POST request for all bids', () => { + it('should create a single POST request for all bids', function () { bidRequests[0].mediaTypes = { banner: {} }; bidRequests[1].mediaTypes = { banner: {} }; const requests = spec.buildRequests(bidRequests); @@ -259,7 +286,7 @@ describe('BeachfrontAdapter', () => { expect(requests[0].url).to.equal(BANNER_ENDPOINT); }); - it('should attach request data', () => { + it('should attach request data', function () { const width = 300; const height = 250; const bidRequest = bidRequests[0]; @@ -268,9 +295,14 @@ describe('BeachfrontAdapter', () => { sizes: [ width, height ] } }; - const requests = spec.buildRequests([ bidRequest ]); + const topLocation = parseUrl('http://www.example.com?foo=bar', { decodeSearchAsString: true }); + const bidderRequest = { + refererInfo: { + referer: topLocation.href + } + }; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); const data = requests[0].data; - const topLocation = utils.getTopWindowLocation(); expect(data.slots).to.deep.equal([ { slot: bidRequest.adUnitCode, @@ -285,7 +317,7 @@ describe('BeachfrontAdapter', () => { expect(data.ua).to.equal(navigator.userAgent); }); - it('must parse bid size from a nested array', () => { + it('must parse bid size from a nested array', function () { const width = 300; const height = 250; const bidRequest = bidRequests[0]; @@ -301,7 +333,7 @@ describe('BeachfrontAdapter', () => { ]); }); - it('must parse bid size from a string', () => { + it('must parse bid size from a string', function () { const width = 300; const height = 250; const bidRequest = bidRequests[0]; @@ -317,7 +349,7 @@ describe('BeachfrontAdapter', () => { ]); }); - it('must handle an empty bid size', () => { + it('must handle an empty bid size', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: { @@ -329,7 +361,7 @@ describe('BeachfrontAdapter', () => { expect(data.slots[0].sizes).to.deep.equal([]); }); - it('must fall back to the size on the bid object', () => { + it('must fall back to the size on the bid object', function () { const width = 300; const height = 250; const bidRequest = bidRequests[0]; @@ -340,13 +372,13 @@ describe('BeachfrontAdapter', () => { expect(data.slots[0].sizes).to.deep.contain({ w: width, h: height }); }); - it('must add GDPR consent data to the request', () => { + it('must add GDPR consent data to the request', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; const bidderRequest = { gdprConsent: { - consentRequired: true, + gdprApplies: true, consentString } }; @@ -355,10 +387,20 @@ describe('BeachfrontAdapter', () => { expect(data.gdpr).to.equal(1); expect(data.gdprConsent).to.equal(consentString); }); + + it('must add the Trade Desk User ID to the request', () => { + const tdid = '4321'; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + bidRequest.userId = { tdid }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.tdid).to.equal(tdid); + }); }); - describe('for multi-format bids', () => { - it('should create a POST request for each bid format', () => { + describe('for multi-format bids', function () { + it('should create a POST request for each bid format', function () { const width = 300; const height = 250; const bidRequest = bidRequests[0]; @@ -386,7 +428,7 @@ describe('BeachfrontAdapter', () => { expect(requests[1].url).to.contain(BANNER_ENDPOINT); }); - it('must parse bid sizes for each bid format', () => { + it('must parse bid sizes for each bid format', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { @@ -413,16 +455,16 @@ describe('BeachfrontAdapter', () => { }); }); - describe('spec.interpretResponse', () => { - describe('for video bids', () => { - it('should return no bids if the response is not valid', () => { + describe('spec.interpretResponse', function () { + describe('for video bids', function () { + it('should return no bids if the response is not valid', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response "url" is missing', () => { + it('should return no bids if the response "url" is missing', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; const serverResponse = { @@ -432,7 +474,7 @@ describe('BeachfrontAdapter', () => { expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response "bidPrice" is missing', () => { + it('should return no bids if the response "bidPrice" is missing', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; const serverResponse = { @@ -442,7 +484,7 @@ describe('BeachfrontAdapter', () => { expect(bidResponse.length).to.equal(0); }); - it('should return a valid video bid response', () => { + it('should return a valid video bid response', function () { const width = 640; const height = 480; const bidRequest = bidRequests[0]; @@ -473,7 +515,7 @@ describe('BeachfrontAdapter', () => { }); }); - it('should return a renderer for outstream video bids', () => { + it('should return a renderer for outstream video bids', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { @@ -491,24 +533,81 @@ describe('BeachfrontAdapter', () => { url: OUTSTREAM_SRC }); }); + + it('should initialize a player for outstream bids', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + context: 'outstream', + playerSize: [ width, height ] + } + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + cmpId: '123abc' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + window.Beachfront = { Player: sinon.spy() }; + bidResponse.adUnitCode = bidRequest.adUnitCode; + bidResponse.renderer.render(bidResponse); + sinon.assert.calledWith(window.Beachfront.Player, bidResponse.adUnitCode, sinon.match({ + adTagUrl: bidResponse.vastUrl, + width: bidResponse.width, + height: bidResponse.height, + expandInView: false, + collapseOnComplete: true + })); + delete window.Beachfront; + }); + + it('should configure outstream player settings from the bidder params', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + context: 'outstream', + playerSize: [ width, height ] + } + }; + bidRequest.params.player = { + expandInView: true, + collapseOnComplete: false, + progressColor: 'green' + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + cmpId: '123abc' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + window.Beachfront = { Player: sinon.spy() }; + bidResponse.adUnitCode = bidRequest.adUnitCode; + bidResponse.renderer.render(bidResponse); + sinon.assert.calledWith(window.Beachfront.Player, bidResponse.adUnitCode, sinon.match(bidRequest.params.player)); + delete window.Beachfront; + }); }); - describe('for banner bids', () => { - it('should return no bids if the response is not valid', () => { + describe('for banner bids', function () { + it('should return no bids if the response is not valid', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response is empty', () => { + it('should return no bids if the response is empty', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return valid banner bid responses', () => { + it('should return valid banner bid responses', function () { bidRequests[0].mediaTypes = { banner: { sizes: [[ 300, 250 ], [ 728, 90 ]] @@ -554,4 +653,119 @@ describe('BeachfrontAdapter', () => { }); }); }); + + describe('spec.getUserSyncs', function () { + describe('for video bids', function () { + let bidResponse; + + beforeEach(function () { + bidResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + cmpId: '123abc' + }; + }); + + it('should return an iframe user sync if iframes are enabled', function () { + const syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + const serverResponses = [{ + body: bidResponse + }]; + const userSyncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(userSyncs.length).to.equal(1); + expect(userSyncs[0].type).to.equal('iframe'); + }); + + it('should return an image user sync if iframes are disabled', function () { + const syncOptions = { + iframeEnabled: false, + pixelEnabled: true + }; + const serverResponses = [{ + body: bidResponse + }]; + const userSyncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(userSyncs.length).to.equal(1); + expect(userSyncs[0].type).to.equal('image'); + }); + + it('should not return user syncs if none are enabled', function () { + const syncOptions = { + iframeEnabled: false, + pixelEnabled: false + }; + const serverResponses = [{ + body: bidResponse + }]; + const userSyncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(userSyncs).to.deep.equal([]); + }); + }); + + describe('for banner bids', function () { + let bidResponse; + + beforeEach(function () { + bidResponse = { + slot: bidRequests[0].adUnitCode, + adm: '
    ', + crid: 'crid_1', + price: 3.02, + w: 728, + h: 90 + }; + }); + + it('should return user syncs defined the bid response', function () { + const syncUrl = 'https://sync.bfmio.com/sync_iframe?ifpl=5&ifg=1&id=test&gdpr=0&gc=&gce=0'; + const syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + const serverResponses = [{ + body: [ + { sync: syncUrl }, + bidResponse + ] + }]; + const userSyncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(userSyncs).to.deep.equal([ + { type: 'iframe', url: syncUrl } + ]); + }); + + it('should not return user syncs if iframes are disabled', function () { + const syncUrl = 'https://sync.bfmio.com/sync_iframe?ifpl=5&ifg=1&id=test&gdpr=0&gc=&gce=0'; + const syncOptions = { + iframeEnabled: false, + pixelEnabled: true + }; + const serverResponses = [{ + body: [ + { sync: syncUrl }, + bidResponse + ] + }]; + const userSyncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(userSyncs).to.deep.equal([]); + }); + + it('should not return user syncs if there are none in the bid response', function () { + const syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + const serverResponses = [{ + body: [ + bidResponse + ] + }]; + const userSyncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(userSyncs).to.deep.equal([]); + }); + }); + }); }); diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js new file mode 100644 index 00000000000..f2d770805c5 --- /dev/null +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -0,0 +1,69 @@ +import { expect } from 'chai'; +import { spec } from 'modules/betweenBidAdapter'; + +describe('betweenBidAdapterTests', function () { + it('validate_pub_params', function () { + expect(spec.isBidRequestValid({ + bidder: 'between', + params: { + w: 240, + h: 400, + s: 1112 + } + })).to.equal(true); + }); + it('validate_generated_params', function () { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'between', + params: {w: 240, h: 400, s: 1112}, + sizes: [[240, 400]] + }] + let request = spec.buildRequests(bidRequestData); + let req_data = request[0].data; + expect(req_data.bidid).to.equal('bid1234'); + }); + it('validate_response_params', function () { + let serverResponse = { + body: [{ + bidid: 'bid1234', + cpm: 1.12, + w: 240, + h: 400, + currency: 'USD', + ad: 'Ad html' + }] + }; + let bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(1.12); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal(240); + expect(bid.height).to.equal(400); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid1234'); + expect(bid.ad).to.equal('Ad html'); + }); + it('validate_response_params', function () { + let serverResponse = { + body: [{ + bidid: 'bid1234', + w: 240, + h: 400, + currency: 'USD', + ad: 'Ad html' + }] + }; + let bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(0); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal(240); + expect(bid.height).to.equal(400); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid1234'); + expect(bid.ad).to.equal('Ad html'); + }); +}); diff --git a/test/spec/modules/bidfluenceBidAdapter_spec.js b/test/spec/modules/bidfluenceBidAdapter_spec.js new file mode 100644 index 00000000000..9ce6e808c6b --- /dev/null +++ b/test/spec/modules/bidfluenceBidAdapter_spec.js @@ -0,0 +1,114 @@ +import { expect } from 'chai'; +import { spec } from 'modules/bidfluenceBidAdapter'; + +const BIDDER_CODE = 'bidfluence'; +const PLACEMENT_ID = '1000'; +const PUB_ID = '1000'; +const CONSENT_STRING = 'DUXDSDFSFWRRR8345F=='; + +const validBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': { + 'placementId': PLACEMENT_ID, + 'publisherId': PUB_ID, + 'reservePrice': 0 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '2b1f23307fb8ef', + 'bidderRequestId': '10edf38ec1a719', + 'auctionId': '1025ba77-5463-4877-b0eb-14b205cb9304' +}]; + +const bidderRequest = { + 'bidderCode': 'bidfluence', + 'auctionId': '1025ba77-5463-4877-b0eb-14b205cb9304', + 'bidderRequestId': '10edf38ec1a719', + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'test', + 'stack': ['test'] + }, + 'timeout': 1000, + 'gdprConsent': { + 'gdprApplies': true, + 'consentString': CONSENT_STRING, + 'vendorData': '' + } +}; + +bidderRequest.bids = validBidRequests; + +describe('Bidfluence Adapter test', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(validBidRequests[0])).to.equal(true); + }); + it('should return the right bidder code', function () { + expect(spec.code).to.eql(BIDDER_CODE); + }); + }); + + describe('buildRequests', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + const payload = JSON.parse(request.data); + + expect(payload.bids[0].bid).to.equal(validBidRequests[0].bidId); + expect(payload.azr).to.equal(true); + expect(payload.ck).to.not.be.undefined; + expect(payload.bids[0].tid).to.equal(PLACEMENT_ID); + expect(payload.bids[0].pid).to.equal(PUB_ID); + expect(payload.bids[0].rp).to.be.a('number'); + expect(payload.re).to.not.be.undefined; + expect(payload.st).to.not.be.undefined; + expect(payload.tz).to.not.be.undefined; + expect(payload.sr).to.not.be.undefined; + expect(payload.vp).to.not.be.undefined; + expect(payload.sdt).to.not.be.undefined; + expect(payload.bids[0].w).to.equal('300'); + expect(payload.bids[0].h).to.equal('250'); + + it('sends gdpr info if exists', function () { + expect(payload.gdpr).to.equal(true); + expect(payload.gdprc).to.equal(CONSENT_STRING); + }); + }); + + describe('interpretResponse', function () { + const response = { + body: { + Bids: + [{ + 'CreativeId': '1000', + 'Cpm': 0.50, + 'Ad': '
    ', + 'Height': 250, + 'Width': 300 + }] + } + }; + + it('should get correct bid response', function () { + const expectedResponse = [{ + requestId: response.body.Bids[0].BidId, + cpm: response.body.Bids[0].Cpm, + width: response.body.Bids[0].Width, + height: response.body.Bids[0].Height, + creativeId: response.body.Bids[0].CreativeId, + ad: response.body.Bids[0].Ad, + currency: 'USD', + netRevenue: true, + ttl: 360 + }]; + + let result = spec.interpretResponse(response, { 'bidderRequest': validBidRequests[0] }); + expect(result).to.deep.equal(expectedResponse); + }); + }); +}); diff --git a/test/spec/modules/bidglassAdapter_spec.js b/test/spec/modules/bidglassAdapter_spec.js new file mode 100644 index 00000000000..00a47fc997a --- /dev/null +++ b/test/spec/modules/bidglassAdapter_spec.js @@ -0,0 +1,103 @@ +import { expect } from 'chai'; +import { spec } from 'modules/bidglassBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('Bid Glass Adapter', function () { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'bidglass', + 'params': { + 'adUnitId': '3' + }, + 'adUnitCode': 'bidglass-testunit', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + 'bidder': 'bidglass', + 'params': { + 'adUnitId': '3' + }, + 'adUnitCode': 'bidglass-testunit', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }]; + + const request = spec.buildRequests(bidRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('sets withCredentials to false', function () { + expect(request.options.withCredentials).to.equal(false); + }); + }); + + describe('interpretResponse', function () { + let response; + beforeEach(function () { + response = { + body: { + 'bidResponses': [{ + 'ad': '', + 'cpm': '0.01', + 'creativeId': '-1', + 'width': '300', + 'height': '250', + 'requestId': '30b31c1838de1e' + }] + } + }; + }); + + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': '30b31c1838de1e', + 'cpm': 0.01, + 'width': 300, + 'height': 250, + 'creativeId': '-1', + 'dealId': null, + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 10, + 'ad': '' + }]; + + let result = spec.interpretResponse(response); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('handles empty bid response', function () { + let response = { + body: { + 'bidResponses': [] + } + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/bidphysicsBidAdapter_spec.js b/test/spec/modules/bidphysicsBidAdapter_spec.js new file mode 100644 index 00000000000..ba93642ad81 --- /dev/null +++ b/test/spec/modules/bidphysicsBidAdapter_spec.js @@ -0,0 +1,261 @@ +import {expect} from 'chai'; +import {spec} from 'modules/bidphysicsBidAdapter'; + +const REQUEST = { + 'bidderCode': 'bidphysics', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708', + 'bidderRequestId': 'requestId', + 'bidRequest': [{ + 'bidder': 'bidphysics', + 'params': { + 'unitId': 123456, + }, + 'placementCode': 'div-gpt-dummy-placement-code', + 'sizes': [ + [300, 250] + ], + 'bidId': 'bidId1', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' + }, + { + 'bidder': 'bidphysics', + 'params': { + 'unitId': 123456, + }, + 'placementCode': 'div-gpt-dummy-placement-code', + 'sizes': [ + [300, 250] + ], + 'bidId': 'bidId2', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' + }], + 'start': 1487883186070, + 'auctionStart': 1487883186069, + 'timeout': 3000 +}; + +const RESPONSE = { + 'headers': null, + 'body': { + 'id': 'responseId', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'bidId1', + 'impid': 'bidId1', + 'price': 0.18, + 'adm': '', + 'adid': '144762342', + 'adomain': [ + 'http://dummydomain.com' + ], + 'iurl': 'iurl', + 'cid': '109', + 'crid': 'creativeId', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 334553, + 'auction_id': 514667951122925701, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + }, + { + 'id': 'bidId2', + 'impid': 'bidId2', + 'price': 0.1, + 'adm': '', + 'adid': '144762342', + 'adomain': [ + 'http://dummydomain.com' + ], + 'iurl': 'iurl', + 'cid': '109', + 'crid': 'creativeId', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 386046, + 'auction_id': 517067951122925501, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'bidphysics' + } + ], + 'ext': { + 'usersync': { + 'sovrn': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlsovrn', + 'type': 'iframe' + } + ] + }, + 'appnexus': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlappnexus', + 'type': 'pixel' + } + ] + } + }, + 'responsetimemillis': { + 'appnexus': 127 + } + } + } +}; + +describe('BidPhysics bid adapter', function () { + describe('isBidRequestValid', function () { + it('should accept request if only unitId is passed', function () { + let bid = { + bidder: 'bidphysics', + params: { + unitId: 'unitId', + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should accept request if only networkId is passed', function () { + let bid = { + bidder: 'bidphysics', + params: { + networkId: 'networkId', + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should accept request if only publisherId is passed', function () { + let bid = { + bidder: 'bidphysics', + params: { + publisherId: 'publisherId', + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('reject requests without params', function () { + let bid = { + bidder: 'bidphysics', + params: {} + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('creates request data', function () { + let request = spec.buildRequests(REQUEST.bidRequest, REQUEST); + + expect(request).to.exist.and.to.be.a('object'); + const payload = JSON.parse(request.data); + expect(payload.imp[0]).to.have.property('id', REQUEST.bidRequest[0].bidId); + expect(payload.imp[1]).to.have.property('id', REQUEST.bidRequest[1].bidId); + }); + + it('has gdpr data if applicable', function () { + const req = Object.assign({}, REQUEST, { + gdprConsent: { + consentString: 'consentString', + gdprApplies: true, + } + }); + let request = spec.buildRequests(REQUEST.bidRequest, req); + + const payload = JSON.parse(request.data); + expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); + expect(payload.regs.ext).to.have.property('gdpr', 1); + }); + }); + + describe('interpretResponse', function () { + it('have bids', function () { + let bids = spec.interpretResponse(RESPONSE, REQUEST); + expect(bids).to.be.an('array').that.is.not.empty; + validateBidOnIndex(0); + validateBidOnIndex(1); + + function validateBidOnIndex(index) { + expect(bids[index]).to.have.property('currency', 'USD'); + expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].impid); + expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price); + expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w); + expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h); + expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); + expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); + expect(bids[index]).to.have.property('ttl', 30); + expect(bids[index]).to.have.property('netRevenue', true); + } + }); + + it('handles empty response', function () { + const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}}); + const bids = spec.interpretResponse(EMPTY_RESP, REQUEST); + + expect(bids).to.be.empty; + }); + }); + + describe('getUserSyncs', function () { + it('handles no parameters', function () { + let opts = spec.getUserSyncs({}); + expect(opts).to.be.an('array').that.is.empty; + }); + it('returns non if sync is not allowed', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + + expect(opts).to.be.an('array').that.is.empty; + }); + + it('iframe sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [RESPONSE]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['sovrn'].syncs[0].url); + }); + + it('pixel sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [RESPONSE]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['appnexus'].syncs[0].url); + }); + + it('all sync enabled should return all results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [RESPONSE]); + + expect(opts.length).to.equal(2); + }); + }); +}); diff --git a/test/spec/modules/bizzclickBidAdapter_spec.js b/test/spec/modules/bizzclickBidAdapter_spec.js new file mode 100644 index 00000000000..6f518f32ccf --- /dev/null +++ b/test/spec/modules/bizzclickBidAdapter_spec.js @@ -0,0 +1,117 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/bizzclickBidAdapter'; + +describe('BizzclickBidAdapter', function () { + let bid = { + bidId: '67d581a232281d', + bidder: 'bizzclickBidAdapter', + bidderRequestId: 'a7837c9145e136', + params: { + placementId: 0, + type: 'banner' + }, + placementCode: 'placementId', + auctionId: 'bfe951372e62-a92d-4cf1-869f-d24029', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-1b244bbfb5' + }; + + describe('isBidRequestValid', function () { + it('Should return true when placement_id can be cast to a number', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when placement_id is not a number', function () { + bid.params.placementId = 'aaa'; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('//supply.bizzclick.com/?c=o&m=multi'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + let placements = data['placements']; + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.all.keys('placementId', 'bidId', 'type', 'sizes'); + expect(placement.placementId).to.be.a('number'); + expect(placement.bidId).to.be.a('string'); + expect(placement.type).to.be.a('string'); + expect(placement.sizes).to.be.an('array'); + } + }); + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

    Hello ad

    ', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + }] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + + describe('getUserSyncs', function () { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and type', function () { + 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('//supply.bizzclick.com/?c=o&m=cookie'); + }); + }); +}); diff --git a/test/spec/modules/brainyBidAdapter_spec.js b/test/spec/modules/brainyBidAdapter_spec.js index 8dc74435263..a3ce90d927a 100644 --- a/test/spec/modules/brainyBidAdapter_spec.js +++ b/test/spec/modules/brainyBidAdapter_spec.js @@ -31,27 +31,50 @@ const bidReq = [{ const correctReq = { accountID: '12345', slotID: '12345' -} +}; const bidResponse = { ad_id: '1036e9746c-d186-49ae-90cb-2796d0f9b223', adm: '', + syncUrl: '//testparm.com/ssp-sync/p/sync?uid=2110180601155125000059&buyer=2&slot=34', cpm: 100, height: 250, width: 300 }; -describe('brainy Adapter', () => { - describe('request', () => { - it('should validate bid request', () => { +const bidSyncResponse = [{ + body: { + ad_id: '1036e9746c-d186-49ae-90cb-2796d0f9b223', + adm: '', + syncUrl: '//testparm.com/ssp-sync/p/sync?uid=2110180601155125000059&buyer=2&slot=34', + cpm: 100, + height: 250, + width: 300 + } +}]; + +const invalidSyncBidResponse = [{ + body: { + ad_id: '1036e9746c-d186-49ae-90cb-2796d0f9b223', + adm: '', + syncUrl: 'null', + cpm: 100, + height: 250, + width: 300 + } +}]; + +describe('brainy Adapter', function () { + describe('request', function () { + it('should validate bid request', function () { expect(spec.isBidRequestValid(validBidReq)).to.equal(true); }); - it('should not validate incorrect bid request', () => { + it('should not validate incorrect bid request', function () { expect(spec.isBidRequestValid(invalidBidReq)).to.equal(false); }); }); - describe('build request', () => { - it('Verify bid request', () => { + describe('build request', function () { + it('Verify bid request', function () { const request = spec.buildRequests(bidReq); expect(request[0].method).to.equal('GET'); expect(request[0].url).to.equal(URL); @@ -60,14 +83,14 @@ describe('brainy Adapter', () => { }); }); - describe('interpretResponse', () => { - it('should build bid array', () => { + describe('interpretResponse', function () { + it('should build bid array', function () { const request = spec.buildRequests(bidReq); const result = spec.interpretResponse({body: bidResponse}, request[0]); expect(result.length).to.equal(1); }); - it('should have all relevant fields', () => { + it('should have all relevant fields', function () { const request = spec.buildRequests(bidReq); const result = spec.interpretResponse({body: bidResponse}, request[0]); const bid = result[0]; @@ -77,4 +100,29 @@ describe('brainy Adapter', () => { expect(bid.height).to.equal(bidResponse.height); }); }); + + describe('spec.getUserSyncs', function () { + let syncOptions + beforeEach(function () { + syncOptions = { + enabledBidders: ['brainy'], + pixelEnabled: true + } + }); + it('sucess with usersync url', function () { + const result = []; + result.push({type: 'image', url: '//testparm.com/ssp-sync/p/sync?uid=2110180601155125000059&buyer=2&slot=34'}); + expect(spec.getUserSyncs(syncOptions, bidSyncResponse)).to.deep.equal(result); + }); + + it('sucess without usersync url', function () { + const result = []; + expect(spec.getUserSyncs(syncOptions, invalidSyncBidResponse)).to.deep.equal(result); + }); + it('empty response', function () { + const serverResponse = [{body: {}}]; + const result = []; + expect(spec.getUserSyncs(syncOptions, serverResponse)).to.deep.equal(result); + }); + }); }); diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 5dae3c474ac..6ca9675a1bb 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -169,13 +169,13 @@ describe('bridgewellBidAdapter', function () { ]; const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bidWithoutCpmWeight = { 'bidder': 'bridgewell', 'params': { @@ -227,18 +227,18 @@ describe('bridgewellBidAdapter', function () { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bidWithoutCpmWeight)).to.equal(true); expect(spec.isBidRequestValid(bidWithCorrectCpmWeight)).to.equal(true); expect(spec.isBidRequestValid(bidWithUncorrectCpmWeight)).to.equal(false); expect(spec.isBidRequestValid(bidWithZeroCpmWeight)).to.equal(false); }); - it('should return false when required params not found', () => { + it('should return false when required params not found', function () { expect(spec.isBidRequestValid({})).to.equal(false); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bidWithoutCpmWeight = Object.assign({}, bidWithoutCpmWeight); let bidWithCorrectCpmWeight = Object.assign({}, bidWithCorrectCpmWeight); let bidWithUncorrectCpmWeight = Object.assign({}, bidWithUncorrectCpmWeight); @@ -272,8 +272,8 @@ describe('bridgewellBidAdapter', function () { }); }); - describe('buildRequests', () => { - it('should attach valid params to the tag', () => { + describe('buildRequests', function () { + it('should attach valid params to the tag', function () { const request = spec.buildRequests(bidRequests); const payload = request.data; const adUnits = payload.adUnits; @@ -286,14 +286,14 @@ describe('bridgewellBidAdapter', function () { } }); - it('should attach validBidRequests to the tag', () => { + it('should attach validBidRequests to the tag', function () { const request = spec.buildRequests(bidRequests); const validBidRequests = request.validBidRequests; expect(validBidRequests).to.deep.equal(bidRequests); }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const request = spec.buildRequests(bidRequests); const serverResponses = [ { @@ -440,7 +440,7 @@ describe('bridgewellBidAdapter', function () { } ]; - it('should return all required parameters', () => { + it('should return all required parameters', function () { const result = spec.interpretResponse({'body': serverResponses}, request); result.every(res => expect(res.cpm).to.be.a('number')); result.every(res => expect(res.width).to.be.a('number')); @@ -457,12 +457,12 @@ describe('bridgewellBidAdapter', function () { }); }); - it('should give up bid if server response is undefiend', () => { + it('should give up bid if server response is undefiend', function () { const result = spec.interpretResponse({'body': undefined}, request); expect(result).to.deep.equal([]); }); - it('should give up bid if request sizes is missing', () => { + it('should give up bid if request sizes is missing', function () { let target = Object.assign({}, serverResponses[0]); target.consumed = false; const result = spec.interpretResponse({'body': [target]}, spec.buildRequests([{ @@ -478,7 +478,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if response sizes is invalid', () => { + it('should give up bid if response sizes is invalid', function () { let target = { 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', 'bidder_code': 'bridgewell', @@ -495,7 +495,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if cpm is missing', () => { + it('should give up bid if cpm is missing', function () { let target = { 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', 'bidder_code': 'bridgewell', @@ -511,7 +511,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if width or height is missing', () => { + it('should give up bid if width or height is missing', function () { let target = { 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', 'bidder_code': 'bridgewell', @@ -526,7 +526,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if ad is missing', () => { + it('should give up bid if ad is missing', function () { let target = { 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', 'bidder_code': 'bridgewell', @@ -543,7 +543,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if revenue mode is missing', () => { + it('should give up bid if revenue mode is missing', function () { let target = { 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', 'bidder_code': 'bridgewell', @@ -559,7 +559,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if currency is missing', () => { + it('should give up bid if currency is missing', function () { let target = { 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', 'bidder_code': 'bridgewell', @@ -575,7 +575,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if mediaType is missing', () => { + it('should give up bid if mediaType is missing', function () { let target = { 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', 'bidder_code': 'bridgewell', @@ -592,7 +592,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if property native of mediaType native is missing', () => { + it('should give up bid if property native of mediaType native is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -609,7 +609,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native title is missing', () => { + it('should give up bid if native title is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -643,7 +643,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native title is too long', () => { + it('should give up bid if native title is too long', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -678,7 +678,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native body is missing', () => { + it('should give up bid if native body is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -711,7 +711,7 @@ describe('bridgewellBidAdapter', function () { const result = spec.interpretResponse({'body': [target]}, request); expect(result).to.deep.equal([]); - it('should give up bid if native image url is missing', () => { + it('should give up bid if native image url is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -746,7 +746,7 @@ describe('bridgewellBidAdapter', function () { }); }); - it('should give up bid if native image is missing', () => { + it('should give up bid if native image is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -776,7 +776,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native image url is missing', () => { + it('should give up bid if native image url is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -808,7 +808,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native image sizes is unmatch', () => { + it('should give up bid if native image sizes is unmatch', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -841,7 +841,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native sponsoredBy is missing', () => { + it('should give up bid if native sponsoredBy is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -875,7 +875,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native icon is missing', () => { + it('should give up bid if native icon is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -905,7 +905,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native icon url is missing', () => { + it('should give up bid if native icon url is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -939,7 +939,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native icon sizes is unmatch', () => { + it('should give up bid if native icon sizes is unmatch', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -972,7 +972,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native clickUrl is missing', () => { + it('should give up bid if native clickUrl is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -1006,7 +1006,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native clickTrackers is missing', () => { + it('should give up bid if native clickTrackers is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -1040,7 +1040,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native clickTrackers is empty', () => { + it('should give up bid if native clickTrackers is empty', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -1075,7 +1075,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native impressionTrackers is missing', () => { + it('should give up bid if native impressionTrackers is missing', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -1109,7 +1109,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if native impressionTrackers is empty', () => { + it('should give up bid if native impressionTrackers is empty', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', @@ -1144,7 +1144,7 @@ describe('bridgewellBidAdapter', function () { expect(result).to.deep.equal([]); }); - it('should give up bid if mediaType is not support', () => { + it('should give up bid if mediaType is not support', function () { let target = { 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', 'bidder_code': 'bridgewell', diff --git a/test/spec/modules/brightcomBidAdapter_spec.js b/test/spec/modules/brightcomBidAdapter_spec.js new file mode 100644 index 00000000000..73186250f5b --- /dev/null +++ b/test/spec/modules/brightcomBidAdapter_spec.js @@ -0,0 +1,283 @@ +import { expect } from 'chai'; +import * as utils from 'src/utils'; +import { spec } from 'modules/brightcomBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const URL = 'https://brightcombid.marphezis.com/hb'; + +describe('brightcomBidAdapter', function() { + const adapter = newBidder(spec); + let element, win; + let bidRequests; + let sandbox; + + beforeEach(function() { + element = { + x: 0, + y: 0, + + width: 0, + height: 0, + + getBoundingClientRect: () => { + return { + width: element.width, + height: element.height, + + left: element.x, + top: element.y, + right: element.x + element.width, + bottom: element.y + element.height + }; + } + }; + win = { + document: { + visibilityState: 'visible' + }, + + innerWidth: 800, + innerHeight: 600 + }; + bidRequests = [{ + 'bidder': 'brightcom', + 'params': { + 'publisherId': 1234567 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '5fb26ac22bde4', + 'bidderRequestId': '4bf93aeb730cb9', + 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e' + }]; + + sandbox = sinon.sandbox.create(); + sandbox.stub(document, 'getElementById').withArgs('adunit-code').returns(element); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns(win); + }); + + afterEach(function() { + sandbox.restore(); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'brightcom', + 'params': { + 'publisherId': 1234567 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '5fb26ac22bde4', + 'bidderRequestId': '4bf93aeb730cb9', + 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when tagid not passed correctly', function () { + bid.params.publisherId = undefined; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('sends bid request to our endpoint via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.method).to.equal('POST'); + }); + + it('request url should match our endpoint url', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(URL); + }); + + it('sets the proper banner object', function() { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + }); + + it('accepts a single array as a size', function() { + bidRequests[0].sizes = [300, 250]; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); + }); + + it('sends bidfloor param if present', function () { + bidRequests[0].params.bidFloor = 0.05; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].bidfloor).to.equal(0.05); + }); + + it('sends tagid', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].tagid).to.equal('adunit-code'); + }); + + it('sends publisher id', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.site.publisher.id).to.equal(1234567); + }); + + context('when element is fully in view', function() { + it('returns 100', function() { + Object.assign(element, { width: 600, height: 400 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(100); + }); + }); + + context('when element is out of view', function() { + it('returns 0', function() { + Object.assign(element, { x: -300, y: 0, width: 207, height: 320 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(0); + }); + }); + + context('when element is partially in view', function() { + it('returns percentage', function() { + Object.assign(element, { width: 800, height: 800 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(75); + }); + }); + + context('when width or height of the element is zero', function() { + it('try to use alternative values', function() { + Object.assign(element, { width: 0, height: 0 }); + bidRequests[0].sizes = [[800, 2400]]; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(25); + }); + }); + + context('when nested iframes', function() { + it('returns \'na\'', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + utils.getWindowSelf.restore(); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns({}); + + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal('na'); + }); + }); + + context('when tab is inactive', function() { + it('returns 0', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + win.document.visibilityState = 'hidden'; + sandbox.stub(utils, 'getWindowTop').returns(win); + + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(0); + }); + }); + }); + + describe('interpretResponse', function () { + let response; + beforeEach(function () { + response = { + body: { + 'id': '37386aade21a71', + 'seatbid': [{ + 'bid': [{ + 'id': '376874781', + 'impid': '283a9f4cd2415d', + 'price': 0.35743275, + 'nurl': '', + 'adm': '', + 'w': 300, + 'h': 250 + }] + }] + } + }; + }); + + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': '283a9f4cd2415d', + 'cpm': 0.35743275, + 'width': 300, + 'height': 250, + 'creativeId': '376874781', + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': `
    `, + 'ttl': 60 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('crid should default to the bid id if not on the response', function () { + let expectedResponse = [{ + 'requestId': '283a9f4cd2415d', + 'cpm': 0.35743275, + 'width': 300, + 'height': 250, + 'creativeId': response.body.seatbid[0].bid[0].id, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': `
    `, + 'ttl': 60 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('handles empty bid response', function () { + let response = { + body: '' + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs ', () => { + let syncOptions = {iframeEnabled: true, pixelEnabled: true}; + + it('should not return', () => { + let returnStatement = spec.getUserSyncs(syncOptions, []); + expect(returnStatement).to.be.empty; + }); + }); +}); diff --git a/test/spec/modules/bucksenseBidAdapter_spec.js b/test/spec/modules/bucksenseBidAdapter_spec.js new file mode 100644 index 00000000000..17b5c3ceff5 --- /dev/null +++ b/test/spec/modules/bucksenseBidAdapter_spec.js @@ -0,0 +1,147 @@ +import {expect} from 'chai'; +import {spec} from 'modules/bucksenseBidAdapter'; + +describe('Bucksense Adapter', function() { + const BIDDER_CODE = 'bucksense'; + const BID_ID = '12345'; + const PLACE_ID = '1000'; + + function getValidBidObject() { + return { + bidder: BIDDER_CODE, + params: { + placementId: PLACE_ID, + } + } + }; + + describe('isBidRequestValid', function() { + var bid; + + beforeEach(function() { + bid = getValidBidObject(); + }); + + it('returns true when valid bid request is sent', function() { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('returns true when valid test bid request is sent', function() { + bid.params['test'] = 1; + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('returns false when bidder not set to "bucksense"', function() { + bid.bidder = 'dummy'; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('returns false when params not set', function() { + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function() { + var bid, bidRequestObj; + + beforeEach(function() { + bid = getValidBidObject(); + bidRequestObj = { + 'bidderCode': 'bucksense', + 'auctionId': '73540558-86cb-4eef-895f-bf99c5353bd7', + 'bidderRequestId': '1feebcb5938c7e', + 'bids': [ + { + 'bidder': 'bucksense', + 'params': { + 'placementId': 1000 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ [ 300, 250 ], [ 300, 600 ] ] + } + }, + 'adUnitCode': 'test-div', + 'transactionId': '52b3ed07-2a09-4f58-a426-75acc7602c96', + 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], + 'bidId': '22aecdacdcd773', + 'bidderRequestId': '1feebcb5938c7e', + 'auctionId': '73540558-86cb-4eef-895f-bf99c5353bd7', + 'src': 'client', + 'bidRequestsCount': 1 + } + ], + 'auctionStart': 1557176022728, + 'timeout': 1000, + 'refererInfo': { + 'referer': 'http://stefanod.hera.pe/prebid/?pbjs_debug=true', + 'reachedTop': true, + 'numIframes': 0, + 'stack': [ + 'http://stefanod.hera.pe/prebid/?pbjs_debug=true' + ] + }, + 'start': 1557176022731 + }; + }); + + it('should build a very basic request', function() { + var request = spec.buildRequests([bid], bidRequestObj); + expect(request[0].method).to.equal('POST'); + }); + + it('bidRequest data', function () { + var request = spec.buildRequests([bid], bidRequestObj); + expect(request[0].data).to.exist; + }); + }); + + describe('interpretResponse', function() { + var serverResponse; + var serverRequest; + + beforeEach(function() { + serverRequest = { + 'method': 'POST', + 'url': 'https://prebid.bksn.se:445/prebidjs/', + 'data': { + 'pub_id': 'prebid.org', + 'pl_id': '1000', + 'secure': 0, + 'href': 'http://prebid.org/developers.html', + 'bid_id': '27aaf8e96d9fd5', + 'params': { + 'placementId': '1000' + }, + 'sizes': [ [ 300, 250 ], [ 300, 600 ] ] + } + }; + + serverResponse = { + body: { + 'requestId': '', + 'cpm': 0.3, + 'width': 300, + 'height': 250, + 'ttl': 360, + 'creativeId': 'creative002', + 'currency': 'USD', + 'netRevenue': false, + 'ad': '
    ' + } + }; + }); + + it('should return an array of bid responses', function() { + var responses = spec.interpretResponse(serverResponse, serverRequest); + expect(responses).to.be.an('array').with.length(1); + }); + + it('should return an array of bid responses', function() { + serverResponse = {}; + var responses = spec.interpretResponse(serverResponse, serverRequest); + expect(responses).to.be.an('array').with.length(0); + }); + }); +}); diff --git a/test/spec/modules/buzzoolaBidAdapter_spec.js b/test/spec/modules/buzzoolaBidAdapter_spec.js new file mode 100644 index 00000000000..e6f22d1da20 --- /dev/null +++ b/test/spec/modules/buzzoolaBidAdapter_spec.js @@ -0,0 +1,279 @@ +import {expect} from 'chai'; +import {spec} from 'modules/buzzoolaBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; +import {executeRenderer, Renderer} from '../../../src/Renderer'; +import {deepClone} from '../../../src/utils'; + +const ENDPOINT = 'https://exchange.buzzoola.com/ssp/prebidjs'; +const RENDERER_SRC = 'https://tube.buzzoola.com/new/build/buzzlibrary.js'; + +const INVALID_BIDS = [{ + 'bidder': 'buzzoola', + 'mediaTypes': {'banner': {'sizes': [[240, 400], [300, 600]]}}, + 'sizes': [[240, 400], [300, 600]] +}, { + 'bidder': 'buzzoola', + 'params': {'placementId': 417846}, + 'sizes': [[240, 400], [300, 600]] +}, { + 'bidder': 'buzzoola', + 'mediaTypes': { + 'video': { + 'playerSize': [[640, 380]], + 'mimes': ['video/mp4'], + 'minduration': 1, + 'maxduration': 2 + } + } +}, { + 'bidder': 'buzzoola', + 'params': {'placementId': 417845} +}]; + +const BANNER_BID = { + 'bidder': 'buzzoola', + 'params': {'placementId': 417846}, + 'mediaTypes': {'banner': {'sizes': [[240, 400], [300, 600]]}}, + 'sizes': [[240, 400], [300, 600]], + 'bidId': '2a11641ada3c6a' +}; + +const BANNER_BID_REQUEST = { + bidderCode: 'buzzoola', + bids: [BANNER_BID] +}; + +const BANNER_RESPONSE = [{ + 'requestId': '2a11641ada3c6a', + 'cpm': 5.583115, + 'width': 240, + 'height': 400, + 'creativeId': '11773', + 'dealId': '', + 'currency': 'RUB', + 'netRevenue': true, + 'ttl': 10800, + 'ad': '
    ', + 'mediaType': 'banner' +}]; + +const REQUIRED_BANNER_FIELDS = [ + 'requestId', + 'cpm', + 'width', + 'height', + 'ad', + 'ttl', + 'creativeId', + 'netRevenue', + 'currency', + 'mediaType' +]; + +const VIDEO_BID = { + 'bidder': 'buzzoola', + 'params': {'placementId': 417845}, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 380]], + 'mimes': ['video/mp4'], + 'minduration': 1, + 'maxduration': 2 + } + }, + 'bidId': '325a54271dc40a' +}; + +const VIDEO_BID_REQUEST = { + bidderCode: 'buzzoola', + bids: [VIDEO_BID] +}; + +const VIDEO_RESPONSE = [{ + 'requestId': '325a54271dc40a', + 'cpm': 4.6158956756756755, + 'width': 640, + 'height': 380, + 'creativeId': '11774', + 'dealId': '', + 'currency': 'RUB', + 'netRevenue': true, + 'ttl': 10800, + 'ad': '{"crs":[{"advertiser_id":165,"title":"qa//PrebidJStestVideoURL","description":"qa//PrebidJStest","duration":0,"ya_id":"55038886","raw_content":"{\\"main_content\\": \\"https://tube.buzzoola.com/xstatic/o42/mcaug/2.mp4\\"}","content":{"main_content":"https://tube.buzzoola.com/xstatic/o42/mcaug/2.mp4"},"content_type":"video_url","sponsor_link":"","sponsor_name":"","overlay":"","overlay_start_after":0,"overlay_close_after":0,"action_button_title":"","tracking_url":{},"iframe_domains":[],"soc_share_url":"https://tube.buzzoola.com/share.html","player_show_skip_button_before_play":false,"player_show_skip_button_seconds":5,"player_show_title":true,"player_data_attributes":{"expandable":"default","overroll":"default"},"click_event_view":"default","share_panel_position":"left","auto_play":true,"logo_url":{},"share_buttons":["vkontakte","facebook","twitter","moimir","odnoklassniki","embed"],"player_show_panels":false,"thumbnail":"","tracking_js":{},"click_event_url":"https://exchange.buzzoola.com/event/f9382ceb-49c2-4683-50d8-5c516c53cd69/14795a96-6261-49dc-7241-207333ab1490/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm/click/0/","vpaid_js_url":"https://tube.buzzoola.com/new/js/lib/vpaid_js_proxy.js","skip_clickthru":false,"landing_link_text":"","sound_enabled_by_default":false,"landing_link_position":"right","displayed_price":"","js_wrapper_url":"","enable_moat":false,"branding_template":"","event_url":"https://exchange.buzzoola.com/event/f9382ceb-49c2-4683-50d8-5c516c53cd69/14795a96-6261-49dc-7241-207333ab1490/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm/","resend_event_url":"https://exchange.buzzoola.com/resend_event/f9382ceb-49c2-4683-50d8-5c516c53cd69/14795a96-6261-49dc-7241-207333ab1490/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm/","creative_hash":"m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm","custom_html":"","custom_js":"","height":0,"width":0,"campaign_id":5758,"line_item_id":17319,"creative_id":11774,"extra":{"imp_id":"14795a96-6261-49dc-7241-207333ab1490","rtime":"2019-08-27 13:58:36"},"subcontent":"vast","auction_settings":{"price":"4.6158956756756755","currency":"RUB","event_name":"player_seen","time_slice":0},"hash_to_embed":"kbDH64c7yFYkSu0KCwSkoUD2bNHAnUTHBERqLGtWnaIF4Kow5peD5g","need_ad":false}],"tracking_urls":{"ctor":["https://www.tns-counter.ru/V13a****buzzola_com/ru/CP1251/tmsec=buzzola_total/1322650417245790778","https://www.tns-counter.ru/V13a****buzzoola_kz/ru/UTF-8/tmsec=buzzoola_video/5395765100939533275","https://buzzoolaru.solution.weborama.fr/fcgi-bin/dispatch.fcgi?a.A=ev&a.si=3071&a.te=37&a.aap=1&a.agi=862&a.evn=PrebidJS.test&g.ra=4581478478720298652","https://x01.aidata.io/0.gif?pid=BUZZOOLA&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://top-fwz1.mail.ru/counter?id=3026769","https://www.tns-counter.ru/V13a****buzzola_com/ru/UTF-8/tmsec=buzzola_inread/542059452789128996","https://dm.hybrid.ai/match?id=111&vid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://px.adhigh.net/p/cm/buzzoola?u=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://ssp1.rtb.beeline.ru/userbind?src=buz&ssp_user_id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://sync.upravel.com/image?source=buzzoola&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://relap.io/api/partners/bzcs.gif?uid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://x.bidswitch.net/sync?ssp=sspicyads","https://inv-nets.admixer.net/adxcm.aspx?ssp=3C5173FC-CA30-4692-9116-009C19CB1BF9&rurl=%2F%2Fexchange.buzzoola.com%2Fcookiesync%2Fdsp%2Fadmixer-video%2F%24%24visitor_cookie%24%24","https://sync.datamind.ru/cookie/accepter?source=buzzoola&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://dmp.vihub.ru/match?sysid=buz&redir=no&uid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://ad.adriver.ru/cgi-bin/rle.cgi?sid=1&ad=608223&bt=21&pid=2551979&bid=6150299&bn=6150299&rnd=1279444531737367663","https://reichelcormier.bid/point/?method=match&type=ssp&key=4677290772f9000878093d69c199bfba&id=3509&extUid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://sync.republer.com/match?src=buzzoola&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://sm.rtb.mts.ru/p?id=dbdb5b13-e719-4987-7f6a-a882322bbfce&ssp=buzzoola","https://cm.mgid.com/m?cdsp=371151&adu=https%3A%2F%2Fexchange.buzzoola.com%2Fcookiesync%2Fdsp%2Fmarketgid-native%2F%7Bmuidn%7D","https://dmp.gotechnology.io/dmp/syncsspdmp?sspid=122258"]},"tracking_js":{"ctor":["https://buzzoola.fraudscore.mobi/dooJ9sheeeDaZ3fe.js?s=268671&l=417845"]},"placement":{"placement_id":417845,"unit_type":"inread","unit_settings":{"align":"left","autoplay_enable_sound":false,"creatives_amount":1,"debug_mode":false,"expandable":"never","sound_control":"default","target":"","width":"100%"},"unit_settings_list":["width","sound_control","debug_mode","target","creatives_amount","expandable","container_height","align","height"]},"uuid":"dbdb5b13-e719-4987-7f6a-a882322bbfce","auction_id":"f9382ceb-49c2-4683-50d8-5c516c53cd69","env":"prod"}', + 'vastXml': '\n00:00:30', + 'mediaType': 'video' +}]; + +const RENDERER_DATA = { + data: JSON.parse(VIDEO_RESPONSE[0].ad) +}; +RENDERER_DATA.data.placement.unit_settings.width = '' + VIDEO_RESPONSE[0].width; +RENDERER_DATA.data.placement.unit_settings.height = RENDERER_DATA.data.placement.unit_settings.container_height = '' + VIDEO_RESPONSE[0].height; + +const REQUIRED_VIDEO_FIELDS = [ + 'requestId', + 'cpm', + 'width', + 'height', + 'ad', + 'ttl', + 'creativeId', + 'netRevenue', + 'currency', + 'vastXml', + 'mediaType' +]; + +describe('buzzoolaBidAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(VIDEO_BID)).to.be.true; + }); + + it('should return false when required params are not passed', () => { + INVALID_BIDS.forEach(bid => { + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + }); + + describe('buildRequests', () => { + let videoBidRequests = [VIDEO_BID]; + let bannerBidRequests = [BANNER_BID]; + + const bannerRequest = spec.buildRequests(bannerBidRequests, BANNER_BID_REQUEST); + const videoRequest = spec.buildRequests(videoBidRequests, VIDEO_BID_REQUEST); + + it('sends bid request to ENDPOINT via POST', () => { + expect(videoRequest.method).to.equal('POST'); + expect(bannerRequest.method).to.equal('POST'); + }); + + it('sends bid request to correct ENDPOINT', () => { + expect(videoRequest.url).to.equal(ENDPOINT); + expect(bannerRequest.url).to.equal(ENDPOINT); + }); + + it('sends correct video bid parameters', () => { + expect(videoRequest.data).to.deep.equal(VIDEO_BID_REQUEST); + }); + + it('sends correct banner bid parameters', () => { + expect(bannerRequest.data).to.deep.equal(BANNER_BID_REQUEST); + }); + }); + + describe('interpretResponse', () => { + const noBidServerResponse = []; + const emptyResponse = ''; + + function nobidServerResponseCheck(request, response = noBidServerResponse) { + const noBidResult = spec.interpretResponse({body: response}, {data: request}); + + expect(noBidResult.length).to.equal(0); + } + + function bidServerResponseCheck(response, request, fields) { + const result = spec.interpretResponse({body: response}, {data: request}); + + expect(result).to.deep.equal(response); + result.forEach(bid => { + fields.forEach(field => { + expect(bid).to.have.own.property(field); + }) + }); + } + + it('handles video nobid responses', () => { + nobidServerResponseCheck(VIDEO_BID_REQUEST); + }); + + it('handles banner nobid responses', () => { + nobidServerResponseCheck(BANNER_BID_REQUEST); + }); + + it('handles video empty responses', () => { + nobidServerResponseCheck(VIDEO_BID_REQUEST, emptyResponse); + }); + + it('handles banner empty responses', () => { + nobidServerResponseCheck(BANNER_BID_REQUEST, emptyResponse); + }); + + it('should get correct video bid response', () => { + bidServerResponseCheck(VIDEO_RESPONSE, VIDEO_BID_REQUEST, REQUIRED_VIDEO_FIELDS); + }); + + it('should get correct banner bid response', () => { + bidServerResponseCheck(BANNER_RESPONSE, BANNER_BID_REQUEST, REQUIRED_BANNER_FIELDS); + }); + }); + + describe('outstream renderer', () => { + let result; + let renderer; + + before(() => { + const adContainer = document.createElement('div'); + adContainer.id = 'adUnitCode'; + document.body.appendChild(adContainer); + + const outstreamVideoBid = deepClone(VIDEO_BID); + outstreamVideoBid.mediaTypes.video.context = 'outstream'; + + const outstreamVideoRequest = deepClone(VIDEO_BID_REQUEST); + outstreamVideoRequest.bids = [outstreamVideoBid]; + + const scriptElement = document.createElement('div'); + + const scriptStub = sinon.stub(document, 'createElement'); + scriptStub.withArgs('script').returns(scriptElement); + + result = spec.interpretResponse({body: VIDEO_RESPONSE}, {data: outstreamVideoRequest})[0]; + renderer = result.renderer; + + result.adUnitCode = 'adUnitCode'; + + scriptElement.onload && scriptElement.onload(); + + scriptStub.restore(); + }); + + it('should add renderer for outstream video', () => { + expect(result).to.have.own.property('renderer'); + }); + + it('should be instance of Renderer', () => { + expect(renderer).to.be.instanceof(Renderer); + }); + + it('should have valid src', () => { + expect(renderer.url).to.equal(RENDERER_SRC); + }); + + it('should create player instance', () => { + window.Buzzoola = { + Core: { + install: () => {} + } + }; + const spy = sinon.spy(window.Buzzoola.Core, 'install'); + executeRenderer(renderer, result); + expect(spy.called).to.be.true; + + const spyCall = spy.getCall(0); + + expect(spyCall.args[0]).to.be.instanceof(Element); + expect(spyCall.args[1]).to.deep.equal(RENDERER_DATA); + }); + }); +}); diff --git a/test/spec/modules/c1xBidAdapter_spec.js b/test/spec/modules/c1xBidAdapter_spec.js index 0517668ea0d..268ad46d0ce 100644 --- a/test/spec/modules/c1xBidAdapter_spec.js +++ b/test/spec/modules/c1xBidAdapter_spec.js @@ -5,16 +5,16 @@ import { newBidder } from 'src/adapters/bidderFactory'; const ENDPOINT = 'https://ht.c1exchange.com/ht'; const BIDDER_CODE = 'c1x'; -describe('C1XAdapter', () => { +describe('C1XAdapter', function () { const adapter = newBidder(c1xAdapter); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': BIDDER_CODE, 'adUnitCode': 'adunit-code', @@ -24,11 +24,11 @@ describe('C1XAdapter', () => { } }; - it('should return true when required params are passed', () => { + it('should return true when required params are passed', function () { expect(c1xAdapter.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not found', () => { + it('should return false when required params are not found', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -38,7 +38,7 @@ describe('C1XAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': BIDDER_CODE, @@ -61,19 +61,19 @@ describe('C1XAdapter', () => { return parsedData; }; - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { const request = c1xAdapter.buildRequests(bidRequests); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET'); }); - it('should generate correct bid Id tag', () => { + it('should generate correct bid Id tag', function () { const request = c1xAdapter.buildRequests(bidRequests); expect(request.bids[0].adUnitCode).to.equal('adunit-code'); expect(request.bids[0].bidId).to.equal('30b31c1838de1e'); }); - it('should convert params to proper form and attach to request', () => { + it('should convert params to proper form and attach to request', function () { const request = c1xAdapter.buildRequests(bidRequests); const originalPayload = parseRequest(request.data); const payloadObj = JSON.parse(originalPayload); @@ -83,7 +83,7 @@ describe('C1XAdapter', () => { expect(payloadObj.site).to.equal('9999'); }); - it('should convert floor price to proper form and attach to request', () => { + it('should convert floor price to proper form and attach to request', function () { let bidRequest = Object.assign({}, bidRequests[0], { @@ -100,7 +100,7 @@ describe('C1XAdapter', () => { expect(payloadObj.a1p).to.equal('4.35'); }); - it('should convert pageurl to proper form and attach to request', () => { + it('should convert pageurl to proper form and attach to request', function () { let bidRequest = Object.assign({}, bidRequests[0], { @@ -114,9 +114,27 @@ describe('C1XAdapter', () => { const payloadObj = JSON.parse(originalPayload); expect(payloadObj.pageurl).to.equal('http://c1exchange.com/'); }); + + it('should convert GDPR Consent to proper form and attach to request', function () { + let consentString = 'BOP2gFWOQIFovABABAENBGAAAAAAMw'; + let bidderRequest = { + 'bidderCode': 'c1x', + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + } + bidderRequest.bids = bidRequests; + + const request = c1xAdapter.buildRequests(bidRequests, bidderRequest); + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj['consent_string']).to.equal('BOP2gFWOQIFovABABAENBGAAAAAAMw'); + expect(payloadObj['consent_required']).to.equal('true'); + }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let response = { 'bid': true, 'cpm': 1.5, @@ -128,7 +146,7 @@ describe('C1XAdapter', () => { 'bidType': 'GROSS_BID' }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { let expectedResponse = [ { width: 300, @@ -151,7 +169,7 @@ describe('C1XAdapter', () => { expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = { bid: false, adId: 'c1x-test' diff --git a/test/spec/modules/categoryTranslation_spec.js b/test/spec/modules/categoryTranslation_spec.js new file mode 100644 index 00000000000..17cc07269b0 --- /dev/null +++ b/test/spec/modules/categoryTranslation_spec.js @@ -0,0 +1,98 @@ +import { getAdserverCategoryHook, initTranslation } from 'modules/categoryTranslation'; +import { config } from 'src/config'; +import * as utils from 'src/utils'; +import { expect } from 'chai'; + +describe('category translation', function () { + let fakeTranslationServer; + let getLocalStorageStub; + + beforeEach(function () { + fakeTranslationServer = sinon.fakeServer.create(); + getLocalStorageStub = sinon.stub(utils, 'getDataFromLocalStorage'); + }); + + afterEach(function() { + fakeTranslationServer.reset(); + getLocalStorageStub.restore(); + config.resetConfig(); + }); + + it('should translate iab category to adserver category', function () { + config.setConfig({ + 'adpod': { + 'brandCategoryExclusion': true + } + }); + getLocalStorageStub.returns(JSON.stringify({ + 'mapping': { + 'iab-1': { + 'id': 1, + 'name': 'sample' + } + } + })); + let bid = { + meta: { + iabSubCatId: 'iab-1' + } + } + getAdserverCategoryHook(sinon.spy(), 'code', bid); + expect(bid.meta.adServerCatId).to.equal(1); + }); + + it('should set adserverCatId to undefined if not found in mapping file', function() { + config.setConfig({ + 'adpod': { + 'brandCategoryExclusion': true + } + }); + getLocalStorageStub.returns(JSON.stringify({ + 'mapping': { + 'iab-1': { + 'id': 1, + 'name': 'sample' + } + } + })); + let bid = { + meta: { + iabSubCatId: 'iab-2' + } + } + getAdserverCategoryHook(sinon.spy(), 'code', bid); + expect(bid.meta.adServerCatId).to.equal(undefined); + }); + + it('should not make ajax call to update mapping file if data found in localstorage and is not expired', function () { + let clock = sinon.useFakeTimers(utils.timestamp()); + getLocalStorageStub.returns(JSON.stringify({ + lastUpdated: utils.timestamp(), + mapping: { + 'iab-1': '1' + } + })); + initTranslation(); + expect(fakeTranslationServer.requests.length).to.equal(0); + clock.restore(); + }); + + it('should use default mapping file if publisher has not defined in config', function () { + getLocalStorageStub.returns(null); + initTranslation('http://sample.com', 'somekey'); + expect(fakeTranslationServer.requests.length).to.equal(1); + expect(fakeTranslationServer.requests[0].url).to.equal('http://sample.com'); + }); + + it('should use publisher defined defined mapping file', function () { + config.setConfig({ + 'brandCategoryTranslation': { + 'translationFile': 'http://sample.com' + } + }); + getLocalStorageStub.returns(null); + initTranslation('http://sample.com', 'somekey'); + expect(fakeTranslationServer.requests.length).to.equal(2); + expect(fakeTranslationServer.requests[0].url).to.equal('http://sample.com'); + }); +}); diff --git a/test/spec/modules/ccxBidAdapter_spec.js b/test/spec/modules/ccxBidAdapter_spec.js new file mode 100644 index 00000000000..a89a0402a97 --- /dev/null +++ b/test/spec/modules/ccxBidAdapter_spec.js @@ -0,0 +1,403 @@ +import { expect } from 'chai'; +import { spec } from 'modules/ccxBidAdapter'; +import * as utils from 'src/utils'; + +describe('ccxAdapter', function () { + let bids = [ + { + adUnitCode: 'banner', + auctionId: '0b9de793-8eda-481e-a548-c187d58b28d9', + bidId: '2e56e1af51a5d7', + bidder: 'ccx', + bidderRequestId: '17e7b9f58a607e', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 607 + }, + sizes: [[300, 250]], + transactionId: 'aefddd38-cfa0-48ab-8bdd-325de4bab5f9' + }, + { + adUnitCode: 'video', + auctionId: '0b9de793-8eda-481e-a548-c187d58b28d9', + bidId: '3u94t90ut39tt3t', + bidder: 'ccx', + bidderRequestId: '23ur20r239r2r', + mediaTypes: { + video: { + playerSize: [[640, 480]] + } + }, + params: { + placementId: 608 + }, + sizes: [[640, 480]], + transactionId: 'aefddd38-cfa0-48ab-8bdd-325de4bab5f9' + } + ]; + describe('isBidRequestValid', function () { + it('Valid bid requests', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + expect(spec.isBidRequestValid(bids[1])).to.be.true; + }); + it('Invalid bid reqeusts - no placementId', function () { + let bidsClone = utils.deepClone(bids); + bidsClone[0].params = undefined; + expect(spec.isBidRequestValid(bidsClone[0])).to.be.false; + }); + it('Invalid bid reqeusts - invalid banner sizes', function () { + let bidsClone = utils.deepClone(bids); + bidsClone[0].mediaTypes.banner.sizes = [300, 250]; + expect(spec.isBidRequestValid(bidsClone[0])).to.be.false; + bidsClone[0].mediaTypes.banner.sizes = [[300, 250], [750]]; + expect(spec.isBidRequestValid(bidsClone[0])).to.be.false; + bidsClone[0].mediaTypes.banner.sizes = []; + expect(spec.isBidRequestValid(bidsClone[0])).to.be.false; + }); + it('Invalid bid reqeusts - invalid video sizes', function () { + let bidsClone = utils.deepClone(bids); + bidsClone[1].mediaTypes.video.playerSize = []; + expect(spec.isBidRequestValid(bidsClone[1])).to.be.false; + bidsClone[1].mediaTypes.video.sizes = [640, 480]; + expect(spec.isBidRequestValid(bidsClone[1])).to.be.false; + }); + it('Valid bid reqeust - old style sizes', function () { + let bidsClone = utils.deepClone(bids); + delete (bidsClone[0].mediaTypes); + delete (bidsClone[1].mediaTypes); + expect(spec.isBidRequestValid(bidsClone[0])).to.be.true; + expect(spec.isBidRequestValid(bidsClone[1])).to.be.true; + bidsClone[0].sizes = [300, 250]; + expect(spec.isBidRequestValid(bidsClone[0])).to.be.true; + }); + }); + describe('buildRequests', function () { + it('No valid bids', function () { + expect(spec.buildRequests([])).to.be.undefined; + }); + + it('Valid bid request - default', function () { + let response = spec.buildRequests(bids, {bids}); + expect(response).to.be.not.empty; + expect(response.data).to.be.not.empty; + + let data = JSON.parse(response.data); + + expect(data).to.be.an('object'); + expect(data).to.have.keys('site', 'imp', 'id', 'ext', 'device'); + + let imps = [ + { + banner: { + format: [ + { + w: 300, + h: 250 + } + ] + }, + ext: { + pid: 607 + }, + id: '2e56e1af51a5d7', + secure: 1 + }, + { + video: { + w: 640, + h: 480, + protocols: [2, 3, 5, 6], + mimes: ['video/mp4', 'video/x-flv'], + playbackmethod: [1, 2, 3, 4], + skip: 0 + }, + id: '3u94t90ut39tt3t', + secure: 1, + ext: { + pid: 608 + } + } + ]; + expect(data.imp).to.deep.have.same.members(imps); + }); + + it('Valid bid request - custom', function () { + let bidsClone = utils.deepClone(bids); + let imps = [ + { + banner: { + format: [ + { + w: 300, + h: 250 + } + ] + }, + ext: { + pid: 607 + }, + id: '2e56e1af51a5d7', + secure: 1 + }, + { + video: { + w: 640, + h: 480, + protocols: [5, 6], + mimes: ['video/mp4'], + playbackmethod: [3], + skip: 1, + skipafter: 5 + }, + id: '3u94t90ut39tt3t', + secure: 1, + ext: { + pid: 608 + } + } + ]; + + bidsClone[1].params.video = {}; + bidsClone[1].params.video.protocols = [5, 6]; + bidsClone[1].params.video.mimes = ['video/mp4']; + bidsClone[1].params.video.playbackmethod = [3]; + bidsClone[1].params.video.skip = 1; + bidsClone[1].params.video.skipafter = 5; + + let response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + let data = JSON.parse(response.data); + + expect(data.imp).to.deep.have.same.members(imps); + }); + it('Valid bid request - sizes old style', function () { + let bidsClone = utils.deepClone(bids); + delete (bidsClone[0].mediaTypes); + delete (bidsClone[1].mediaTypes); + bidsClone[0].mediaType = 'banner'; + bidsClone[1].mediaType = 'video'; + + let imps = [ + { + banner: { + format: [ + { + w: 300, + h: 250 + } + ] + }, + ext: { + pid: 607 + }, + id: '2e56e1af51a5d7', + secure: 1 + }, + { + video: { + w: 640, + h: 480, + protocols: [2, 3, 5, 6], + mimes: ['video/mp4', 'video/x-flv'], + playbackmethod: [1, 2, 3, 4], + skip: 0 + }, + id: '3u94t90ut39tt3t', + secure: 1, + ext: { + pid: 608 + } + } + ]; + + let response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + let data = JSON.parse(response.data); + + expect(data.imp).to.deep.have.same.members(imps); + }); + it('Valid bid request - sizes old style - no media type', function () { + let bidsClone = utils.deepClone(bids); + delete (bidsClone[0].mediaTypes); + delete (bidsClone[1]); + + let imps = [ + { + banner: { + format: [ + { + w: 300, + h: 250 + } + ] + }, + ext: { + pid: 607 + }, + id: '2e56e1af51a5d7', + secure: 1 + } + ]; + + let response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + let data = JSON.parse(response.data); + + expect(data.imp).to.deep.have.same.members(imps); + }); + }); + + let response = { + id: '0b9de793-8eda-481e-a548-c187d58b28d9', + seatbid: [ + { + bid: [ + { + id: '2e56e1af51a5d7_221', + impid: '2e56e1af51a5d7', + price: 8.1, + adid: '221', + adm: '', + adomain: ['clickonometrics.com'], + crid: '221', + w: 300, + h: 250, + ext: { + type: 'standard' + } + }, + { + id: '2e56e1af51a5d8_222', + impid: '2e56e1af51a5d8', + price: 5.68, + adid: '222', + adm: '', + adomain: ['clickonometrics.com'], + crid: '222', + w: 640, + h: 480, + ext: { + type: 'video' + } + } + ] + } + ], + cur: 'PLN', + ext: { + ttl: 5, + usersync: [ + { + type: 'image', + url: 'http://foo.sync?param=1' + }, + { + type: 'iframe', + url: 'http://foo.sync?param=2' + } + ] + } + }; + + describe('interpretResponse', function () { + it('Valid bid response - multi', function () { + let bidResponses = [ + { + requestId: '2e56e1af51a5d7', + cpm: 8.1, + width: 300, + height: 250, + creativeId: '221', + netRevenue: false, + ttl: 5, + currency: 'PLN', + ad: '' + }, + { + requestId: '2e56e1af51a5d8', + cpm: 5.68, + width: 640, + height: 480, + creativeId: '222', + netRevenue: false, + ttl: 5, + currency: 'PLN', + vastXml: '' + } + ]; + expect(spec.interpretResponse({body: response})).to.deep.have.same.members(bidResponses); + }); + + it('Valid bid response - single', function () { + delete response.seatbid[0].bid[1]; + let bidResponses = [ + { + requestId: '2e56e1af51a5d7', + cpm: 8.1, + width: 300, + height: 250, + creativeId: '221', + netRevenue: false, + ttl: 5, + currency: 'PLN', + ad: '' + } + ]; + expect(spec.interpretResponse({body: response})).to.deep.have.same.members(bidResponses); + }); + + it('Empty bid response', function () { + expect(spec.interpretResponse({})).to.be.empty; + }); + }); + describe('getUserSyncs', function () { + it('Valid syncs - all', function () { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + + let expectedSyncs = [ + { + type: 'image', + url: 'http://foo.sync?param=1' + }, + { + type: 'iframe', + url: 'http://foo.sync?param=2' + } + ]; + expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.deep.have.same.members(expectedSyncs); + }); + + it('Valid syncs - only image', function () { + let syncOptions = { + iframeEnabled: false, + pixelEnabled: true + }; + let expectedSyncs = [ + { + type: 'image', url: 'http://foo.sync?param=1' + } + ]; + expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.deep.have.same.members(expectedSyncs); + }); + + it('Valid syncs - only iframe', function () { + let syncOptions = {iframeEnabled: true, pixelEnabled: false}; + let expectedSyncs = [ + { + type: 'iframe', url: 'http://foo.sync?param=2' + } + ]; + expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.deep.have.same.members(expectedSyncs); + }); + + it('Valid syncs - empty', function () { + let syncOptions = {iframeEnabled: true, pixelEnabled: true}; + response.ext.usersync = {}; + expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.be.empty; + }); + }); +}); diff --git a/test/spec/modules/cedatoBidAdapter_spec.js b/test/spec/modules/cedatoBidAdapter_spec.js new file mode 100644 index 00000000000..969c06a64a2 --- /dev/null +++ b/test/spec/modules/cedatoBidAdapter_spec.js @@ -0,0 +1,91 @@ +import {expect} from 'chai'; +import {spec} from 'modules/cedatoBidAdapter'; + +describe('the cedato adapter', function () { + function getValidBidObject() { + return { + bidId: '2f4a613a702b6c', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + params: { + player_id: 1450133326, + } + }; + }; + + describe('isBidRequestValid', function() { + var bid; + + beforeEach(function() { + bid = getValidBidObject(); + }); + + it('should fail validation if the bid isn\'t defined or not an object', function() { + var result = spec.isBidRequestValid(); + + expect(result).to.equal(false); + + result = spec.isBidRequestValid('not an object'); + + expect(result).to.equal(false); + }); + }); + describe('buildRequests', function() { + var bid, bidRequestObj; + + beforeEach(function() { + bid = getValidBidObject(); + bidRequestObj = {refererInfo: {referer: 'prebid.js'}}; + }); + + it('should build a very basic request', function() { + var request = spec.buildRequests([bid], bidRequestObj); + expect(request.method).to.equal('POST'); + }); + }); + + describe('interpretResponse', function() { + var bid, serverResponse, bidderRequest; + + beforeEach(function() { + bid = getValidBidObject(); + serverResponse = { + body: { + bidid: '0.36157306192821', + seatbid: [ + { + seat: '0', + bid: [{ + gp: { + 'negative': 0.496954, + 'positive': 0.503046, + 'class': '0' + }, + id: '0.75549202124378', + adomain: 'cedato.com', + uuid: bid.bidId, + crid: '1450133326', + adm: "
    \n\n\n", + h: 250, + w: 300, + price: '0.1' + }] + } + ], + cur: 'USD' + } + }; + bidderRequest = { + bids: [bid] + }; + }); + + it('should return an array of bid responses', function() { + var responses = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(responses).to.be.an('array').with.length(1); + }); + }); +}); diff --git a/test/spec/modules/cleanmedianetBidAdapter_spec.js b/test/spec/modules/cleanmedianetBidAdapter_spec.js new file mode 100644 index 00000000000..09f76806fd7 --- /dev/null +++ b/test/spec/modules/cleanmedianetBidAdapter_spec.js @@ -0,0 +1,603 @@ +import { expect } from 'chai'; +import { spec } from 'modules/cleanmedianetBidAdapter'; +import { helper } from 'modules/cleanmedianetBidAdapter'; +import * as utils from 'src/utils'; +import { newBidder } from '../../../src/adapters/bidderFactory'; +import { deepClone } from 'src/utils'; + +const supplyPartnerId = '123'; +const adapter = newBidder(spec); +describe('CleanmedianetAdapter', function() { + describe('Is String start with search ', function() { + it('check if a string started with', function() { + expect(helper.startsWith('cleanmediaads.com', 'cleanmediaads')).to.equal( + true + ); + }); + }); + + describe('inherited functions', function() { + it('exists and is a function', function() { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function() { + it('should validate supply-partner ID', function() { + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: 123 } }) + ).to.equal(false); + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: '123' } }) + ).to.equal(true); + }); + + it('should validate bid floor', function() { + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: '123' } }) + ).to.equal(true); // bidfloor has a default + expect( + spec.isBidRequestValid({ + params: { supplyPartnerId: '123', bidfloor: '123' } + }) + ).to.equal(false); + expect( + spec.isBidRequestValid({ + params: { supplyPartnerId: '123', bidfloor: 0.1 } + }) + ).to.equal(true); + }); + + it('should validate adpos', function() { + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: '123' } }) + ).to.equal(true); // adpos has a default + expect( + spec.isBidRequestValid({ + params: { supplyPartnerId: '123', adpos: '123' } + }) + ).to.equal(false); + expect( + spec.isBidRequestValid({ + params: { supplyPartnerId: '123', adpos: 0.1 } + }) + ).to.equal(true); + }); + + it('should validate instl', function() { + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: '123' } }) + ).to.equal(true); // adpos has a default + expect( + spec.isBidRequestValid({ + params: { supplyPartnerId: '123', instl: '123' } + }) + ).to.equal(false); + expect( + spec.isBidRequestValid({ + params: { supplyPartnerId: '123', instl: -1 } + }) + ).to.equal(false); + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: '123', instl: 0 } }) + ).to.equal(true); + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: '123', instl: 1 } }) + ).to.equal(true); + expect( + spec.isBidRequestValid({ params: { supplyPartnerId: '123', instl: 2 } }) + ).to.equal(false); + }); + }); + + describe('buildRequests', function() { + const bidRequest = { + adUnitCode: 'adunit-code', + auctionId: '1d1a030790a475', + mediaTypes: { + banner: {} + }, + params: { + supplyPartnerId: supplyPartnerId + }, + sizes: [[300, 250], [300, 600]], + transactionId: 'a123456789', + refererInfo: { referer: 'http://examplereferer.com' }, + gdprConsent: { + consentString: 'some string', + gdprApplies: true + } + }; + it('returns an array', function() { + let response; + response = spec.buildRequests([]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + response = spec.buildRequests([bidRequest], bidRequest); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + const adUnit1 = Object.assign({}, utils.deepClone(bidRequest), { + auctionId: '1', + adUnitCode: 'a' + }); + const adUnit2 = Object.assign({}, utils.deepClone(bidRequest), { + auctionId: '1', + adUnitCode: 'b' + }); + response = spec.buildRequests([adUnit1, adUnit2], bidRequest); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(2); + }); + + it('builds request correctly', function() { + let stub = sinon + .stub(utils, 'getTopWindowUrl') + .returns('http://www.test.com/page.html'); + let bidRequest2 = deepClone(bidRequest); + bidRequest2.refererInfo.referer = 'http://www.test.com/page.html'; + let response = spec.buildRequests([bidRequest], bidRequest2)[0]; + expect(response.data.site.domain).to.equal('www.test.com'); + expect(response.data.site.page).to.equal('http://www.test.com/page.html'); + expect(response.data.site.ref).to.equal('http://www.test.com/page.html'); + expect(response.data.imp.length).to.equal(1); + expect(response.data.imp[0].id).to.equal(bidRequest.transactionId); + expect(response.data.imp[0].instl).to.equal(0); + expect(response.data.imp[0].tagid).to.equal(bidRequest.adUnitCode); + expect(response.data.imp[0].bidfloor).to.equal(0); + expect(response.data.imp[0].bidfloorcur).to.equal('USD'); + const bidRequestWithInstlEquals1 = utils.deepClone(bidRequest); + bidRequestWithInstlEquals1.params.instl = 1; + response = spec.buildRequests( + [bidRequestWithInstlEquals1], + bidRequest2 + )[0]; + expect(response.data.imp[0].instl).to.equal( + bidRequestWithInstlEquals1.params.instl + ); + const bidRequestWithInstlEquals0 = utils.deepClone(bidRequest); + bidRequestWithInstlEquals0.params.instl = 1; + response = spec.buildRequests( + [bidRequestWithInstlEquals0], + bidRequest2 + )[0]; + expect(response.data.imp[0].instl).to.equal( + bidRequestWithInstlEquals0.params.instl + ); + const bidRequestWithBidfloorEquals1 = utils.deepClone(bidRequest); + bidRequestWithBidfloorEquals1.params.bidfloor = 1; + response = spec.buildRequests( + [bidRequestWithBidfloorEquals1], + bidRequest2 + )[0]; + expect(response.data.imp[0].bidfloor).to.equal( + bidRequestWithBidfloorEquals1.params.bidfloor + ); + stub.restore(); + }); + + it('builds request banner object correctly', function() { + let response; + const bidRequestWithBanner = utils.deepClone(bidRequest); + bidRequestWithBanner.mediaTypes = { + banner: { + sizes: [[300, 250], [120, 600]] + } + }; + response = spec.buildRequests([bidRequestWithBanner], bidRequest)[0]; + expect(response.data.imp[0].banner.w).to.equal( + bidRequestWithBanner.mediaTypes.banner.sizes[0][0] + ); + expect(response.data.imp[0].banner.h).to.equal( + bidRequestWithBanner.mediaTypes.banner.sizes[0][1] + ); + expect(response.data.imp[0].banner.pos).to.equal(0); + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithBanner); + bidRequestWithPosEquals1.params.pos = 1; + response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; + expect(response.data.imp[0].banner.pos).to.equal( + bidRequestWithPosEquals1.params.pos + ); + }); + + it('builds request video object correctly', function() { + let response; + const bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes = { + video: { + sizes: [[300, 250], [120, 600]] + } + }; + response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[0].video.w).to.equal( + bidRequestWithVideo.mediaTypes.video.sizes[0][0] + ); + expect(response.data.imp[0].video.h).to.equal( + bidRequestWithVideo.mediaTypes.video.sizes[0][1] + ); + expect(response.data.imp[0].video.pos).to.equal(0); + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals1.params.pos = 1; + response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; + expect(response.data.imp[0].video.pos).to.equal( + bidRequestWithPosEquals1.params.pos + ); + }); + + it('builds request video object correctly with context', function() { + let response; + const bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes = { + video: { + context: 'instream' + } + }; + response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[0].video.ext.context).to.equal('instream'); + bidRequestWithVideo.mediaTypes.video.context = 'outstream'; + + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals1.mediaTypes.video.context = 'outstream'; + response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; + expect(response.data.imp[0].video.ext.context).to.equal('outstream'); + + const bidRequestWithPosEquals2 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals2.mediaTypes.video.context = null; + response = spec.buildRequests([bidRequestWithPosEquals2], bidRequest)[0]; + expect(response.data.imp[0].video.ext.context).to.equal(null); + }); + it('builds request video object correctly with multi-dimensions size array', function () { + let bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes.video = { + playerSize: [[304, 254], [305, 255]], + context: 'instream' + }; + + let response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[1].video.w).to.equal(304); + expect(response.data.imp[1].video.h).to.equal(254); + + bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes.video = { + playerSize: [304, 254] + }; + + response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[1].video.w).to.equal(304); + expect(response.data.imp[1].video.h).to.equal(254); + }); + + it('builds request with gdpr consent', function() { + let response = spec.buildRequests([bidRequest], bidRequest)[0]; + expect(response.data.ext).to.have.property('gdpr_consent'); + expect(response.data.ext.gdpr_consent.consent_string).to.equal( + 'some string' + ); + expect(response.data.ext.gdpr_consent.consent_required).to.equal(true); + }); + }); + + describe('interpretResponse', function() { + const bannerBidRequest = { + adUnitCode: 'adunit-code', + auctionId: '1d1a030790a475', + mediaTypes: { + banner: {} + }, + params: { + supplyPartnerId: supplyPartnerId + }, + sizes: [[300, 250], [300, 600]], + transactionId: 'a123456789', + bidId: '111', + refererInfo: { referer: 'http://examplereferer.com' } + }; + + const videoBidRequest = { + adUnitCode: 'adunit-code', + auctionId: '1d1a030790a475', + mediaTypes: { + video: {} + }, + params: { + supplyPartnerId: supplyPartnerId + }, + sizes: [[300, 250], [300, 600]], + transactionId: 'a123456789', + bidId: '111', + refererInfo: { referer: 'http://examplereferer.com' } + }; + + const rtbResponse = { + id: 'imp_5b05b9fde4b09084267a556f', + bidid: 'imp_5b05b9fde4b09084267a556f', + cur: 'USD', + ext: { + utrk: [ + { type: 'iframe', url: '//bidder.cleanmediaads.com/user/sync/1' }, + { type: 'image', url: '//bidder.cleanmediaads.com/user/sync/2' } + ] + }, + seatbid: [ + { + seat: 'seat1', + group: 0, + bid: [ + { + id: '0', + impid: '1', + price: 2.016, + adid: '579ef31bfa788b9d2000d562', + nurl: + 'https://bidder.cleanmediaads.com/pix/monitoring/win_notice/imp_5b05b9fde4b09084267a556f/im.gif?r=imp_5b05b9fde4b09084267a556f&i=1&a=579ef31bfa788b9d2000d562&b=0&p=${AUCTION_PRICE}', + adm: + '', + adomain: ['aaa.com'], + cid: '579ef268fa788b9d2000d55c', + crid: '579ef31bfa788b9d2000d562', + attr: [], + h: 600, + w: 120, + ext: { + vast_url: 'http://my.vast.com', + utrk: [{ type: 'iframe', url: '//p.partner1.io/user/sync/1' }] + } + } + ] + }, + { + seat: 'seat2', + group: 0, + bid: [ + { + id: '1', + impid: '1', + price: 3, + adid: '542jlhdfd2112jnjf3x', + nurl: + 'https://bidder.cleanmediaads.com/pix/monitoring/win_notice/imp_5b05b9fde4b09084267a556f/im.gif?r=imp_5b05b9fde4b09084267a556f&i=1&a=579ef31bfa788b9d2000d562&b=0&p=${AUCTION_PRICE}', + adm: + ' ', + adomain: ['bbb.com'], + cid: 'fgdlwjh2498ydjhg1', + crid: 'kjh34297ydh2133d', + attr: [], + h: 250, + w: 300, + ext: { + utrk: [{ type: 'image', url: '//p.partner2.io/user/sync/1' }] + } + } + ] + } + ] + }; + + it('returns an empty array on missing response', function() { + let response; + + response = spec.interpretResponse(undefined, { + bidRequest: bannerBidRequest + }); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + + response = spec.interpretResponse({}, { bidRequest: bannerBidRequest }); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + }); + + it('aggregates banner bids from all seat bids', function() { + const response = spec.interpretResponse( + { body: rtbResponse }, + { bidRequest: bannerBidRequest } + ); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + + const ad0 = response[0]; + expect(ad0.requestId).to.equal(bannerBidRequest.bidId); + expect(ad0.cpm).to.equal(rtbResponse.seatbid[1].bid[0].price); + expect(ad0.width).to.equal(rtbResponse.seatbid[1].bid[0].w); + expect(ad0.height).to.equal(rtbResponse.seatbid[1].bid[0].h); + expect(ad0.ttl).to.equal(60 * 10); + expect(ad0.creativeId).to.equal(rtbResponse.seatbid[1].bid[0].crid); + expect(ad0.netRevenue).to.equal(true); + expect(ad0.currency).to.equal( + rtbResponse.seatbid[1].bid[0].cur || rtbResponse.cur || 'USD' + ); + expect(ad0.ad).to.equal(rtbResponse.seatbid[1].bid[0].adm); + expect(ad0.vastXml).to.be.an('undefined'); + expect(ad0.vastUrl).to.be.an('undefined'); + }); + + it('aggregates video bids from all seat bids', function() { + const response = spec.interpretResponse( + { body: rtbResponse }, + { bidRequest: videoBidRequest } + ); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + + const ad0 = response[0]; + expect(ad0.requestId).to.equal(videoBidRequest.bidId); + expect(ad0.cpm).to.equal(rtbResponse.seatbid[0].bid[0].price); + expect(ad0.width).to.equal(rtbResponse.seatbid[0].bid[0].w); + expect(ad0.height).to.equal(rtbResponse.seatbid[0].bid[0].h); + expect(ad0.ttl).to.equal(60 * 10); + expect(ad0.creativeId).to.equal(rtbResponse.seatbid[0].bid[0].crid); + expect(ad0.netRevenue).to.equal(true); + expect(ad0.currency).to.equal( + rtbResponse.seatbid[0].bid[0].cur || rtbResponse.cur || 'USD' + ); + expect(ad0.ad).to.be.an('undefined'); + expect(ad0.vastXml).to.equal(rtbResponse.seatbid[0].bid[0].adm); + expect(ad0.vastUrl).to.equal(rtbResponse.seatbid[0].bid[0].ext.vast_url); + }); + + it('aggregates user-sync pixels', function() { + const response = spec.getUserSyncs({}, [{ body: rtbResponse }]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(4); + expect(response[0].type).to.equal(rtbResponse.ext.utrk[0].type); + expect(response[0].url).to.equal( + rtbResponse.ext.utrk[0].url + '?gc=missing' + ); + expect(response[1].type).to.equal(rtbResponse.ext.utrk[1].type); + expect(response[1].url).to.equal( + rtbResponse.ext.utrk[1].url + '?gc=missing' + ); + expect(response[2].type).to.equal( + rtbResponse.seatbid[0].bid[0].ext.utrk[0].type + ); + expect(response[2].url).to.equal( + rtbResponse.seatbid[0].bid[0].ext.utrk[0].url + '?gc=missing' + ); + expect(response[3].type).to.equal( + rtbResponse.seatbid[1].bid[0].ext.utrk[0].type + ); + expect(response[3].url).to.equal( + rtbResponse.seatbid[1].bid[0].ext.utrk[0].url + '?gc=missing' + ); + }); + + it('supports configuring outstream renderers', function() { + const videoResponse = { + id: '64f32497-b2f7-48ec-9205-35fc39894d44', + bidid: 'imp_5c24924de4b0d106447af333', + cur: 'USD', + seatbid: [ + { + seat: '3668', + group: 0, + bid: [ + { + id: 'gb_1', + impid: 'afbb5852-7cea-4a81-aa9a-a41aab505c23', + price: 5.0, + adid: '1274', + nurl: + 'https://bidder.cleanmediaads.com/pix/1275/win_notice/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&p=${AUCTION_PRICE}', + adomain: [], + adm: + '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', + cid: '3668', + crid: '1274', + cat: [], + attr: [], + h: 250, + w: 300, + ext: { + vast_url: + 'https://bidder.cleanmediaads.com/pix/1275/vast_o/imp_5c24924de4b0d106447af333/im.xml?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&w=300&h=250&vatu=aHR0cHM6Ly9zdGF0aWMuZ2FtYmlkLmlvL2RlbW8vdmFzdC54bWw&vwarv', + imptrackers: [ + 'https://bidder.cleanmediaads.com/pix/1275/imp/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1' + ] + } + } + ] + } + ], + ext: { + utrk: [ + { + type: 'image', + url: + 'https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675' + } + ] + } + }; + const videoRequest = deepClone(videoBidRequest); + videoRequest.mediaTypes.video.context = 'outstream'; + const result = spec.interpretResponse( + { body: videoResponse }, + { bidRequest: videoRequest } + ); + expect(result[0].renderer).to.not.equal(undefined); + }); + + it('validates in/existing of gdpr consent', function() { + let videoResponse = { + id: '64f32497-b2f7-48ec-9205-35fc39894d44', + bidid: 'imp_5c24924de4b0d106447af333', + cur: 'USD', + seatbid: [ + { + seat: '3668', + group: 0, + bid: [ + { + id: 'gb_1', + impid: 'afbb5852-7cea-4a81-aa9a-a41aab505c23', + price: 5.0, + adid: '1274', + nurl: + 'https://bidder.cleanmediaads.com/pix/1275/win_notice/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&p=${AUCTION_PRICE}', + adomain: [], + adm: + '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', + cid: '3668', + crid: '1274', + cat: [], + attr: [], + h: 250, + w: 300, + ext: { + vast_url: + 'https://bidder.cleanmediaads.com/pix/1275/vast_o/imp_5c24924de4b0d106447af333/im.xml?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&w=300&h=250&vatu=aHR0cHM6Ly9zdGF0aWMuZ2FtYmlkLmlvL2RlbW8vdmFzdC54bWw&vwarv', + imptrackers: [ + 'https://bidder.cleanmediaads.com/pix/1275/imp/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1' + ] + } + } + ] + } + ], + ext: { + utrk: [ + { + type: 'image', + url: + 'https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675' + } + ] + } + }; + let gdprConsent = { + gdprApplies: true, + consentString: 'consent string' + }; + let result = spec.getUserSyncs( + {}, + [{ body: videoResponse }], + gdprConsent + ); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal( + 'https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675&gc=consent%20string' + ); + + gdprConsent.gdprApplies = false; + result = spec.getUserSyncs({}, [{ body: videoResponse }], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal( + 'https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675&gc=missing' + ); + + videoResponse.ext.utrk[0].url = + 'https://bidder.cleanmediaads.com/pix/1275/scm'; + result = spec.getUserSyncs({}, [{ body: videoResponse }], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal( + 'https://bidder.cleanmediaads.com/pix/1275/scm?gc=missing' + ); + }); + }); +}); diff --git a/test/spec/modules/clickforceBidAdapter_spec.js b/test/spec/modules/clickforceBidAdapter_spec.js index 8b0955590a0..3d4fc70c057 100644 --- a/test/spec/modules/clickforceBidAdapter_spec.js +++ b/test/spec/modules/clickforceBidAdapter_spec.js @@ -2,16 +2,16 @@ import { expect } from 'chai'; import { spec } from 'modules/clickforceBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('ClickforceAdapter', () => { +describe('ClickforceAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'clickforce', 'params': { @@ -26,11 +26,11 @@ describe('ClickforceAdapter', () => { 'auctionId': '1d1a030790a475' }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -40,7 +40,7 @@ describe('ClickforceAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [{ 'bidder': 'clickforce', 'params': { @@ -57,12 +57,12 @@ describe('ClickforceAdapter', () => { const request = spec.buildRequests(bidRequests); - it('sends bid request to our endpoint via POST', () => { + it('sends bid request to our endpoint via POST', function () { expect(request.method).to.equal('POST'); }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let response = [{ 'cpm': 0.5, 'width': '300', @@ -78,25 +78,88 @@ describe('ClickforceAdapter', () => { 'zone': '6682' }]; - it('should get the correct bid response', () => { - let expectedResponse = [{ - 'requestId': '220ed41385952a', - 'cpm': 0.5, - 'width': '300', - 'height': '250', - 'creativeId': '1f99ac5c3ef10a4097499a5686b30aff-6682', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 60, - 'ad': '' - }]; + let response1 = [{ + 'cpm': 0.0625, + 'width': '3', + 'height': '3', + 'callback_uid': '2e27ec595bf1a', + 'type': 'public Bid', + 'tag': { + 'content': { + 'title': 'title', + 'content': 'content', + 'advertiser': 'advertiser', + 'button_text': 'button_text', + 'image': 'image', + 'icon': 'icon' + }, + 'cu': ['cu'], + 'iu': ['iu'], + 'p': '6878:11062:32586:8380573788dad9b9fc17edde444c4dcf:2795' + }, + 'creativeId': '8380573788dad9b9fc17edde444c4dcf-6878', + 'requestId': '2e27ec595bf1a', + 'currency': 'USD', + 'ttl': 60, + 'netRevenue': true, + 'zone': '6878' + }]; + + let expectedResponse = [{ + 'requestId': '220ed41385952a', + 'cpm': 0.5, + 'width': '300', + 'height': '250', + 'creativeId': '1f99ac5c3ef10a4097499a5686b30aff-6682', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 60, + 'ad': '', + 'mediaType': 'banner', + }]; + + let expectedResponse1 = [{ + 'requestId': '2e27ec595bf1a', + 'cpm': 0.0625, + 'width': '3', + 'height': '3', + 'creativeId': '8380573788dad9b9fc17edde444c4dcf-6878', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 60, + 'mediaType': 'native', + 'native': { + 'image': { + 'url': 'image', + 'width': 1600, + 'height': 900 + }, + 'title': 'title', + 'sponsoredBy': 'advertiser', + 'body': 'content', + 'icon': { + 'url': 'icon', + 'width': 900, + 'height': 900 + }, + 'clickUrl': 'cu', + 'impressionTrackers': ['iu'] + } + }]; + it('should get the correct bid response by display ad', function () { let bidderRequest; let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('handles empty bid response', () => { + it('should get the correct bid response by native ad', function () { + let bidderRequest; + let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse1[0])); + }); + + it('handles empty bid response', function () { let response = { body: {} }; @@ -105,8 +168,8 @@ describe('ClickforceAdapter', () => { }); }); - describe('getUserSyncs function', () => { - it('should register type is iframe', () => { + describe('getUserSyncs function', function () { + it('should register type is iframe', function () { const syncOptions = { 'iframeEnabled': 'true' } @@ -115,7 +178,7 @@ describe('ClickforceAdapter', () => { expect(userSync[0].url).to.equal('https://cdn.doublemax.net/js/capmapping.htm'); }); - it('should register type is image', () => { + it('should register type is image', function () { const syncOptions = { 'pixelEnabled': 'true' } diff --git a/test/spec/modules/coinzillaBidAdapter_spec.js b/test/spec/modules/coinzillaBidAdapter_spec.js new file mode 100644 index 00000000000..7a0c745d57d --- /dev/null +++ b/test/spec/modules/coinzillaBidAdapter_spec.js @@ -0,0 +1,120 @@ +import {assert, expect} from 'chai'; +import {spec} from 'modules/coinzillaBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT_URL = 'https://request.czilladx.com/serve/request.php'; + +describe('coinzillaBidAdapter', function () { + const adapter = newBidder(spec); + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'coinzilla', + 'params': { + placementId: 'testPlacementId' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '1234asdf1234', + 'bidderRequestId': '1234asdf1234asdf', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' + }; + it('should return true where required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'coinzilla', + 'params': { + placementId: 'testPlacementId' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + }, + { + 'bidder': 'coinzilla', + 'params': { + placementId: 'testPlacementId' + }, + 'adUnitCode': 'adunit-code2', + 'sizes': [ + [300, 250] + ], + 'bidId': '382091349b149f"', + 'bidderRequestId': '1f9c98192de251', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + } + ]; + + let bidderRequests = { + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'http://example.com', + 'stack': ['http://example.com'] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequests); + it('sends bid request to our endpoint via POST', function () { + expect(request[0].method).to.equal('POST'); + expect(request[1].method).to.equal('POST'); + }); + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.equal(ENDPOINT_URL); + expect(request[1].url).to.equal(ENDPOINT_URL); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = [ + { + 'method': 'POST', + 'url': ENDPOINT_URL, + 'data': { + 'placementId': 'testPlacementId', + 'width': '300', + 'height': '200', + 'bidId': 'bidId123', + 'referer': 'www.example.com' + } + + } + ]; + let serverResponse = { + body: { + 'ad': '

    I am an ad

    ', + 'cpm': 4.2, + 'creativeId': '12345asdfg', + 'currency': 'EUR', + 'statusMessage': 'Bid available', + 'requestId': 'bidId123', + 'width': 300, + 'height': 250, + 'netRevenue': true + } + }; + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': 'bidId123', + 'cpm': 4.2, + 'width': 300, + 'height': 250, + 'creativeId': '12345asdfg', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 3000, + 'ad': '

    I am an ad

    ' + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + }); +}); diff --git a/test/spec/modules/collectcentBidAdapter_spec.js b/test/spec/modules/collectcentBidAdapter_spec.js new file mode 100644 index 00000000000..04c819992cd --- /dev/null +++ b/test/spec/modules/collectcentBidAdapter_spec.js @@ -0,0 +1,118 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/collectcentBidAdapter'; + +describe('Collectcent', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'collectcent', + bidderRequestId: '145e1d6a7837c9', + params: { + placementId: 123, + traffic: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + describe('isBidRequestValid', function () { + it('Should return true when placementId can be cast to a number', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when placementId is not a number', function () { + bid.params.placementId = 'aaa'; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('//publishers.motionspots.com/?c=o&m=multi'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + let placements = data['placements']; + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.all.keys('placementId', 'bidId', 'traffic', 'sizes'); + expect(placement.placementId).to.be.a('number'); + expect(placement.bidId).to.be.a('string'); + expect(placement.traffic).to.be.a('string'); + expect(placement.sizes).to.be.an('array'); + } + }); + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

    Hello ad

    ', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + + describe('getUserSyncs', function () { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and `', function () { + 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('//publishers.motionspots.com/?c=o&m=cookie'); + }); + }); +}); diff --git a/test/spec/modules/colombiaBidAdapter_spec.js b/test/spec/modules/colombiaBidAdapter_spec.js new file mode 100644 index 00000000000..5a8678e866c --- /dev/null +++ b/test/spec/modules/colombiaBidAdapter_spec.js @@ -0,0 +1,152 @@ +import { expect } from 'chai'; +import { spec } from 'modules/colombiaBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const HOST_NAME = document.location.protocol + '//' + window.location.host; +const ENDPOINT = 'https://ade.clmbtech.com/cde/prebid.htm'; + +describe('colombiaBidAdapter', function() { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'colombia', + 'params': { + placementId: '307466' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when placementId not passed correctly', function () { + bid.params.placementId = ''; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'colombia', + 'params': { + placementId: '307466' + }, + 'adUnitCode': 'adunit-code1', + 'sizes': [ + [300, 250] + ], + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + }, + { + 'bidder': 'colombia', + 'params': { + placementId: '307466' + }, + 'adUnitCode': 'adunit-code2', + 'sizes': [ + [300, 250] + ], + 'bidId': '382091349b149f"', + 'bidderRequestId': '"1f9c98192de251"', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + } + ]; + + const request = spec.buildRequests(bidRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request[0].method).to.equal('POST'); + expect(request[1].method).to.equal('POST'); + }); + + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.equal(ENDPOINT); + expect(request[1].url).to.equal(ENDPOINT); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = [ + { + 'method': 'POST', + 'url': ENDPOINT, + 'data': { + 'v': 'hb1', + 'p': '307466', + 'w': '300', + 'h': '250', + 'cb': 12892917383, + 'r': 'http%3A%2F%2Flocalhost%3A9876%2F%3Fid%3D74552836', + 'uid': '23beaa6af6cdde', + 't': 'i', + 'd': HOST_NAME + } + } + ]; + + let serverResponse = { + body: { + 'ad': '
    This is test case
    ', + 'cpm': 3.14, + 'creativeId': '6b958110-612c-4b03-b6a9-7436c9f746dc-1sk24', + 'currency': 'USD', + 'statusMessage': 'Bid available', + 'uid': '23beaa6af6cdde', + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'ttl': 600 + } + }; + + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': '23beaa6af6cdde', + 'cpm': 3.14, + 'width': 300, + 'height': 250, + 'creativeId': '6b958110-612c-4b03-b6a9-7436c9f746dc-1sk24', + 'dealId': '', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 3000, + 'referrer': '', + 'ad': '
    This is test case
    ' + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + + it('handles empty bid response', function () { + let response = { + body: { + 'uid': '2c0b634db95a01', + 'height': 0, + 'crid': '', + 'statusMessage': 'Bid returned empty or error response', + 'width': 0, + 'cpm': 0 + } + }; + let result = spec.interpretResponse(response, bidRequest[0]); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index 54952fbf4b5..62b4158676e 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import {spec} from '../../../modules/colossussspBidAdapter'; -describe('ColossussspAdapter', () => { +describe('ColossussspAdapter', function () { let bid = { bidId: '2dd581a2b6281d', bidder: 'colossusssp', @@ -15,31 +15,31 @@ describe('ColossussspAdapter', () => { transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' }; - describe('isBidRequestValid', () => { - it('Should return true when placement_id can be cast to a number', () => { + describe('isBidRequestValid', function () { + it('Should return true when placement_id can be cast to a number', function () { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false when placement_id is not a number', () => { + it('Should return false when placement_id is not a number', function () { bid.params.placement_id = 'aaa'; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let serverRequest = spec.buildRequests([bid]); - it('Creates a ServerRequest object with method, URL and data', () => { + it('Creates a ServerRequest object with method, URL and data', function () { expect(serverRequest).to.exist; expect(serverRequest.method).to.exist; expect(serverRequest.url).to.exist; expect(serverRequest.data).to.exist; }); - it('Returns POST method', () => { + it('Returns POST method', function () { expect(serverRequest.method).to.equal('POST'); }); - it('Returns valid URL', () => { + it('Returns valid URL', function () { expect(serverRequest.url).to.equal('//colossusssp.com/?c=o&m=multi'); }); - it('Returns valid data if array of bids is valid', () => { + it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; expect(data).to.be.an('object'); expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); @@ -59,13 +59,13 @@ describe('ColossussspAdapter', () => { expect(placement.sizes).to.be.an('array'); } }); - it('Returns empty data if no valid requests are passed', () => { + it('Returns empty data if no valid requests are passed', function () { serverRequest = spec.buildRequests([]); let data = serverRequest.data; expect(data.placements).to.be.an('array').that.is.empty; }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let resObject = { body: [ { requestId: '123', @@ -81,7 +81,7 @@ describe('ColossussspAdapter', () => { } ] }; let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', () => { + it('Returns an array of valid server responses if response object is valid', function () { expect(serverResponses).to.be.an('array').that.is.not.empty; for (let i = 0; i < serverResponses.length; i++) { let dataItem = serverResponses[i]; @@ -98,16 +98,16 @@ describe('ColossussspAdapter', () => { expect(dataItem.currency).to.be.a('string'); expect(dataItem.mediaType).to.be.a('string'); } - it('Returns an empty array if invalid response is passed', () => { + it('Returns an empty array if invalid response is passed', function () { serverResponses = spec.interpretResponse('invalid_response'); expect(serverResponses).to.be.an('array').that.is.empty; }); }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs', function () { let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', () => { + it('Returns valid URL and type', function () { expect(userSync).to.be.an('array').with.lengthOf(1); expect(userSync[0].type).to.exist; expect(userSync[0].url).to.exist; diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index 7607dfd2e4b..6be96427750 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -1,5 +1,5 @@ -import {setConfig, requestBidsHook, resetConsentData, userCMP, consentTimeout, allowAuction} from 'modules/consentManagement'; -import {gdprDataHandler} from 'src/adaptermanager'; +import {setConsentConfig, requestBidsHook, resetConsentData, userCMP, consentTimeout, allowAuction, staticConsentData} from 'modules/consentManagement'; +import {gdprDataHandler} from 'src/adapterManager'; import * as utils from 'src/utils'; import { config } from 'src/config'; @@ -7,47 +7,456 @@ let assert = require('chai').assert; let expect = require('chai').expect; describe('consentManagement', function () { - describe('setConfig tests:', () => { - describe('empty setConfig value', () => { - beforeEach(() => { + describe('setConsentConfig tests:', function () { + describe('empty setConsentConfig value', function () { + beforeEach(function () { sinon.stub(utils, 'logInfo'); }); - afterEach(() => { + afterEach(function () { utils.logInfo.restore(); config.resetConfig(); }); - it('should use system default values', () => { - setConfig({}); + it('should use system default values', function () { + setConsentConfig({}); expect(userCMP).to.be.equal('iab'); expect(consentTimeout).to.be.equal(10000); expect(allowAuction).to.be.true; - sinon.assert.callCount(utils.logInfo, 3); + sinon.assert.callCount(utils.logInfo, 4); }); }); - describe('valid setConfig value', () => { - afterEach(() => { + describe('valid setConsentConfig value', function () { + afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook); + $$PREBID_GLOBAL$$.requestBids.removeAll(); }); - it('results in all user settings overriding system defaults', () => { + it('results in all user settings overriding system defaults', function () { let allConfig = { cmpApi: 'iab', timeout: 7500, allowAuctionWithoutConsent: false }; - setConfig(allConfig); + setConsentConfig(allConfig); expect(userCMP).to.be.equal('iab'); expect(consentTimeout).to.be.equal(7500); expect(allowAuction).to.be.false; }); }); + + describe('static consent string setConsentConfig value', () => { + afterEach(() => { + config.resetConfig(); + $$PREBID_GLOBAL$$.requestBids.removeAll(); + }); + it('results in user settings overriding system defaults', () => { + let staticConfig = { + cmpApi: 'static', + timeout: 7500, + allowAuctionWithoutConsent: false, + consentData: { + getConsentData: { + 'gdprApplies': true, + 'hasGlobalScope': false, + 'consentData': 'BOOgjO9OOgjO9APABAENAi-AAAAWd7_______9____7_9uz_Gv_r_ff_3nW0739P1A_r_Oz_rm_-zzV44_lpQQRCEA' + }, + getVendorConsents: { + 'metadata': 'BOOgjO9OOgjO9APABAENAi-AAAAWd7_______9____7_9uz_Gv_r_ff_3nW0739P1A_r_Oz_rm_-zzV44_lpQQRCEA', + 'gdprApplies': true, + 'hasGlobalScope': false, + 'isEU': true, + 'cookieVersion': 1, + 'created': '2018-05-29T07:45:48.522Z', + 'lastUpdated': '2018-05-29T07:45:48.522Z', + 'cmpId': 15, + 'cmpVersion': 1, + 'consentLanguage': 'EN', + 'vendorListVersion': 34, + 'maxVendorId': 359, + 'purposeConsents': { + '1': true, + '2': true, + '3': true, + '4': true, + '5': true + }, + 'vendorConsents': { + '1': true, + '2': true, + '3': true, + '4': true, + '5': false, + '6': true, + '7': true, + '8': true, + '9': true, + '10': true, + '11': true, + '12': true, + '13': true, + '14': true, + '15': true, + '16': true, + '17': true, + '18': true, + '19': true, + '20': true, + '21': true, + '22': true, + '23': true, + '24': true, + '25': true, + '26': true, + '27': true, + '28': true, + '29': true, + '30': true, + '31': true, + '32': true, + '33': true, + '34': true, + '35': true, + '36': true, + '37': true, + '38': true, + '39': true, + '40': true, + '41': true, + '42': true, + '43': true, + '44': true, + '45': true, + '46': true, + '47': true, + '48': true, + '49': true, + '50': true, + '51': true, + '52': true, + '53': true, + '54': false, + '55': true, + '56': true, + '57': true, + '58': true, + '59': true, + '60': true, + '61': true, + '62': true, + '63': true, + '64': true, + '65': true, + '66': true, + '67': true, + '68': true, + '69': true, + '70': true, + '71': true, + '72': true, + '73': true, + '74': true, + '75': true, + '76': true, + '77': true, + '78': true, + '79': true, + '80': true, + '81': true, + '82': true, + '83': false, + '84': true, + '85': true, + '86': true, + '87': true, + '88': true, + '89': true, + '90': true, + '91': true, + '92': true, + '93': true, + '94': true, + '95': true, + '96': false, + '97': true, + '98': true, + '99': false, + '100': true, + '101': true, + '102': true, + '103': false, + '104': true, + '105': true, + '106': false, + '107': false, + '108': true, + '109': true, + '110': true, + '111': true, + '112': true, + '113': true, + '114': true, + '115': true, + '116': false, + '117': false, + '118': false, + '119': true, + '120': true, + '121': false, + '122': true, + '123': false, + '124': true, + '125': true, + '126': true, + '127': true, + '128': true, + '129': true, + '130': true, + '131': true, + '132': true, + '133': true, + '134': true, + '135': false, + '136': true, + '137': false, + '138': true, + '139': true, + '140': true, + '141': true, + '142': true, + '143': true, + '144': true, + '145': true, + '146': false, + '147': true, + '148': true, + '149': true, + '150': true, + '151': true, + '152': false, + '153': true, + '154': true, + '155': true, + '156': true, + '157': true, + '158': true, + '159': true, + '160': true, + '161': true, + '162': true, + '163': true, + '164': true, + '165': true, + '166': false, + '167': true, + '168': true, + '169': true, + '170': true, + '171': false, + '172': false, + '173': true, + '174': true, + '175': true, + '176': false, + '177': true, + '178': false, + '179': true, + '180': true, + '181': false, + '182': true, + '183': true, + '184': false, + '185': true, + '186': false, + '187': false, + '188': true, + '189': true, + '190': true, + '191': false, + '192': true, + '193': true, + '194': true, + '195': true, + '196': false, + '197': true, + '198': true, + '199': true, + '200': true, + '201': true, + '202': true, + '203': true, + '204': false, + '205': true, + '206': false, + '207': false, + '208': true, + '209': true, + '210': true, + '211': true, + '212': true, + '213': true, + '214': false, + '215': true, + '216': false, + '217': true, + '218': false, + '219': false, + '220': false, + '221': false, + '222': false, + '223': false, + '224': true, + '225': true, + '226': true, + '227': true, + '228': true, + '229': true, + '230': true, + '231': false, + '232': true, + '233': false, + '234': true, + '235': true, + '236': true, + '237': true, + '238': true, + '239': true, + '240': true, + '241': true, + '242': false, + '243': false, + '244': true, + '245': true, + '246': true, + '247': false, + '248': true, + '249': true, + '250': false, + '251': false, + '252': true, + '253': true, + '254': true, + '255': true, + '256': true, + '257': true, + '258': true, + '259': true, + '260': true, + '261': false, + '262': true, + '263': false, + '264': true, + '265': true, + '266': true, + '267': false, + '268': false, + '269': true, + '270': true, + '271': false, + '272': true, + '273': true, + '274': true, + '275': true, + '276': true, + '277': true, + '278': true, + '279': true, + '280': true, + '281': true, + '282': true, + '283': false, + '284': true, + '285': true, + '286': false, + '287': false, + '288': true, + '289': true, + '290': true, + '291': true, + '292': false, + '293': false, + '294': true, + '295': true, + '296': false, + '297': true, + '298': false, + '299': true, + '300': false, + '301': true, + '302': true, + '303': true, + '304': true, + '305': false, + '306': false, + '307': false, + '308': true, + '309': true, + '310': true, + '311': false, + '312': false, + '313': false, + '314': true, + '315': true, + '316': true, + '317': true, + '318': true, + '319': true, + '320': true, + '321': false, + '322': false, + '323': true, + '324': false, + '325': true, + '326': true, + '327': false, + '328': true, + '329': false, + '330': false, + '331': true, + '332': false, + '333': true, + '334': false, + '335': false, + '336': false, + '337': false, + '338': false, + '339': true, + '340': false, + '341': false, + '342': false, + '343': false, + '344': false, + '345': true, + '346': false, + '347': false, + '348': false, + '349': true, + '350': false, + '351': false, + '352': false, + '353': false, + '354': true, + '355': false, + '356': false, + '357': false, + '358': false, + '359': true + } + } + } + }; + + setConsentConfig(staticConfig); + expect(userCMP).to.be.equal('static'); + expect(consentTimeout).to.be.equal(0); // should always return without a timeout when config is used + expect(allowAuction).to.be.false; + expect(staticConsentData).to.be.equal(staticConfig.consentData); + }); + }); }); - describe('requestBidsHook tests:', () => { + describe('requestBidsHook tests:', function () { let goodConfigWithCancelAuction = { cmpApi: 'iab', timeout: 7500, @@ -62,71 +471,83 @@ describe('consentManagement', function () { let didHookReturn; - afterEach(() => { + afterEach(function () { gdprDataHandler.consentData = null; resetConsentData(); }); - describe('error checks:', () => { - describe('unknown CMP framework ID:', () => { - beforeEach(() => { - sinon.stub(utils, 'logWarn'); - }); + describe('error checks:', function () { + beforeEach(function () { + didHookReturn = false; + sinon.stub(utils, 'logWarn'); + sinon.stub(utils, 'logError'); + }); - afterEach(() => { - utils.logWarn.restore(); - config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook); - resetConsentData(); - }); + afterEach(function () { + utils.logWarn.restore(); + utils.logError.restore(); + config.resetConfig(); + $$PREBID_GLOBAL$$.requestBids.removeAll(); + resetConsentData(); + }); - it('should return Warning message and return to hooked function', () => { - let badCMPConfig = { - cmpApi: 'bad' - }; - setConfig(badCMPConfig); - expect(userCMP).to.be.equal(badCMPConfig.cmpApi); - - didHookReturn = false; - - requestBidsHook({}, () => { - didHookReturn = true; - }); - let consent = gdprDataHandler.getConsentData(); - sinon.assert.calledOnce(utils.logWarn); - expect(didHookReturn).to.be.true; - expect(consent).to.be.null; - }); + it('should throw a warning and return to hooked function when an unknown CMP framework ID is used', function () { + let badCMPConfig = { + cmpApi: 'bad' + }; + setConsentConfig(badCMPConfig); + expect(userCMP).to.be.equal(badCMPConfig.cmpApi); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gdprDataHandler.getConsentData(); + sinon.assert.calledOnce(utils.logWarn); + expect(didHookReturn).to.be.true; + expect(consent).to.be.null; + }); + + it('should throw proper errors when CMP is not found', function () { + setConsentConfig(goodConfigWithCancelAuction); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gdprDataHandler.getConsentData(); + // throw 2 errors; one for no bidsBackHandler and for CMP not being found (this is an error due to gdpr config) + sinon.assert.calledTwice(utils.logError); + expect(didHookReturn).to.be.false; + expect(consent).to.be.null; }); }); - describe('already known consentData:', () => { + describe('already known consentData:', function () { let cmpStub = sinon.stub(); - beforeEach(() => { + beforeEach(function () { didHookReturn = false; window.__cmp = function() {}; }); - afterEach(() => { + afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook); + $$PREBID_GLOBAL$$.requestBids.removeAll(); cmpStub.restore(); delete window.__cmp; resetConsentData(); }); - it('should bypass CMP and simply use previously stored consentData', () => { + it('should bypass CMP and simply use previously stored consentData', function () { let testConsentData = { gdprApplies: true, - metadata: 'xyz' + consentData: 'xyz' }; cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { args[2](testConsentData); }); - setConfig(goodConfigWithAllowAuction); - requestBidsHook({}, () => {}); + setConsentConfig(goodConfigWithAllowAuction); + requestBidsHook(() => {}, {}); cmpStub.restore(); // reset the stub to ensure it wasn't called during the second round of calls @@ -134,22 +555,22 @@ describe('consentManagement', function () { args[2](testConsentData); }); - requestBidsHook({}, () => { + requestBidsHook(() => { didHookReturn = true; - }); + }, {}); let consent = gdprDataHandler.getConsentData(); expect(didHookReturn).to.be.true; - expect(consent.consentString).to.equal(testConsentData.metadata); + expect(consent.consentString).to.equal(testConsentData.consentData); expect(consent.gdprApplies).to.be.true; sinon.assert.notCalled(cmpStub); }); }); - describe('CMP workflow for safeframe page', () => { + describe('CMP workflow for safeframe page', function () { let registerStub = sinon.stub(); - beforeEach(() => { + beforeEach(function () { didHookReturn = false; window.$sf = { ext: { @@ -161,23 +582,27 @@ describe('consentManagement', function () { sinon.stub(utils, 'logWarn'); }); - afterEach(() => { + afterEach(function () { delete window.$sf; config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook); + $$PREBID_GLOBAL$$.requestBids.removeAll(); registerStub.restore(); utils.logError.restore(); utils.logWarn.restore(); resetConsentData(); }); - it('should return the consent data from a safeframe callback', () => { + it('should return the consent data from a safeframe callback', function () { var testConsentData = { data: { msgName: 'cmpReturn', vendorConsents: { metadata: 'abc123def', gdprApplies: true + }, + vendorConsentData: { + consentData: 'abc123def', + gdprApplies: true } } }; @@ -185,10 +610,10 @@ describe('consentManagement', function () { args[2](testConsentData.data.msgName, testConsentData.data); }); - setConfig(goodConfigWithAllowAuction); - requestBidsHook({adUnits: [{ sizes: [[300, 250]] }]}, () => { + setConsentConfig(goodConfigWithAllowAuction); + requestBidsHook(() => { didHookReturn = true; - }); + }, {adUnits: [{ sizes: [[300, 250]] }]}); let consent = gdprDataHandler.getConsentData(); sinon.assert.notCalled(utils.logWarn); @@ -199,77 +624,92 @@ describe('consentManagement', function () { }); }); - describe('CMP workflow for iframed page', () => { - let eventStub = sinon.stub(); - let cmpStub = sinon.stub(); + describe('CMP workflow for iframed page', function () { + let ifr = null; + let stringifyResponse = false; - beforeEach(() => { - didHookReturn = false; - window.__cmp = function() {}; + beforeEach(function () { sinon.stub(utils, 'logError'); sinon.stub(utils, 'logWarn'); + ifr = createIFrameMarker(); + window.addEventListener('message', cmpMessageHandler, false); }); - afterEach(() => { + afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook); - eventStub.restore(); - cmpStub.restore(); + $$PREBID_GLOBAL$$.requestBids.removeAll(); delete window.__cmp; utils.logError.restore(); utils.logWarn.restore(); resetConsentData(); + document.body.removeChild(ifr); + window.removeEventListener('message', cmpMessageHandler); }); - it('should return the consent string from a postmessage + addEventListener response', () => { - let testConsentData = { - data: { - __cmpReturn: { - returnValue: { - gdprApplies: true, - metadata: 'BOJy+UqOJy+UqABAB+AAAAAZ+A==' + function createIFrameMarker() { + var ifr = document.createElement('iframe'); + ifr.width = 0; + ifr.height = 0; + ifr.name = '__cmpLocator'; + document.body.appendChild(ifr); + return ifr; + } + + function cmpMessageHandler(event) { + if (event && event.data) { + var data = event.data; + if (data.__cmpCall) { + var callId = data.__cmpCall.callId; + var returnValue = null; + var response = { + __cmpReturn: { + callId, + returnValue: { + consentData: 'encoded_consent_data_via_post_message', + gdprApplies: true, + }, + success: true } - } + }; + event.source.postMessage(stringifyResponse ? JSON.stringify(response) : response, '*'); } - }; - eventStub = sinon.stub(window, 'addEventListener').callsFake((...args) => { - args[1](testConsentData); - }); - cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { - args[2]({ - gdprApplies: true, - metadata: 'BOJy+UqOJy+UqABAB+AAAAAZ+A==' - }); + } + } + + // Run tests with JSON response and String response + // from CMP window postMessage listener. + testIFramedPage('with/JSON response', false); + testIFramedPage('with/String response', true); + + function testIFramedPage(testName, messageFormatString) { + it(`should return the consent string from a postmessage + addEventListener response - ${testName}`, (done) => { + stringifyResponse = messageFormatString; + setConsentConfig(goodConfigWithAllowAuction); + requestBidsHook(() => { + let consent = gdprDataHandler.getConsentData(); + sinon.assert.notCalled(utils.logWarn); + sinon.assert.notCalled(utils.logError); + expect(consent.consentString).to.equal('encoded_consent_data_via_post_message'); + expect(consent.gdprApplies).to.be.true; + done(); + }, {}); }); - - setConfig(goodConfigWithAllowAuction); - - requestBidsHook({}, () => { - didHookReturn = true; - }); - let consent = gdprDataHandler.getConsentData(); - - sinon.assert.notCalled(utils.logWarn); - sinon.assert.notCalled(utils.logError); - expect(didHookReturn).to.be.true; - expect(consent.consentString).to.equal('BOJy+UqOJy+UqABAB+AAAAAZ+A=='); - expect(consent.gdprApplies).to.be.true; - }); + } }); - describe('CMP workflow for normal pages:', () => { + describe('CMP workflow for normal pages:', function () { let cmpStub = sinon.stub(); - beforeEach(() => { + beforeEach(function () { didHookReturn = false; sinon.stub(utils, 'logError'); sinon.stub(utils, 'logWarn'); window.__cmp = function() {}; }); - afterEach(() => { + afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook); + $$PREBID_GLOBAL$$.requestBids.removeAll(); cmpStub.restore(); utils.logError.restore(); utils.logWarn.restore(); @@ -277,60 +717,62 @@ describe('consentManagement', function () { resetConsentData(); }); - it('performs lookup check and stores consentData for a valid existing user', () => { + it('performs lookup check and stores consentData for a valid existing user', function () { let testConsentData = { gdprApplies: true, - metadata: 'BOJy+UqOJy+UqABAB+AAAAAZ+A==' + consentData: 'BOJy+UqOJy+UqABAB+AAAAAZ+A==' }; cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { args[2](testConsentData); }); - setConfig(goodConfigWithAllowAuction); + setConsentConfig(goodConfigWithAllowAuction); - requestBidsHook({}, () => { + requestBidsHook(() => { didHookReturn = true; - }); + }, {}); let consent = gdprDataHandler.getConsentData(); sinon.assert.notCalled(utils.logWarn); sinon.assert.notCalled(utils.logError); expect(didHookReturn).to.be.true; - expect(consent.consentString).to.equal(testConsentData.metadata); + expect(consent.consentString).to.equal(testConsentData.consentData); expect(consent.gdprApplies).to.be.true; }); - it('throws an error when processCmpData check failed while config had allowAuction set to false', () => { - let testConsentData = null; + it('throws an error when processCmpData check failed while config had allowAuction set to false', function () { + let testConsentData = {}; + let bidsBackHandlerReturn = false; cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { args[2](testConsentData); }); - setConfig(goodConfigWithCancelAuction); + setConsentConfig(goodConfigWithCancelAuction); - requestBidsHook({}, () => { + requestBidsHook(() => { didHookReturn = true; - }); + }, { bidsBackHandler: () => bidsBackHandlerReturn = true }); let consent = gdprDataHandler.getConsentData(); sinon.assert.calledOnce(utils.logError); expect(didHookReturn).to.be.false; + expect(bidsBackHandlerReturn).to.be.true; expect(consent).to.be.null; }); - it('throws a warning + stores consentData + calls callback when processCmpData check failed while config had allowAuction set to true', () => { - let testConsentData = null; + it('throws a warning + stores consentData + calls callback when processCmpData check failed while config had allowAuction set to true', function () { + let testConsentData = {}; cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { args[2](testConsentData); }); - setConfig(goodConfigWithAllowAuction); + setConsentConfig(goodConfigWithAllowAuction); - requestBidsHook({}, () => { + requestBidsHook(() => { didHookReturn = true; - }); + }, {}); let consent = gdprDataHandler.getConsentData(); sinon.assert.calledOnce(utils.logWarn); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index b87ce6634f6..fc5e1d1b45a 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -1,215 +1,280 @@ -import {expect} from 'chai'; -import * as utils from 'src/utils'; -import {spec} from 'modules/consumableBidAdapter'; -import {config} from 'src/config'; - -const DEFAULT_OAD_CONTENT = ''; -const DEFAULT_AD_CONTENT = '' - -let getDefaultBidResponse = () => { - return { - id: '245730051428950632', - cur: 'USD', - seatbid: [{ - bid: [{ - id: 1, - impid: '245730051428950632', - price: 0.09, - adm: DEFAULT_OAD_CONTENT, - crid: 'creative-id', - h: 90, - w: 728, - dealid: 'deal-id', - ext: {sizeid: 225} - }] - }] - }; -}; +import { expect } from 'chai'; +import { spec } from 'modules/consumableBidAdapter'; +import { createBid } from 'src/bidfactory'; -let getBidParams = () => { - return { - placement: 1234567, - network: '9599.1', - unitId: '987654', - unitName: 'unitname', - zoneId: '9599.1' - }; -}; +const ENDPOINT = 'https://e.serverbid.com/api/v2'; +const SMARTSYNC_CALLBACK = 'serverbidCallBids'; -let getDefaultBidRequest = () => { - return { - bidderCode: 'consumable', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - bidderRequestId: '7101db09af0db2', - start: new Date().getTime(), - bids: [{ - bidder: 'consumable', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - placementCode: 'foo', - params: getBidParams() - }] - }; +const REQUEST = { + 'bidderCode': 'consumable', + 'auctionId': 'a4713c32-3762-4798-b342-4ab810ca770d', + 'bidderRequestId': '109f2a181342a9', + 'bidRequest': [{ + 'bidder': 'consumable', + 'params': { + 'networkId': 9969, + 'siteId': 730181, + 'unitId': 123456, + 'unitName': 'cnsmbl-unit' + }, + 'placementCode': 'div-gpt-ad-1487778092495-0', + 'sizes': [ + [728, 90], + [970, 90] + ], + 'bidId': '2b0f82502298c9', + 'bidderRequestId': '109f2a181342a9', + 'auctionId': 'a4713c32-3762-4798-b342-4ab810ca770d' + }, + { + 'bidder': 'consumable', + 'params': { + 'networkId': 9969, + 'siteId': 730181, + 'unitId': 123456, + 'unitName': 'cnsmbl-unit' + }, + 'placementCode': 'div-gpt-ad-1487778092495-0', + 'sizes': [ + [728, 90], + [970, 90] + ], + 'bidId': '123', + 'bidderRequestId': '109f2a181342a9', + 'auctionId': 'a4713c32-3762-4798-b342-4ab810ca770d' + }], + 'gdprConsent': { + 'consentString': 'consent-test', + 'gdprApplies': true + }, + 'start': 1487883186070, + 'auctionStart': 1487883186069, + 'timeout': 3000 }; -let getPixels = () => { - return ''; +const RESPONSE = { + 'headers': null, + 'body': { + 'user': { 'key': 'ue1-2d33e91b71e74929b4aeecc23f4376f1' }, + 'pixels': [{ 'type': 'image', 'url': '//sync.serverbid.com/ss/' }], + 'decisions': { + '2b0f82502298c9': { + 'adId': 2364764, + 'creativeId': 1950991, + 'flightId': 2788300, + 'campaignId': 542982, + 'clickUrl': 'https://e.serverbid.com/r', + 'impressionUrl': 'https://e.serverbid.com/i.gif', + 'contents': [{ + 'type': 'html', + 'body': '', + 'data': { + 'height': 90, + 'width': 728, + 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif', + 'fileName': 'b0ab77db8a7848c8b78931aed022a5ef.gif' + }, + 'template': 'image' + }], + 'height': 90, + 'width': 728, + 'events': [], + 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} + }, + '123': { + 'adId': 2364764, + 'creativeId': 1950991, + 'flightId': 2788300, + 'campaignId': 542982, + 'clickUrl': 'https://e.serverbid.com/r', + 'impressionUrl': 'https://e.serverbid.com/i.gif', + 'contents': [{ + 'type': 'html', + 'body': '', + 'data': { + 'height': 90, + 'width': 728, + 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif', + 'fileName': 'b0ab77db8a7848c8b78931aed022a5ef.gif' + }, + 'template': 'image' + }], + 'height': 90, + 'width': 728, + 'events': [], + 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} + } + } + } }; -describe('ConsumableAdapter', () => { - const CONSUMABLE_URL = '//adserver-us.adtech.advertising.com/pubapi/3.0/'; - const CONSUMABLE_TTL = 60; +describe('Consumable BidAdapter', function () { + let bidRequests; + let adapter = spec; - function createCustomBidRequest({bids, params} = {}) { - var bidderRequest = getDefaultBidRequest(); - if (bids && Array.isArray(bids)) { - bidderRequest.bids = bids; - } - if (params) { - bidderRequest.bids.forEach(bid => bid.params = params); - } - return bidderRequest; - } + beforeEach(function () { + bidRequests = [ + { + bidder: 'consumable', + params: { + networkId: '9969', + siteId: '730181', + unitId: '123456', + unitName: 'cnsmbl-unit' + }, + placementCode: 'header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + }); - describe('interpretResponse()', () => { - let bidderSettingsBackup; - let bidResponse; - let bidRequest; - let logWarnSpy; - - beforeEach(() => { - bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; - bidRequest = { - bidderCode: 'test-bidder-code', - bidId: 'bid-id', - unitName: 'unitname', - unitId: '987654', - zoneId: '9599.1', - network: '9599.1' + describe('bid request validation', function () { + it('should accept valid bid requests', function () { + let bid = { + bidder: 'consumable', + params: { + networkId: '9969', + siteId: '123', + unitId: '123456', + unitName: 'cnsmbl-unit' + } }; - bidResponse = { - body: getDefaultBidResponse() - }; - logWarnSpy = sinon.spy(utils, 'logWarn'); - }); - - afterEach(() => { - $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; - logWarnSpy.restore(); - }); - - it('should return formatted bid response with required properties', () => { - let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); - expect(formattedBidResponse).to.deep.equal({ - bidderCode: bidRequest.bidderCode, - requestId: 'bid-id', - ad: DEFAULT_AD_CONTENT, - cpm: 0.09, - width: 728, - height: 90, - creativeId: 'creative-id', - pubapiId: '245730051428950632', - currency: 'USD', - dealId: 'deal-id', - netRevenue: true, - ttl: 60 - }); + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should add formatted pixels to ad content when pixels are present in the response', () => { - bidResponse.body.ext = { - pixels: 'pixels-content' + it('should accept valid bid requests with extra fields', function () { + let bid = { + bidder: 'consumable', + params: { + networkId: '9969', + siteId: '123', + unitId: '123456', + unitName: 'cnsmbl-unit', + zoneId: '123' + } }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); - let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); + it('should reject bid requests without siteId', function () { + let bid = { + bidder: 'consumable', + params: { + networkId: '9969', + unitId: '123456', + unitName: 'cnsmbl-unit' + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - expect(formattedBidResponse.ad).to.equal(DEFAULT_AD_CONTENT + ''); - return true; + it('should reject bid requests without networkId', function () { + let bid = { + bidder: 'consumable', + params: { + siteId: '9969', + unitId: '123456', + unitName: 'cnsmbl-unit' + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests()', () => { - it('method exists and is a function', () => { - expect(spec.buildRequests).to.exist.and.to.be.a('function'); + describe('buildRequests validation', function () { + it('creates request data', function () { + let request = spec.buildRequests(bidRequests); + + expect(request).to.exist.and.to.be.a('object'); }); - describe('Consumable', () => { - it('should not return request when no bids are present', () => { - let [request] = spec.buildRequests([]); - expect(request).to.be.empty; - }); + it('request to consumable should contain a url', function () { + let request = spec.buildRequests(bidRequests); - it('should return request for endpoint', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(CONSUMABLE_URL); - }); + expect(request.url).to.have.string('serverbid.com'); + }); - it('should return url with pubapi bid option', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('cmd=bid;'); - }); + it('requires valid bids to make request', function () { + let request = spec.buildRequests([]); + expect(request.bidRequest).to.be.empty; + }); - it('should return url with version 2 of pubapi', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('v=2;'); - }); + it('sends bid request to ENDPOINT via POST', function () { + let request = spec.buildRequests(bidRequests); - it('should return url with cache busting option', () => { - let bidRequest = getDefaultBidRequest(); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.match(/misc=\d+/); - }); + expect(request.method).to.have.string('POST'); }); }); + describe('interpretResponse validation', function () { + it('response should have valid bidderCode', function () { + let bidRequest = spec.buildRequests(REQUEST.bidRequest, REQUEST); + let bid = createBid(1, bidRequest.bidRequest[0]); - describe('getUserSyncs()', () => { - let bidResponse; - let bidRequest; + expect(bid.bidderCode).to.equal('consumable'); + }); - beforeEach(() => { - $$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped = false; - config.setConfig({ - consumable: { - userSyncOn: 'bidResponse' - }, + it('response should include objects for all bids', function () { + let bids = spec.interpretResponse(RESPONSE, REQUEST); + expect(bids.length).to.equal(2); + }); + + it('registers bids', function () { + let bids = spec.interpretResponse(RESPONSE, REQUEST); + bids.forEach(b => { + expect(b).to.have.property('cpm'); + expect(b.cpm).to.be.above(0); + expect(b).to.have.property('requestId'); + expect(b).to.have.property('unitId'); + expect(b).to.have.property('unitName'); + expect(b).to.have.property('cpm'); + expect(b).to.have.property('width'); + expect(b).to.have.property('height'); + expect(b).to.have.property('ad'); + expect(b).to.have.property('currency', 'USD'); + expect(b).to.have.property('creativeId'); + expect(b).to.have.property('ttl', 30); + expect(b).to.have.property('netRevenue', true); + expect(b).to.have.property('referrer'); }); - bidResponse = getDefaultBidResponse(); - bidResponse.ext = { - pixels: getPixels() - }; }); - it('should return user syncs only if userSyncOn equals to "bidResponse"', () => { - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + it('handles nobid responses', function () { + let EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {'decisions': null}}) + let bids = spec.interpretResponse(EMPTY_RESP, REQUEST); - expect($$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped).to.be.true; - expect(userSyncs).to.deep.equal([ - {type: 'image', url: 'img.org'}, - {type: 'iframe', url: 'pixels1.org'} - ]); + expect(bids).to.be.empty; }); - it('should not return user syncs if it has already been returned', () => { - $$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped = true; + it('handles no server response', function () { + let bids = spec.interpretResponse(null, REQUEST); + + expect(bids).to.be.empty; + }); + }); + describe('getUserSyncs', function () { + let syncOptions = {'iframeEnabled': true}; - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + it('handles empty sync options', function () { + let opts = spec.getUserSyncs({}); - expect($$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped).to.be.true; - expect(userSyncs).to.deep.equal([]); + expect(opts).to.be.undefined; }); - it('should not return user syncs if pixels are not present', () => { - bidResponse.ext.pixels = null; + it('should return a sync url if iframe syncs are enabled', function () { + let opts = spec.getUserSyncs(syncOptions); + + expect(opts.length).to.equal(1); + }); - let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + it('should return a sync url if pixel syncs are enabled and some are returned from the server', function () { + let syncOptions = {'pixelEnabled': true}; + let opts = spec.getUserSyncs(syncOptions, [RESPONSE]); - expect($$PREBID_GLOBAL$$.consumableGlobals.pixelsDropped).to.be.false; - expect(userSyncs).to.deep.equal([]); + expect(opts.length).to.equal(1); }); }); }); diff --git a/test/spec/modules/contentigniteBidAdapter_spec.js b/test/spec/modules/contentigniteBidAdapter_spec.js index cdf70e15615..1867791a234 100644 --- a/test/spec/modules/contentigniteBidAdapter_spec.js +++ b/test/spec/modules/contentigniteBidAdapter_spec.js @@ -1,10 +1,10 @@ import { expect } from 'chai'; import { spec } from '../../../modules/contentigniteBidAdapter'; -describe('Content Ignite adapter', () => { +describe('Content Ignite adapter', function () { let bidRequests; - beforeEach(() => { + beforeEach(function () { bidRequests = [ { bidder: 'contentignite', @@ -25,9 +25,9 @@ describe('Content Ignite adapter', () => { ]; }); - describe('implementation', () => { - describe('for requests', () => { - it('should accept valid bid', () => { + describe('implementation', function () { + describe('for requests', function () { + it('should accept valid bid', function () { const validBid = { bidder: 'contentignite', params: { @@ -40,7 +40,7 @@ describe('Content Ignite adapter', () => { expect(isValid).to.equal(true); }); - it('should reject invalid bid', () => { + it('should reject invalid bid', function () { const invalidBid = { bidder: 'contentignite', params: { @@ -52,14 +52,14 @@ describe('Content Ignite adapter', () => { expect(isValid).to.equal(false); }); - it('should set the keyword parameter', () => { + it('should set the keyword parameter', function () { const requests = spec.buildRequests(bidRequests), requestURL = requests[0].url; expect(requestURL).to.have.string(';kw=business;'); }); - it('should increment the count for the same zone', () => { + it('should increment the count for the same zone', function () { const bidRequests = [ { sizes: [[728, 90]], @@ -87,8 +87,8 @@ describe('Content Ignite adapter', () => { }); }); - describe('bid responses', () => { - it('should return complete bid response', () => { + describe('bid responses', function () { + it('should return complete bid response', function () { const serverResponse = { body: { status: 'SUCCESS', @@ -116,7 +116,7 @@ describe('Content Ignite adapter', () => { expect(bids[0].ad).to.have.length.above(1); }); - it('should return empty bid response', () => { + it('should return empty bid response', function () { const serverResponse = { status: 'NO_ELIGIBLE_ADS', zone_id: 299680, @@ -131,7 +131,7 @@ describe('Content Ignite adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response on incorrect size', () => { + it('should return empty bid response on incorrect size', function () { const serverResponse = { status: 'SUCCESS', account_id: 168237, @@ -148,7 +148,7 @@ describe('Content Ignite adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response with CPM too low', () => { + it('should return empty bid response with CPM too low', function () { const serverResponse = { status: 'SUCCESS', account_id: 168237, @@ -165,7 +165,7 @@ describe('Content Ignite adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response with CPM too high', () => { + it('should return empty bid response with CPM too high', function () { const serverResponse = { status: 'SUCCESS', account_id: 168237, diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 91b3ed6892b..aeadc3bf828 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -6,8 +6,10 @@ var Adapter = require('modules/conversantBidAdapter'); describe('Conversant adapter tests', function() { const siteId = '108060'; + const versionPattern = /^\d+\.\d+\.\d+(.)*$/; const bidRequests = [ + // banner with single size { bidder: 'conversant', params: { @@ -23,19 +25,27 @@ describe('Conversant adapter tests', function() { bidId: 'bid000', bidderRequestId: '117d765b87bed38', auctionId: 'req000' - }, { + }, + // banner with sizes in mediaTypes.banner.sizes + { bidder: 'conversant', params: { site_id: siteId, secure: false }, + mediaTypes: { + banner: { + sizes: [[728, 90], [468, 60]] + } + }, placementCode: 'pcode001', transactionId: 'tx001', - sizes: [[468, 60]], bidId: 'bid001', bidderRequestId: '117d765b87bed38', auctionId: 'req000' - }, { + }, + // banner with tag id and position + { bidder: 'conversant', params: { site_id: siteId, @@ -49,7 +59,9 @@ describe('Conversant adapter tests', function() { bidId: 'bid002', bidderRequestId: '117d765b87bed38', auctionId: 'req000' - }, { + }, + // video with single size + { bidder: 'conversant', params: { site_id: siteId, @@ -60,7 +72,8 @@ describe('Conversant adapter tests', function() { }, mediaTypes: { video: { - context: 'instream' + context: 'instream', + playerSize: [632, 499], } }, placementCode: 'pcode003', @@ -69,6 +82,47 @@ describe('Conversant adapter tests', function() { bidId: 'bid003', bidderRequestId: '117d765b87bed38', auctionId: 'req000' + }, + // video with playerSize + { + bidder: 'conversant', + params: { + site_id: siteId, + maxduration: 30, + api: [2, 3] + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [1024, 768], + api: [1, 2], + protocols: [1, 2, 3], + mimes: ['video/mp4', 'video/x-flv'] + } + }, + placementCode: 'pcode004', + transactionId: 'tx004', + bidId: 'bid004', + bidderRequestId: '117d765b87bed38', + auctionId: 'req000' + }, + // video without sizes + { + bidder: 'conversant', + params: { + site_id: siteId + }, + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mp4', 'video/x-flv'] + } + }, + placementCode: 'pcode005', + transactionId: 'tx005', + bidId: 'bid005', + bidderRequestId: '117d765b87bed38', + auctionId: 'req000' }]; const bidResponses = { @@ -128,6 +182,8 @@ describe('Conversant adapter tests', function() { expect(spec.isBidRequestValid(bidRequests[1])).to.be.true; expect(spec.isBidRequestValid(bidRequests[2])).to.be.true; expect(spec.isBidRequestValid(bidRequests[3])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[4])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[5])).to.be.true; const simpleVideo = JSON.parse(JSON.stringify(bidRequests[3])); simpleVideo.params.site_id = 123; @@ -150,13 +206,13 @@ describe('Conversant adapter tests', function() { 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).to.be.an('array').with.lengthOf(6); 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('displaymanagerver').that.matches(versionPattern); 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); @@ -168,18 +224,18 @@ describe('Conversant adapter tests', function() { 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.have.property('displaymanagerver').that.matches(versionPattern); 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[1].banner.format).to.deep.equal([{w: 728, h: 90}, {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('displaymanagerver').that.matches(versionPattern); 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'); @@ -189,12 +245,12 @@ describe('Conversant adapter tests', function() { 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.have.property('displaymanagerver').that.matches(versionPattern); 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('w', 640); - expect(payload.imp[3].video).to.have.property('h', 480); + expect(payload.imp[3].video).to.have.property('w', 632); + expect(payload.imp[3].video).to.have.property('h', 499); 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'); @@ -204,6 +260,42 @@ describe('Conversant adapter tests', function() { expect(payload.imp[3].video).to.have.property('maxduration', 30); expect(payload.imp[3]).to.not.have.property('banner'); + expect(payload.imp[4]).to.have.property('id', 'bid004'); + expect(payload.imp[4]).to.have.property('secure', 0); + expect(payload.imp[4]).to.have.property('bidfloor', 0); + expect(payload.imp[4]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[4]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[4]).to.not.have.property('tagid'); + expect(payload.imp[4]).to.have.property('video'); + expect(payload.imp[4].video).to.not.have.property('pos'); + expect(payload.imp[4].video).to.have.property('w', 1024); + expect(payload.imp[4].video).to.have.property('h', 768); + expect(payload.imp[4].video).to.have.property('mimes'); + expect(payload.imp[4].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[4].video).to.have.property('protocols'); + expect(payload.imp[4].video.protocols).to.deep.equal([1, 2, 3]); + expect(payload.imp[4].video).to.have.property('api'); + expect(payload.imp[4].video.api).to.deep.equal([2, 3]); + expect(payload.imp[4].video).to.have.property('maxduration', 30); + expect(payload.imp[4]).to.not.have.property('banner'); + + expect(payload.imp[5]).to.have.property('id', 'bid005'); + expect(payload.imp[5]).to.have.property('secure', 0); + expect(payload.imp[5]).to.have.property('bidfloor', 0); + expect(payload.imp[5]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[5]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[5]).to.not.have.property('tagid'); + expect(payload.imp[5]).to.have.property('video'); + expect(payload.imp[5].video).to.not.have.property('pos'); + expect(payload.imp[5].video).to.not.have.property('w'); + expect(payload.imp[5].video).to.not.have.property('h'); + expect(payload.imp[5].video).to.have.property('mimes'); + expect(payload.imp[5].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[5].video).to.not.have.property('protocols'); + expect(payload.imp[5].video).to.not.have.property('api'); + expect(payload.imp[5].video).to.not.have.property('maxduration'); + expect(payload.imp[5]).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]); @@ -254,8 +346,8 @@ describe('Conversant adapter tests', function() { 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('width', 632); + expect(bid).to.have.property('height', 499); expect(bid).to.have.property('vastUrl', 'markup003'); expect(bid).to.have.property('mediaType', 'video'); expect(bid).to.have.property('ttl', 300); @@ -281,7 +373,20 @@ describe('Conversant adapter tests', function() { }); // construct http post payload const payload = spec.buildRequests(requests).data; - expect(payload).to.have.deep.property('user.ext.fpc', 12345); + expect(payload).to.have.deep.nested.property('user.ext.fpc', 12345); + }); + + it('Verify User ID publisher commond id support', function() { + // clone bidRequests + let requests = utils.deepClone(bidRequests) + + // add pubcid to every entry + requests.forEach((unit) => { + Object.assign(unit, {userId: {pubcid: 67890}}); + }); + // construct http post payload + const payload = spec.buildRequests(requests).data; + expect(payload).to.have.deep.nested.property('user.ext.fpc', 67890); }); it('Verify GDPR bid request', function() { @@ -294,8 +399,8 @@ describe('Conversant adapter tests', function() { }; const payload = spec.buildRequests(bidRequests, bidRequest).data; - expect(payload).to.have.deep.property('user.ext.consent', 'BOJObISOJObISAABAAENAA4AAAAAoAAA'); - expect(payload).to.have.deep.property('regs.ext.gdpr', 1); + expect(payload).to.have.deep.nested.property('user.ext.consent', 'BOJObISOJObISAABAAENAA4AAAAAoAAA'); + expect(payload).to.have.deep.nested.property('regs.ext.gdpr', 1); }); it('Verify GDPR bid request without gdprApplies', function() { @@ -307,7 +412,7 @@ describe('Conversant adapter tests', function() { }; const payload = spec.buildRequests(bidRequests, bidRequest).data; - expect(payload).to.have.deep.property('user.ext.consent', ''); - expect(payload).to.not.have.deep.property('regs.ext.gdpr'); + expect(payload).to.have.deep.nested.property('user.ext.consent', ''); + expect(payload).to.not.have.deep.nested.property('regs.ext.gdpr'); }); }) diff --git a/test/spec/modules/cosmosBidAdapter_spec.js b/test/spec/modules/cosmosBidAdapter_spec.js new file mode 100644 index 00000000000..348f5ae3ddf --- /dev/null +++ b/test/spec/modules/cosmosBidAdapter_spec.js @@ -0,0 +1,355 @@ +import { expect } from 'chai'; +import { spec } from 'modules/cosmosBidAdapter'; +import * as utils from 'src/utils'; +const constants = require('src/constants.json'); + +describe('Cosmos adapter', function () { + let bannerBidRequests; + let bannerBidResponse; + let videoBidRequests; + let videoBidResponse; + + beforeEach(function () { + bannerBidRequests = [ + { + bidder: 'cosmos', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + params: { + publisherId: '1001', + currency: 'USD', + geo: { + lat: '09.5', + lon: '21.2', + } + }, + bidId: '29f8bd96defe76' + } + ]; + + videoBidRequests = + [ + { + mediaTypes: { + video: { + mimes: ['video/mp4', 'video/x-flv'], + context: 'instream' + } + }, + bidder: 'cosmos', + params: { + publisherId: 1001, + video: { + skippable: true, + minduration: 5, + maxduration: 30 + } + }, + bidId: '39f5cc6eff9b37' + } + ]; + + bannerBidResponse = { + 'body': { + 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', + 'seatbid': [{ + 'bid': [{ + 'id': '82DAAE22-FF66-4FAB-84AB-347B0C5CD02C', + 'impid': '29f8bd96defe76', + 'price': 1.858309, + 'adm': '

    COSMOS\"Connecting Advertisers and Publishers directly\"

    ', + 'adid': 'v55jutrh', + 'adomain': ['febreze.com'], + 'iurl': 'https://thetradedesk-t-general.s3.amazonaws.com/AdvertiserLogos/vgl908z.png', + 'cid': '1234', + 'crid': 'v55jutrh', + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + } + } + }], + 'seat': 'zeta' + }] + } + }; + + videoBidResponse = { + 'body': { + 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', + 'seatbid': [{ + 'bid': [{ + 'id': '82DAAE22-FF66-4FAB-84AB-347B0C5CD02C', + 'impid': '39f5cc6eff9b37', + 'price': 0.858309, + 'adm': 'CosmosHQVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://track.cosmoshq.com/event?data=%7B%22id%22%3A%221566011421045%22%2C%22bid%22%3A%2282DAAE22-FF66-4FAB-84AB-347B0C5CD02C%22%2C%22ts%22%3A%2220190817031021%22%2C%22pid%22%3A1001%2C%22plcid%22%3A1%2C%22aid%22%3A1%2C%22did%22%3A1%2C%22cid%22%3A%2222918%22%2C%22af%22%3A3%2C%22at%22%3A1%2C%22w%22%3A300%2C%22h%22%3A250%2C%22crid%22%3A%22v55jutrh%22%2C%22pp%22%3A0.858309%2C%22cp%22%3A0.858309%2C%22mg%22%3A0%7D&type=1http://track.dsp.impression.com/impression00:00:60http://sync.cosmoshq.com/static/video/SampleVideo_1280x720_10mb.mp4', + 'adid': 'v55jutrh', + 'adomain': ['febreze.com'], + 'iurl': 'https://thetradedesk-t-general.s3.amazonaws.com/AdvertiserLogos/vgl908z.png', + 'cid': '1234', + 'crid': 'v55jutrh', + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'video' + } + } + }], + 'seat': 'zeta' + }] + } + }; + }); + + describe('isBidRequestValid', function () { + describe('validate the bid object: valid bid', function () { + it('valid bid case', function () { + let validBid = { + bidder: 'cosmos', + params: { + publisherId: 1001, + tagId: 1 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('validate the bid object: nil/empty bid object', function () { + let validBid = { + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + + it('validate the bid object: publisherId not passed', function () { + let validBid = { + bidder: 'cosmos', + params: { + tagId: 1 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + + it('validate the bid object: publisherId is not number', function () { + let validBid = { + bidder: 'cosmos', + params: { + publisherId: '301', + tagId: 1 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + + it('validate the bid object: mimes absent', function () { + let validBid = { + bidder: 'cosmos', + mediaTypes: { + video: {} + }, + params: { + publisherId: 1001 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + + it('validate the bid object: mimes present', function () { + let validBid = { + bidder: 'cosmos', + mediaTypes: { + video: { + mimes: ['video/mp4', 'application/javascript'] + } + }, + params: { + publisherId: 1001 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('validate the bid object: tagId is not passed', function () { + let validBid = { + bidder: 'cosmos', + params: { + publisherId: 1001 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + }); + + describe('buildRequests', function () { + it('build request object: buildRequests function should not modify original bannerBidRequests object', function () { + let originalBidRequests = utils.deepClone(bannerBidRequests); + let request = spec.buildRequests(bannerBidRequests); + expect(bannerBidRequests).to.deep.equal(originalBidRequests); + }); + + it('build request object: endpoint check', function () { + let request = spec.buildRequests(bannerBidRequests); + expect(request[0].url).to.equal('//bid.cosmoshq.com/openrtb2/bids'); + expect(request[0].method).to.equal('POST'); + }); + + it('build request object: request params check', function () { + let request = spec.buildRequests(bannerBidRequests); + let data = JSON.parse(request[0].data); + expect(data.site.publisher.id).to.equal(bannerBidRequests[0].params.publisherId); // publisher Id + expect(data.imp[0].bidfloorcur).to.equal(bannerBidRequests[0].params.currency); + }); + + it('build request object: request params check without tagId', function () { + delete bannerBidRequests[0].params.tagId; + let request = spec.buildRequests(bannerBidRequests); + let data = JSON.parse(request[0].data); + expect(data.site.publisher.id).to.equal(bannerBidRequests[0].params.publisherId); // publisher Id + expect(data.imp[0].tagid).to.equal(undefined); // tagid + expect(data.imp[0].bidfloorcur).to.equal(bannerBidRequests[0].params.currency); + }); + + it('build request object: request params multi size format object check', function () { + let bidRequest = [ + { + bidder: 'cosmos', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + params: { + publisherId: 1001, + currency: 'USD' + } + } + ]; + /* case 1 - size passed in adslot */ + let request = spec.buildRequests(bidRequest); + let data = JSON.parse(request[0].data); + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + + /* case 2 - size passed in adslot as well as in sizes array */ + bidRequest[0].sizes = [[300, 600], [300, 250]]; + bidRequest[0].mediaTypes = { + banner: { + sizes: [[300, 600], [300, 250]] + } + }; + request = spec.buildRequests(bidRequest); + data = JSON.parse(request[0].data); + + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(600); // height + + /* case 3 - size passed in sizes but not in adslot */ + bidRequest[0].params.tagId = 1; + bidRequest[0].sizes = [[300, 250], [300, 600]]; + bidRequest[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + } + }; + request = spec.buildRequests(bidRequest); + data = JSON.parse(request[0].data); + + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + expect(data.imp[0].banner.format).exist.and.to.be.an('array'); + expect(data.imp[0].banner.format[0]).exist.and.to.be.an('object'); + expect(data.imp[0].banner.format[0].w).to.equal(300); // width + expect(data.imp[0].banner.format[0].h).to.equal(250); // height + }); + + it('build request object: request params currency check', function () { + let bidRequest = [ + { + bidder: 'cosmos', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + params: { + publisherId: 1001, + tagId: 1, + currency: 'USD' + }, + sizes: [[300, 250], [300, 600]] + } + ]; + + /* case 1 - + currency specified in adunits + output: imp[0] use currency specified in bannerBidRequests[0].params.currency + + */ + let request = spec.buildRequests(bidRequest); + let data = JSON.parse(request[0].data); + expect(data.imp[0].bidfloorcur).to.equal(bidRequest[0].params.currency); + + /* case 2 - + currency specified in adunit + output: imp[0] use default currency - USD + + */ + delete bidRequest[0].params.currency; + request = spec.buildRequests(bidRequest); + data = JSON.parse(request[0].data); + expect(data.imp[0].bidfloorcur).to.equal('USD'); + }); + + it('build request object: request params check for video ad', function () { + let request = spec.buildRequests(videoBidRequests); + let data = JSON.parse(request[0].data); + expect(data.imp[0].video).to.exist; + expect(data.imp[0]['video']['mimes']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['mimes'][0]).to.equal(videoBidRequests[0].mediaTypes.video['mimes'][0]); + expect(data.imp[0]['video']['mimes'][1]).to.equal(videoBidRequests[0].mediaTypes.video['mimes'][1]); + expect(data.imp[0]['video']['minduration']).to.equal(videoBidRequests[0].params.video['minduration']); + expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); + }); + + describe('interpretResponse', function () { + it('check for banner response', function () { + let request = spec.buildRequests(bannerBidRequests); + let data = JSON.parse(request[0].data); + let response = spec.interpretResponse(bannerBidResponse, request[0]); + expect(response).to.be.an('array').with.length.above(0); + expect(response[0].requestId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].impid); + expect(response[0].cpm).to.equal((bannerBidResponse.body.seatbid[0].bid[0].price).toFixed(2)); + expect(response[0].width).to.equal(bannerBidResponse.body.seatbid[0].bid[0].w); + expect(response[0].height).to.equal(bannerBidResponse.body.seatbid[0].bid[0].h); + if (bannerBidResponse.body.seatbid[0].bid[0].crid) { + expect(response[0].creativeId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].crid); + } else { + expect(response[0].creativeId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].id); + } + expect(response[0].dealId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].dealid); + expect(response[0].currency).to.equal('USD'); + expect(response[0].netRevenue).to.equal(false); + expect(response[0].ttl).to.equal(300); + }); + it('check for video response', function () { + let request = spec.buildRequests(videoBidRequests); + let data = JSON.parse(request[0].data); + let response = spec.interpretResponse(videoBidResponse, request[0]); + }); + }); + }); + }); +}); diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js new file mode 100755 index 00000000000..3dd06a484d9 --- /dev/null +++ b/test/spec/modules/cpmstarBidAdapter_spec.js @@ -0,0 +1,158 @@ +import { expect } from 'chai'; +import { spec } from 'modules/cpmstarBidAdapter'; +import { deepClone } from 'src/utils'; + +describe('Cpmstar Bid Adapter', function () { + describe('isBidRequestValid', function () { + it('should return true since the bid is valid', + function () { + var bid = { params: { placementId: 123456 } }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }) + + it('should return false since the bid is invalid', function () { + var bid = { params: { placementId: '' } }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }) + + it('should return a valid player size', function() { + var bid = { mediaTypes: { + video: { + playerSize: [[960, 540]] + } + }} + expect(spec.getPlayerSize(bid)[0]).to.equal(960); + expect(spec.getPlayerSize(bid)[1]).to.equal(540); + }) + + it('should return a default player size', function() { + var bid = { mediaTypes: { + video: { + playerSize: null + } + }} + expect(spec.getPlayerSize(bid)[0]).to.equal(640); + expect(spec.getPlayerSize(bid)[1]).to.equal(440); + }) + }); + + describe('buildRequests', function () { + const valid_bid_requests = [{ + 'bidder': 'cpmstar', + 'params': { + 'placementId': '57' + }, + 'sizes': [[300, 250]], + 'bidId': 'bidId' + }]; + + const bidderRequest = { + refererInfo: { + referer: 'referer', + reachedTop: false, + } + + }; + + it('should produce a valid production request', function () { + var requests = spec.buildRequests(valid_bid_requests, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('//server.cpmstar.com/view.aspx'); + }); + it('should produce a valid staging request', function () { + var stgReq = deepClone(valid_bid_requests); + stgReq[0].params.endpoint = 'staging'; + var requests = spec.buildRequests(stgReq, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('//staging.server.cpmstar.com/view.aspx'); + }); + it('should produce a valid dev request', function () { + var devReq = deepClone(valid_bid_requests); + devReq[0].params.endpoint = 'dev'; + var requests = spec.buildRequests(devReq, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('//dev.server.cpmstar.com/view.aspx'); + }); + }) + + describe('interpretResponse', function () { + const request = { + bidRequest: { + mediaType: 'BANNER' + } + }; + const serverResponse = { + body: [{ + creatives: [{ + cpm: 1, + width: 0, + height: 0, + currency: 'USD', + netRevenue: true, + ttl: 1, + creativeid: '1234', + requestid: '11123', + code: 'no idea', + media: 'banner', + } + ], + }] + }; + + it('should return a valid bidresponse array', function () { + var r = spec.interpretResponse(serverResponse, request) + var c = serverResponse.body[0].creatives[0]; + expect(r[0].length).to.not.equal(0); + expect(r[0].requestId).equal(c.requestid); + expect(r[0].creativeId).equal(c.creativeid); + expect(r[0].cpm).equal(c.cpm); + expect(r[0].width).equal(c.width); + expect(r[0].height).equal(c.height); + expect(r[0].currency).equal(c.currency); + expect(r[0].netRevenue).equal(c.netRevenue); + expect(r[0].ttl).equal(c.ttl); + expect(r[0].ad).equal(c.code); + }); + + it('should return a valid bidresponse array from a non-array-body', function () { + var r = spec.interpretResponse({ body: serverResponse.body[0] }, request) + var c = serverResponse.body[0].creatives[0]; + expect(r[0].length).to.not.equal(0); + expect(r[0].requestId).equal(c.requestid); + expect(r[0].creativeId).equal(c.creativeid); + expect(r[0].cpm).equal(c.cpm); + expect(r[0].width).equal(c.width); + expect(r[0].height).equal(c.height); + expect(r[0].currency).equal(c.currency); + expect(r[0].netRevenue).equal(c.netRevenue); + expect(r[0].ttl).equal(c.ttl); + expect(r[0].ad).equal(c.code); + }); + + it('should return undefined due to an invalid cpm value', function () { + var badServer = deepClone(serverResponse); + badServer.body[0].creatives[0].cpm = 0; + var c = spec.interpretResponse(badServer, request); + expect(c).to.be.undefined; + }); + + it('should return undefined due to a bad response', function () { + var badServer = deepClone(serverResponse); + badServer.body[0].creatives[0].code = null; + var c = spec.interpretResponse(badServer, request); + expect(c).to.be.undefined; + }); + + it('should return a valid response with a dealId', function () { + var dealServer = deepClone(serverResponse); + dealServer.body[0].creatives[0].dealId = 'deal'; + expect(spec.interpretResponse(dealServer, request)[0].dealId).to.equal('deal'); + }); + }); +}); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js new file mode 100755 index 00000000000..03500d4add6 --- /dev/null +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -0,0 +1,997 @@ +import { expect } from 'chai'; +import { tryGetCriteoFastBid, spec, PROFILE_ID_PUBLISHERTAG, ADAPTER_VERSION, PUBLISHER_TAG_URL } from 'modules/criteoBidAdapter'; +import { createBid } from 'src/bidfactory'; +import CONSTANTS from 'src/constants.json'; +import * as utils from 'src/utils'; +import { config } from '../../../src/config'; +import { VIDEO } from '../../../src/mediaTypes'; + +describe('The Criteo bidding adapter', function () { + let utilsMock; + + beforeEach(function () { + // Remove FastBid to avoid side effects + localStorage.removeItem('criteo_fast_bid'); + utilsMock = sinon.mock(utils); + }); + + afterEach(function() { + global.Criteo = undefined; + utilsMock.restore(); + }); + + describe('isBidRequestValid', function () { + it('should return false when given an invalid bid', function () { + const bid = { + bidder: 'criteo', + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }); + + it('should return true when given a zoneId bid', function () { + const bid = { + bidder: 'criteo', + params: { + zoneId: 123, + }, + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); + + it('should return true when given a networkId bid', function () { + const bid = { + bidder: 'criteo', + params: { + networkId: 456, + }, + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); + + it('should return true when given a mixed bid with both a zoneId and a networkId', function () { + const bid = { + bidder: 'criteo', + params: { + zoneId: 123, + networkId: 456, + }, + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); + + it('should return true when given a valid video bid request', function () { + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(true); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 2, + playbackmethod: 1 + } + }, + })).to.equal(true); + }); + + it('should return false when given an invalid video bid request', function () { + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 2, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'adpod', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30 + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1 + } + }, + })).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidderRequest = { + timeout: 3000, + gdprConsent: { + gdprApplies: 1, + consentString: 'concentDataString', + vendorData: { + vendorConsents: { + '91': 1 + }, + }, + }, + }; + + afterEach(function () { + config.resetConfig(); + }); + + it('should properly build a zoneId request', function () { + const publisherUrl = 'https://criteo.com?pbt_debug=1&pbt_nolog=1'; + utilsMock.expects('getTopWindowUrl').withExactArgs().once().returns(publisherUrl); + + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + params: { + zoneId: 123, + publisherSubId: '123', + nativeCallback: function() {}, + integrationMode: 'amp' + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d+&im=1&debug=1&nolog=1/); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest.publisher.url).to.equal(publisherUrl); + expect(ortbRequest.slots).to.have.lengthOf(1); + expect(ortbRequest.slots[0].impid).to.equal('bid-123'); + expect(ortbRequest.slots[0].transactionid).to.equal('transaction-123'); + expect(ortbRequest.slots[0].sizes).to.have.lengthOf(1); + expect(ortbRequest.slots[0].sizes[0]).to.equal('728x90'); + expect(ortbRequest.slots[0].zoneid).to.equal(123); + expect(ortbRequest.gdprConsent.consentData).to.equal('concentDataString'); + expect(ortbRequest.gdprConsent.gdprApplies).to.equal(true); + expect(ortbRequest.gdprConsent.consentGiven).to.equal(true); + }); + + it('should properly build a networkId request', function () { + const bidderRequest = { + timeout: 3000, + gdprConsent: { + gdprApplies: 0, + consentString: undefined, + vendorData: { + vendorConsents: { + '1': 0 + }, + }, + }, + }; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + } + }, + params: { + networkId: 456, + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d/); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest.publisher.url).to.equal(utils.getTopWindowUrl()); + expect(ortbRequest.publisher.networkid).to.equal(456); + expect(ortbRequest.slots).to.have.lengthOf(1); + expect(ortbRequest.slots[0].impid).to.equal('bid-123'); + expect(ortbRequest.slots[0].transactionid).to.equal('transaction-123'); + expect(ortbRequest.slots[0].sizes).to.have.lengthOf(2); + expect(ortbRequest.slots[0].sizes[0]).to.equal('300x250'); + expect(ortbRequest.slots[0].sizes[1]).to.equal('728x90'); + expect(ortbRequest.gdprConsent.consentData).to.equal(undefined); + expect(ortbRequest.gdprConsent.gdprApplies).to.equal(false); + expect(ortbRequest.gdprConsent.consentGiven).to.equal(undefined); + }); + + it('should properly build a mixed request', function () { + const bidderRequest = { timeout: 3000 }; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + params: { + zoneId: 123, + }, + }, + { + bidder: 'criteo', + adUnitCode: 'bid-234', + transactionId: 'transaction-234', + sizes: [[300, 250], [728, 90]], + params: { + networkId: 456, + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d/); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest.publisher.url).to.equal(utils.getTopWindowUrl()); + expect(ortbRequest.publisher.networkid).to.equal(456); + expect(ortbRequest.slots).to.have.lengthOf(2); + expect(ortbRequest.slots[0].impid).to.equal('bid-123'); + expect(ortbRequest.slots[0].transactionid).to.equal('transaction-123'); + expect(ortbRequest.slots[0].sizes).to.have.lengthOf(1); + expect(ortbRequest.slots[0].sizes[0]).to.equal('728x90'); + expect(ortbRequest.slots[1].impid).to.equal('bid-234'); + expect(ortbRequest.slots[1].transactionid).to.equal('transaction-234'); + expect(ortbRequest.slots[1].sizes).to.have.lengthOf(2); + expect(ortbRequest.slots[1].sizes[0]).to.equal('300x250'); + expect(ortbRequest.slots[1].sizes[1]).to.equal('728x90'); + expect(ortbRequest.gdprConsent).to.equal(undefined); + }); + + it('should properly build request with undefined gdpr consent fields when they are not provided', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + params: { + zoneId: 123, + }, + }, + ]; + const bidderRequest = { timeout: 3000, + gdprConsent: { + }, + }; + + const ortbRequest = spec.buildRequests(bidRequests, bidderRequest).data; + expect(ortbRequest.gdprConsent.consentData).to.equal(undefined); + expect(ortbRequest.gdprConsent.gdprApplies).to.equal(undefined); + expect(ortbRequest.gdprConsent.consentGiven).to.equal(undefined); + }); + + it('should properly build a video request', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + mediaTypes: { + video: { + playerSize: [640, 480], + mimes: ['video/mp4', 'video/x-flv'], + maxduration: 30, + api: [1, 2], + protocols: [2, 3] + } + }, + params: { + zoneId: 123, + video: { + skip: 1, + minduration: 5, + startdelay: 5, + playbackmethod: [1, 3], + placement: 2 + } + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d/); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest.slots[0].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(ortbRequest.slots[0].video.playersizes).to.deep.equal(['640x480']); + expect(ortbRequest.slots[0].video.maxduration).to.equal(30); + expect(ortbRequest.slots[0].video.api).to.deep.equal([1, 2]); + expect(ortbRequest.slots[0].video.protocols).to.deep.equal([2, 3]); + expect(ortbRequest.slots[0].video.skip).to.equal(1); + expect(ortbRequest.slots[0].video.minduration).to.equal(5); + expect(ortbRequest.slots[0].video.startdelay).to.equal(5); + expect(ortbRequest.slots[0].video.playbackmethod).to.deep.equal([1, 3]); + expect(ortbRequest.slots[0].video.placement).to.equal(2); + }); + + it('should properly build a video request with more than one player size', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + mediaTypes: { + video: { + playerSize: [[640, 480], [800, 600]], + mimes: ['video/mp4', 'video/x-flv'], + maxduration: 30, + api: [1, 2], + protocols: [2, 3] + } + }, + params: { + zoneId: 123, + video: { + skip: 1, + minduration: 5, + startdelay: 5, + playbackmethod: [1, 3], + placement: 2 + } + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d/); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest.slots[0].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(ortbRequest.slots[0].video.playersizes).to.deep.equal(['640x480', '800x600']); + expect(ortbRequest.slots[0].video.maxduration).to.equal(30); + expect(ortbRequest.slots[0].video.api).to.deep.equal([1, 2]); + expect(ortbRequest.slots[0].video.protocols).to.deep.equal([2, 3]); + expect(ortbRequest.slots[0].video.skip).to.equal(1); + expect(ortbRequest.slots[0].video.minduration).to.equal(5); + expect(ortbRequest.slots[0].video.startdelay).to.equal(5); + expect(ortbRequest.slots[0].video.playbackmethod).to.deep.equal([1, 3]); + expect(ortbRequest.slots[0].video.placement).to.equal(2); + }); + + it('should properly build a request with ceh', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + params: { + zoneId: 123, + }, + }, + ]; + config.setConfig({ + criteo: { + ceh: 'hashedemail' + } + }); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.user).to.not.be.null; + expect(request.data.user.ceh).to.equal('hashedemail'); + }); + }); + + describe('interpretResponse', function () { + it('should return an empty array when parsing a no bid response', function () { + const response = {}; + const request = { bidRequests: [] }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(0); + }); + + it('should properly parse a bid response with a networkId', function () { + const response = { + body: { + slots: [{ + impid: 'test-requestId', + cpm: 1.23, + creative: 'test-ad', + width: 728, + height: 90, + dealCode: 'myDealCode', + }], + }, + }; + const request = { + bidRequests: [{ + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + params: { + networkId: 456, + } + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('test-bidId'); + expect(bids[0].cpm).to.equal(1.23); + expect(bids[0].ad).to.equal('test-ad'); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + expect(bids[0].dealId).to.equal('myDealCode'); + }); + + it('should properly parse a bid responsewith with a zoneId', function () { + const response = { + body: { + slots: [{ + impid: 'test-requestId', + bidId: 'abc123', + cpm: 1.23, + creative: 'test-ad', + width: 728, + height: 90, + zoneid: 123, + }], + }, + }; + const request = { + bidRequests: [{ + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + params: { + zoneId: 123, + }, + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('test-bidId'); + expect(bids[0].adId).to.equal('abc123'); + expect(bids[0].cpm).to.equal(1.23); + expect(bids[0].ad).to.equal('test-ad'); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + }); + + it('should properly parse a bid responsewith with a video', function () { + const response = { + body: { + slots: [{ + impid: 'test-requestId', + bidId: 'abc123', + cpm: 1.23, + displayurl: 'http://test-ad', + width: 728, + height: 90, + zoneid: 123, + video: true + }], + }, + }; + const request = { + bidRequests: [{ + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + params: { + zoneId: 123, + }, + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('test-bidId'); + expect(bids[0].adId).to.equal('abc123'); + expect(bids[0].cpm).to.equal(1.23); + expect(bids[0].vastUrl).to.equal('http://test-ad'); + expect(bids[0].mediaType).to.equal(VIDEO); + }); + + it('should properly parse a bid responsewith with a zoneId passed as a string', function () { + const response = { + body: { + slots: [{ + impid: 'test-requestId', + cpm: 1.23, + creative: 'test-ad', + width: 728, + height: 90, + zoneid: 123, + }], + }, + }; + const request = { + bidRequests: [{ + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + params: { + zoneId: '123', + }, + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('test-bidId'); + expect(bids[0].cpm).to.equal(1.23); + expect(bids[0].ad).to.equal('test-ad'); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + }); + + it('should generate unique adIds if none are returned by the endpoint', function () { + const response = { + body: { + slots: [{ + impid: 'test-requestId', + cpm: 1.23, + creative: 'test-ad', + width: 300, + height: 250, + }, { + impid: 'test-requestId', + cpm: 4.56, + creative: 'test-ad', + width: 728, + height: 90, + }], + }, + }; + const request = { + bidRequests: [{ + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + sizes: [[300, 250], [728, 90]], + params: { + networkId: 456, + } + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(2); + const prebidBids = bids.map(bid => Object.assign(createBid(CONSTANTS.STATUS.GOOD, request.bidRequests[0]), bid)); + expect(prebidBids[0].adId).to.not.equal(prebidBids[1].adId); + }); + }); + + describe('tryGetCriteoFastBid', function () { + const VALID_HASH = 'vBeD8Q7GU6lypFbzB07W8hLGj7NL+p7dI9ro2tCxkrmyv0F6stNuoNd75Us33iNKfEoW+cFWypelr6OJPXxki2MXWatRhJuUJZMcK4VBFnxi3Ro+3a0xEfxE4jJm4eGe98iC898M+/YFHfp+fEPEnS6pEyw124ONIFZFrcejpHU='; + const INVALID_HASH = 'invalid'; + const VALID_PUBLISHER_TAG = 'test'; + const INVALID_PUBLISHER_TAG = 'test invalid'; + + const FASTBID_LOCAL_STORAGE_KEY = 'criteo_fast_bid'; + + it('should verify valid hash with valid publisher tag', function () { + localStorage.setItem(FASTBID_LOCAL_STORAGE_KEY, '// Hash: ' + VALID_HASH + '\n' + VALID_PUBLISHER_TAG); + + utilsMock.expects('logInfo').withExactArgs('Using Criteo FastBid').once(); + utilsMock.expects('logWarn').withExactArgs('No hash found in FastBid').never(); + utilsMock.expects('logWarn').withExactArgs('Invalid Criteo FastBid found').never(); + + tryGetCriteoFastBid(); + + expect(localStorage.getItem(FASTBID_LOCAL_STORAGE_KEY)).to.equals('// Hash: ' + VALID_HASH + '\n' + VALID_PUBLISHER_TAG); + utilsMock.verify(); + }); + + it('should verify valid hash with invalid publisher tag', function () { + localStorage.setItem(FASTBID_LOCAL_STORAGE_KEY, '// Hash: ' + VALID_HASH + '\n' + INVALID_PUBLISHER_TAG); + + utilsMock.expects('logInfo').withExactArgs('Using Criteo FastBid').never(); + utilsMock.expects('logWarn').withExactArgs('No hash found in FastBid').never(); + utilsMock.expects('logWarn').withExactArgs('Invalid Criteo FastBid found').once(); + + tryGetCriteoFastBid(); + + expect(localStorage.getItem(FASTBID_LOCAL_STORAGE_KEY)).to.be.null; + utilsMock.verify(); + }); + + it('should verify invalid hash with valid publisher tag', function () { + localStorage.setItem(FASTBID_LOCAL_STORAGE_KEY, '// Hash: ' + INVALID_HASH + '\n' + VALID_PUBLISHER_TAG); + + utilsMock.expects('logInfo').withExactArgs('Using Criteo FastBid').never(); + utilsMock.expects('logWarn').withExactArgs('No hash found in FastBid').never(); + utilsMock.expects('logWarn').withExactArgs('Invalid Criteo FastBid found').once(); + + tryGetCriteoFastBid(); + + expect(localStorage.getItem(FASTBID_LOCAL_STORAGE_KEY)).to.be.null; + utilsMock.verify(); + }); + + it('should verify missing hash', function () { + localStorage.setItem(FASTBID_LOCAL_STORAGE_KEY, VALID_PUBLISHER_TAG); + + utilsMock.expects('logInfo').withExactArgs('Using Criteo FastBid').never(); + utilsMock.expects('logWarn').withExactArgs('No hash found in FastBid').once(); + utilsMock.expects('logWarn').withExactArgs('Invalid Criteo FastBid found').never(); + + tryGetCriteoFastBid(); + + expect(localStorage.getItem(FASTBID_LOCAL_STORAGE_KEY)).to.be.null; + utilsMock.verify(); + }); + }); + + describe('when pubtag prebid adapter is available', function () { + it('should forward response to pubtag when calling interpretResponse', () => { + const response = {}; + const request = {}; + + const adapter = { interpretResponse: function() {} }; + const adapterMock = sinon.mock(adapter); + adapterMock.expects('interpretResponse').withExactArgs(response, request).once().returns('ok'); + const prebidAdapter = { GetAdapter: function() {} }; + const prebidAdapterMock = sinon.mock(prebidAdapter); + prebidAdapterMock.expects('GetAdapter').withExactArgs(request).once().returns(adapter); + + global.Criteo = { + PubTag: { + Adapters: { + Prebid: prebidAdapter + } + } + }; + + expect(spec.interpretResponse(response, request)).equal('ok'); + adapterMock.verify(); + prebidAdapterMock.verify(); + }); + + it('should forward bid to pubtag when calling onBidWon', () => { + const bid = { auctionId: 123 }; + + const adapter = { handleBidWon: function() {} }; + const adapterMock = sinon.mock(adapter); + adapterMock.expects('handleBidWon').withExactArgs(bid).once(); + const prebidAdapter = { GetAdapter: function() {} }; + const prebidAdapterMock = sinon.mock(prebidAdapter); + prebidAdapterMock.expects('GetAdapter').withExactArgs(bid.auctionId).once().returns(adapter); + + global.Criteo = { + PubTag: { + Adapters: { + Prebid: prebidAdapter + } + } + }; + + spec.onBidWon(bid); + adapterMock.verify(); + prebidAdapterMock.verify(); + }); + + it('should forward bid to pubtag when calling onSetTargeting', () => { + const bid = { auctionId: 123 }; + + const adapter = { handleSetTargeting: function() {} }; + const adapterMock = sinon.mock(adapter); + adapterMock.expects('handleSetTargeting').withExactArgs(bid).once(); + const prebidAdapter = { GetAdapter: function() {} }; + const prebidAdapterMock = sinon.mock(prebidAdapter); + prebidAdapterMock.expects('GetAdapter').withExactArgs(bid.auctionId).once().returns(adapter); + + global.Criteo = { + PubTag: { + Adapters: { + Prebid: prebidAdapter + } + } + }; + + spec.onSetTargeting(bid); + adapterMock.verify(); + prebidAdapterMock.verify(); + }); + + it('should forward bid to pubtag when calling onTimeout', () => { + const timeoutData = { auctionId: 123 }; + + const adapter = { handleBidTimeout: function() {} }; + const adapterMock = sinon.mock(adapter); + adapterMock.expects('handleBidTimeout').once(); + const prebidAdapter = { GetAdapter: function() {} }; + const prebidAdapterMock = sinon.mock(prebidAdapter); + prebidAdapterMock.expects('GetAdapter').withExactArgs(timeoutData.auctionId).once().returns(adapter); + + global.Criteo = { + PubTag: { + Adapters: { + Prebid: prebidAdapter + } + } + }; + + spec.onTimeout(timeoutData); + adapterMock.verify(); + prebidAdapterMock.verify(); + }); + + it('should return a POST method with url & data from pubtag', () => { + const bidRequests = { }; + const bidderRequest = { }; + + const prebidAdapter = { buildCdbUrl: function() {}, buildCdbRequest: function() {} }; + const prebidAdapterMock = sinon.mock(prebidAdapter); + prebidAdapterMock.expects('buildCdbUrl').once().returns('cdbUrl'); + prebidAdapterMock.expects('buildCdbRequest').once().returns('cdbRequest'); + + const adapters = { Prebid: function() {} }; + const adaptersMock = sinon.mock(adapters); + adaptersMock.expects('Prebid').withExactArgs(PROFILE_ID_PUBLISHERTAG, ADAPTER_VERSION, bidRequests, bidderRequest, '$prebid.version$').once().returns(prebidAdapter); + + global.Criteo = { + PubTag: { + Adapters: adapters + } + }; + + const buildRequestsResult = spec.buildRequests(bidRequests, bidderRequest); + expect(buildRequestsResult.method).equal('POST'); + expect(buildRequestsResult.url).equal('cdbUrl'); + expect(buildRequestsResult.data).equal('cdbRequest'); + + adaptersMock.verify(); + prebidAdapterMock.verify(); + }); + }); +}); diff --git a/test/spec/modules/criteortusIdSystem_spec.js b/test/spec/modules/criteortusIdSystem_spec.js new file mode 100644 index 00000000000..217a2f86ba7 --- /dev/null +++ b/test/spec/modules/criteortusIdSystem_spec.js @@ -0,0 +1,90 @@ +import { criteortusIdSubmodule } from 'modules/criteortusIdSystem'; +import * as utils from 'src/utils'; + +describe('Criteo RTUS', function() { + let xhr; + let requests; + let getCookieStub; + let logErrorStub; + + beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + getCookieStub = sinon.stub(utils, 'getCookie'); + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + xhr.restore(); + getCookieStub.restore(); + logErrorStub.restore(); + }); + + it('should log error when configParams are not passed', function() { + criteortusIdSubmodule.getId(); + expect(logErrorStub.calledOnce).to.be.true; + }) + + it('should call criteo endpoint to get user id', function() { + getCookieStub.returns(null); + let configParams = { + clientIdentifier: { + 'sampleBidder': 1 + } + } + + let response = { 'status': 'ok', 'userid': 'sample-userid' } + let callBackSpy = sinon.spy(); + const idResp = criteortusIdSubmodule.getId(configParams); + const submoduleCallback = idResp.callback; + submoduleCallback(callBackSpy); + requests[0].respond( + 200, + { 'Content-Type': 'text/plain' }, + JSON.stringify(response) + ); + expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.calledWith({'sampleBidder': response})).to.be.true; + }) + + it('should get uid from cookie and not call endpoint', function() { + let response = {'appnexus': {'status': 'ok', 'userid': 'sample-userid'}} + getCookieStub.returns(JSON.stringify(response)); + let configParams = { + clientIdentifier: { + 'sampleBidder': 1 + } + } + let uid = criteortusIdSubmodule.getId(configParams); + expect(requests.length).to.equal(0); + }) + + it('should call criteo endpoint for multiple bidders', function() { + getCookieStub.returns(null); + let configParams = { + clientIdentifier: { + 'sampleBidder': 1, + 'sampleBidder2': 2 + } + } + + let response = { 'status': 'ok', 'userid': 'sample-userid' } + let callBackSpy = sinon.spy(); + const idResp = criteortusIdSubmodule.getId(configParams); + const submoduleCallback = idResp.callback; + submoduleCallback(callBackSpy); + requests[0].respond( + 200, + { 'Content-Type': 'text/plain' }, + JSON.stringify(response) + ); + expect(callBackSpy.calledOnce).to.be.false; + requests[1].respond( + 200, + { 'Content-Type': 'text/plain' }, + JSON.stringify(response) + ); + expect(callBackSpy.calledOnce).to.be.true; + }) +}); diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index a4ef643fece..98f51b72251 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -10,42 +10,122 @@ import { currencyRates } from 'modules/currency'; -import { createHook } from 'src/hook'; - var assert = require('chai').assert; var expect = require('chai').expect; describe('currency', function () { let fakeCurrencyFileServer; + let sandbox; + let clock; let fn = sinon.spy(); - let hookFn = createHook('asyncSeries', fn, 'addBidResponse'); - beforeEach(() => { + beforeEach(function () { fakeCurrencyFileServer = sinon.fakeServer.create(); }); - afterEach(() => { + afterEach(function () { fakeCurrencyFileServer.restore(); + setConfig({}); }); - describe('setConfig', () => { - it('results in currencySupportEnabled = false when currency not configured', () => { + describe('setConfig', function () { + beforeEach(function() { + sandbox = sinon.sandbox.create(); + clock = sinon.useFakeTimers(1046952000000); // 2003-03-06T12:00:00Z + }); + + afterEach(function () { + sandbox.restore(); + clock.restore(); + }); + + it('results in currencySupportEnabled = false when currency not configured', function () { setConfig({}); expect(currencySupportEnabled).to.equal(false); }); - it('results in currencySupportEnabled = true and currencyRates being loaded when configured', () => { + it('results in currencySupportEnabled = true and currencyRates being loaded when configured', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'JPY' }); fakeCurrencyFileServer.respond(); expect(currencyRates.dataAsOf).to.equal('2017-04-25'); expect(currencySupportEnabled).to.equal(true); }); + + it('currency file is called even when default rates are specified', function() { + // RESET to request currency file (specifically url value for this test) + setConfig({ 'adServerCurrency': undefined }); + + // DO NOT SET DEFAULT RATES, currency file should be requested + setConfig({ + 'adServerCurrency': 'JPY' + }); + fakeCurrencyFileServer.respond(); + expect(fakeCurrencyFileServer.requests.length).to.equal(1); + expect(fakeCurrencyFileServer.requests[0].url).to.equal('https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=20030306'); + + // RESET to request currency file (specifically url value for this test) + setConfig({ 'adServerCurrency': undefined }); + + // SET DEFAULT RATES, currency file should STILL be requested + setConfig({ + 'adServerCurrency': 'JPY', + 'defaultRates': { + 'GBP': { 'CNY': 66, 'JPY': 132, 'USD': 264 }, + 'USD': { 'CNY': 60, 'GBP': 120, 'JPY': 240 } + } }); + fakeCurrencyFileServer.respond(); + expect(fakeCurrencyFileServer.requests.length).to.equal(2); + expect(fakeCurrencyFileServer.requests[1].url).to.equal('https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=20030306'); + }); + + it('date macro token $$TODAY$$ is replaced by current date (formatted as yyyymmdd)', function () { + // RESET to request currency file (specifically url value for this test) + setConfig({ 'adServerCurrency': undefined }); + + // date macro should replace $$TODAY$$ with date when DEFAULT_CURRENCY_RATE_URL is used + setConfig({ 'adServerCurrency': 'JPY' }); + fakeCurrencyFileServer.respond(); + expect(fakeCurrencyFileServer.requests[0].url).to.equal('https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=20030306'); + + // RESET to request currency file (specifically url value for this test) + setConfig({ 'adServerCurrency': undefined }); + + // date macro should not modify 'conversionRateFile' if TOKEN is not found + setConfig({ + 'adServerCurrency': 'JPY', + 'conversionRateFile': 'http://test.net/currency.json?date=foobar' + }); + fakeCurrencyFileServer.respond(); + expect(fakeCurrencyFileServer.requests[1].url).to.equal('http://test.net/currency.json?date=foobar'); + + // RESET to request currency file (specifically url value for this test) + setConfig({ 'adServerCurrency': undefined }); + + // date macro should replace $$TODAY$$ with date for 'conversionRateFile' is configured + setConfig({ + 'adServerCurrency': 'JPY', + 'conversionRateFile': 'http://test.net/currency.json?date=$$TODAY$$' + }); + fakeCurrencyFileServer.respond(); + expect(fakeCurrencyFileServer.requests[2].url).to.equal('http://test.net/currency.json?date=20030306'); + + // RESET to request currency file (specifically url value for this test) + setConfig({ 'adServerCurrency': undefined }); + + // MULTIPLE TOKENS used in a url is not supported. Only the TOKEN at left-most position is REPLACED + setConfig({ + 'adServerCurrency': 'JPY', + 'conversionRateFile': 'http://test.net/$$TODAY$$/currency.json?date=$$TODAY$$' + }); + fakeCurrencyFileServer.respond(); + expect(fakeCurrencyFileServer.requests[3].url).to.equal('http://test.net/20030306/currency.json?date=$$TODAY$$'); + }); }); - describe('bidder override', () => { - it('allows setConfig to set bidder currency', () => { + describe('bidder override', function () { + it('allows setConfig to set bidder currency', function () { setConfig({}); var bid = { cpm: 1, bidder: 'rubicon' }; @@ -58,14 +138,16 @@ describe('currency', function () { } }); - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); - expect(innerBid.currency).to.equal('GBP') + expect(innerBid.currency).to.equal('GBP'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('GBP')).to.equal('1.000'); }); - it('uses adapter currency over currency override if specified', () => { + it('uses adapter currency over currency override if specified', function () { setConfig({}); var bid = { cpm: 1, currency: 'JPY', bidder: 'rubicon' }; @@ -78,14 +160,16 @@ describe('currency', function () { } }); - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); - expect(innerBid.currency).to.equal('JPY') + expect(innerBid.currency).to.equal('JPY'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('1.000'); }); - it('uses rates specified in json when provided', () => { + it('uses rates specified in json when provided', function () { setConfig({ adServerCurrency: 'USD', rates: { @@ -98,14 +182,44 @@ describe('currency', function () { var bid = { cpm: 100, currency: 'JPY', bidder: 'rubicon' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); + + expect(innerBid.cpm).to.equal('1.0000'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); + }); + + it('uses rates specified in json when provided and consider boosted bid', function () { + setConfig({ + adServerCurrency: 'USD', + rates: { + USD: { + JPY: 100 + } + } }); + var bid = { cpm: 100, currency: 'JPY', bidder: 'rubicon' }; + var innerBid; + + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); + expect(innerBid.cpm).to.equal('1.0000'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); + + // Boosting the bid now + innerBid.cpm *= 10; + expect(innerBid.cpm).to.equal(10.0000); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('1000.000'); }); - it('uses default rates when currency file fails to load', () => { + it('uses default rates when currency file fails to load', function () { setConfig({}); setConfig({ @@ -123,16 +237,18 @@ describe('currency', function () { var bid = { cpm: 100, currency: 'JPY', bidder: 'rubicon' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.cpm).to.equal('1.0000'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); }); }); - describe('currency.addBidResponseDecorator bidResponseQueue', () => { - it('not run until currency rates file is loaded', () => { + describe('currency.addBidResponseDecorator bidResponseQueue', function () { + it('not run until currency rates file is loaded', function () { setConfig({}); fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); @@ -142,9 +258,9 @@ describe('currency', function () { setConfig({ 'adServerCurrency': 'JPY' }); var marker = false; - addBidResponseHook('elementId', bid, function() { - marker = true; - }); + addBidResponseHook(function() { + marker = true; + }, 'elementId', bid); expect(marker).to.equal(false); @@ -153,111 +269,115 @@ describe('currency', function () { }); }); - describe('currency.addBidResponseDecorator', () => { - it('should leave bid at 1 when currency support is not enabled and fromCurrency is USD', () => { + describe('currency.addBidResponseDecorator', function () { + it('should leave bid at 1 when currency support is not enabled and fromCurrency is USD', function () { setConfig({}); var bid = { 'cpm': 1, 'currency': 'USD' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.cpm).to.equal(1); }); - it('should result in NO_BID when currency support is not enabled and fromCurrency is not USD', () => { + it('should result in NO_BID when currency support is not enabled and fromCurrency is not USD', function () { setConfig({}); + var bid = { 'cpm': 1, 'currency': 'GBP' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.statusMessage).to.equal('Bid returned empty or error response'); }); - it('should not buffer bid when currency is already in desired currency', () => { + it('should not buffer bid when currency is already in desired currency', function () { setConfig({ 'adServerCurrency': 'USD' }); var bid = { 'cpm': 1, 'currency': 'USD' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(bid).to.equal(innerBid); }); - it('should result in NO_BID when fromCurrency is not supported in file', () => { + it('should result in NO_BID when fromCurrency is not supported in file', function () { + // RESET to request currency file + setConfig({ 'adServerCurrency': undefined }); + fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'JPY' }); fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'ABC' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.statusMessage).to.equal('Bid returned empty or error response'); }); - it('should result in NO_BID when adServerCurrency is not supported in file', () => { + it('should result in NO_BID when adServerCurrency is not supported in file', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'ABC' }); fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'GBP' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.statusMessage).to.equal('Bid returned empty or error response'); }); - it('should return 1 when currency support is enabled and same currency code is requested as is set to adServerCurrency', () => { + it('should return 1 when currency support is enabled and same currency code is requested as is set to adServerCurrency', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'JPY' }); fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'JPY' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.cpm).to.equal(1); expect(innerBid.currency).to.equal('JPY'); }); - it('should return direct conversion rate when fromCurrency is one of the configured bases', () => { + it('should return direct conversion rate when fromCurrency is one of the configured bases', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'GBP' }); fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'USD' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.cpm).to.equal('0.7798'); expect(innerBid.currency).to.equal('GBP'); }); - it('should return reciprocal conversion rate when adServerCurrency is one of the configured bases, but fromCurrency is not', () => { + it('should return reciprocal conversion rate when adServerCurrency is one of the configured bases, but fromCurrency is not', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'GBP' }); fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'CNY' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.cpm).to.equal('0.1133'); expect(innerBid.currency).to.equal('GBP'); }); - it('should return intermediate conversion rate when neither fromCurrency nor adServerCurrency is one of the configured bases', () => { + it('should return intermediate conversion rate when neither fromCurrency nor adServerCurrency is one of the configured bases', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'CNY' }); fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'JPY' }; var innerBid; - addBidResponseHook('elementId', bid, function(adCodeId, bid) { - innerBid = bid; - }); + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); expect(innerBid.cpm).to.equal('0.0623'); expect(innerBid.currency).to.equal('CNY'); }); diff --git a/test/spec/modules/danmarketBidAdapter_spec.js b/test/spec/modules/danmarketBidAdapter_spec.js index d1b86d1bf53..973cd2afb1c 100644 --- a/test/spec/modules/danmarketBidAdapter_spec.js +++ b/test/spec/modules/danmarketBidAdapter_spec.js @@ -5,13 +5,13 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('DAN_Marketplace Adapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'danmarket', 'params': { @@ -24,11 +24,11 @@ describe('DAN_Marketplace Adapter', function () { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -38,7 +38,7 @@ describe('DAN_Marketplace Adapter', function () { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': 'danmarket', @@ -75,7 +75,7 @@ describe('DAN_Marketplace Adapter', function () { } ]; - it('should attach valid params to the tag', () => { + it('should attach valid params to the tag', function () { const request = spec.buildRequests([bidRequests[0]]); const payload = request.data; expect(payload).to.be.an('object'); @@ -84,7 +84,7 @@ describe('DAN_Marketplace Adapter', function () { expect(payload).to.have.property('auids', '5'); }); - it('auids must not be duplicated', () => { + it('auids must not be duplicated', function () { const request = spec.buildRequests(bidRequests); const payload = request.data; expect(payload).to.be.an('object'); @@ -93,7 +93,7 @@ describe('DAN_Marketplace Adapter', function () { expect(payload).to.have.property('auids', '5,6'); }); - it('pt parameter must be "gross" if params.priceType === "gross"', () => { + it('pt parameter must be "gross" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; const request = spec.buildRequests(bidRequests); const payload = request.data; @@ -104,7 +104,7 @@ describe('DAN_Marketplace Adapter', function () { delete bidRequests[1].params.priceType; }); - it('pt parameter must be "net" or "gross"', () => { + it('pt parameter must be "net" or "gross"', function () { bidRequests[1].params.priceType = 'some'; const request = spec.buildRequests(bidRequests); const payload = request.data; @@ -114,9 +114,33 @@ describe('DAN_Marketplace Adapter', function () { expect(payload).to.have.property('auids', '5,6'); delete bidRequests[1].params.priceType; }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 1); + }); + + it('if gdprApplies is false gdpr_applies must be 0', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 0); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 1); + }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 4, 'h': 250, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 5, 'h': 90, 'w': 728}], 'seat': '1'}, @@ -127,7 +151,7 @@ describe('DAN_Marketplace Adapter', function () { {'seat': '1'}, ]; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const bidRequests = [ { 'bidder': 'danmarket', @@ -161,7 +185,7 @@ describe('DAN_Marketplace Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('should get correct multi bid response', () => { + it('should get correct multi bid response', function () { const bidRequests = [ { 'bidder': 'danmarket', @@ -241,7 +265,7 @@ describe('DAN_Marketplace Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('handles wrong and nobid responses', () => { + it('handles wrong and nobid responses', function () { const bidRequests = [ { 'bidder': 'danmarket', diff --git a/test/spec/modules/datablocksBidAdapter_spec.js b/test/spec/modules/datablocksBidAdapter_spec.js new file mode 100644 index 00000000000..d39116ccb71 --- /dev/null +++ b/test/spec/modules/datablocksBidAdapter_spec.js @@ -0,0 +1,334 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/datablocksBidAdapter'; + +let bid = { + bidId: '2dd581a2b6281d', + bidder: 'datablocks', + bidderRequestId: '145e1d6a7837c9', + params: { + sourceId: 7560, + host: 'v5demo.datablocks.net' + }, + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + sizes: [ + [300, 250] + ], + transactionId: '1ccbee15-f6f6-46ce-8998-58fe5542e8e1' +}; + +let bid2 = { + bidId: '2dd581a2b624324g', + bidder: 'datablocks', + bidderRequestId: '145e1d6a7837543', + params: { + sourceId: 7560, + host: 'v5demo.datablocks.net' + }, + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + mediaTypes: { + banner: { + sizes: + [728, 90] + } + }, + transactionId: '1ccbee15-f6f6-46ce-8998-58fe55425432' +}; + +let nativeBid = { + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '160c78a4-f808-410f-b682-d8728f3a79ee', + bidId: '332045ee374a99', + bidder: 'datablocks', + bidderRequestId: '15d9012765e36c', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + image: { + required: true + } + } + }, + nativeParams: { + title: { + required: true + }, + body: { + required: true, + data: { + len: 250 + } + }, + image: { + required: true, + sizes: [728, 90] + } + }, + params: { + sourceId: 7560, + host: 'v5demo.datablocks.net' + }, + transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f6' +} + +let videoBid = { + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '160c78a4-f808-410f-b682-d8728f3a79e1', + bidId: '332045ee374b99', + bidder: 'datablocks', + bidderRequestId: '15d9012765e36d', + mediaTypes: { + video: { + context: 'instream', + playerSize: [501, 400], + durationRangeSec: [15, 60] + } + }, + params: { + sourceId: 7560, + host: 'v5demo.datablocks.net', + video: { + minduration: 14 + } + }, + transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f7' +} + +const bidderRequest = { + auctionId: '8bfef1be-d3ac-4d18-8859-754c7b4cf017', + auctionStart: Date.now(), + biddeCode: 'datablocks', + bidderRequestId: '10c47a5fc3c41', + bids: [bid, bid2, nativeBid, videoBid], + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'http://v5demo.datablocks.net/test', + stack: ['http://v5demo.datablocks.net/test'] + }, + start: Date.now(), + timeout: 10000 +}; + +let resObject = { + body: { + id: '10c47a5fc3c41', + bidid: '166895245-28-11347-1', + seatbid: [{ + seat: '7560', + bid: [{ + id: '1090738570', + impid: '2966b257c81d27', + price: 24.000000, + adm: 'RON', + cid: '55', + adid: '177654', + crid: '177656', + cat: [], + api: [], + w: 300, + h: 250 + }, { + id: '1090738571', + impid: '2966b257c81d28', + price: 24.000000, + adm: 'RON', + cid: '55', + adid: '177654', + crid: '177656', + cat: [], + api: [], + w: 728, + h: 90 + }, { + id: '1090738570', + impid: '15d9012765e36c', + price: 24.000000, + adm: '{"native":{"ver":"1.2","assets":[{"id":1,"required":1,"title":{"text":"Example Title"}},{"id":2,"required":1,"data":{"value":"Example Body"}},{"id":3,"required":1,"img":{"url":"http://example.image.com/"}}],"link":{"url":"http://click.example.com/c/264597/?fcid=29699699045816"},"imptrackers":["http://impression.example.com/i/264597/?fcid=29699699045816"]}}', + cid: '132145', + adid: '154321', + crid: '177432', + cat: [], + api: [] + }, { + id: '1090738575', + impid: '15d9012765e36f', + price: 25.000000, + cid: '12345', + adid: '12345', + crid: '123456', + nurl: 'http://click.v5demo.datablocks.net/m//?fcid=435235435432', + cat: [], + api: [], + w: 500, + h: 400 + }] + }], + cur: 'USD', + ext: {} + } +}; +let bidRequest = { + method: 'POST', + url: '//v5demo.datablocks.net/search/?sid=7560', + options: { + withCredentials: false + }, + data: { + device: { + ip: 'peer', + ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) Ap…ML, like Gecko) Chrome/73.0.3683.86 Safari/537.36', + js: 1, + language: 'en' + }, + id: '10c47a5fc3c41', + imp: [{ + banner: { w: 300, h: 250 }, + id: '2966b257c81d27', + secure: false, + tagid: '/19968336/header-bid-tag-0' + }, { + banner: { w: 728, h: 90 }, + id: '2966b257c81d28', + secure: false, + tagid: '/19968336/header-bid-tag-0' + }, { + id: '15d9012765e36c', + native: {request: '{"native":{"assets":[{"id":"1","required":true,"title":{"len":140}},{"id":"2","required":true,"data":{"type":2}},{"id":"3","img":{"w":728,"h":90,"type":3}}]}}'}, + secure: false, + tagid: '/19968336/header-bid-tag-0' + }, { + id: '15d9012765e36f', + video: {w: 500, h: 400, minduration: 15, maxduration: 60}, + secure: false, + tagid: '/19968336/header-bid-tag-0' + }], + site: { + domain: '', + id: 'blank', + page: 'http://v5demo.datablocks.net/test' + } + } +} + +describe('DatablocksAdapter', function() { + describe('isBidRequestValid', function() { + it('Should return true when sourceId and Host are set', function() { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when host/sourceId is not set', function() { + let moddedBid = Object.assign({}, bid); + delete moddedBid.params.sourceId; + delete moddedBid.params.host; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function() { + let requests = spec.buildRequests([bid, bid2, nativeBid, videoBid], bidderRequest); + it('Creates an array of request objects', function() { + expect(requests).to.be.an('array').that.is.not.empty; + }); + + requests.forEach(request => { + expect(request).to.exist; + it('Returns POST method', function() { + expect(request.method).to.exist; + expect(request.method).to.equal('POST'); + }); + it('Returns valid URL', function() { + expect(request.url).to.exist; + expect(request.url).to.equal('//v5demo.datablocks.net/search/?sid=7560'); + }); + + it('Should be a valid openRTB request', function() { + let data = request.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('device', 'imp', 'site', 'id'); + expect(data.id).to.be.a('string'); + + let imps = data['imp']; + imps.forEach((imp, index) => { + let curBid = bidderRequest.bids[index]; + if (imp.banner) { + expect(imp).to.have.all.keys('banner', 'id', 'secure', 'tagid'); + expect(imp.banner).to.be.a('object'); + } else if (imp.native) { + expect(imp).to.have.all.keys('native', 'id', 'secure', 'tagid'); + expect(imp.native).to.have.all.keys('request'); + expect(imp.native.request).to.be.a('string'); + let native = JSON.parse(imp.native.request); + expect(native).to.be.a('object'); + } else if (imp.video) { + expect(imp).to.have.all.keys('video', 'id', 'secure', 'tagid'); + expect(imp.video).to.have.all.keys('w', 'h', 'minduration', 'maxduration') + } else { + expect(true).to.equal(false); + } + + expect(imp.id).to.be.a('string'); + expect(imp.id).to.equal(curBid.bidId); + expect(imp.tagid).to.be.a('string'); + expect(imp.tagid).to.equal(curBid.adUnitCode); + expect(imp.secure).to.equal(false); + }) + + expect(data.device.ip).to.equal('peer'); + }); + }) + + it('Returns empty data if no valid requests are passed', function() { + let request = spec.buildRequests([]); + expect(request).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function() { + let serverResponses = spec.interpretResponse(resObject, bidRequest); + it('Returns an array of valid server responses if response object is valid', function() { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(Object.keys(dataItem)).to.include('cpm', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType', 'requestId'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + + if (dataItem.mediaType == 'banner') { + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + } else if (dataItem.mediaType == 'native') { + expect(dataItem.native.title).to.be.a('string'); + expect(dataItem.native.body).to.be.a('string'); + expect(dataItem.native.clickUrl).to.be.a('string'); + } else if (dataItem.mediaType == 'video') { + expect(dataItem.vastUrl).to.be.a('string'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + } + } + it('Returns an empty array if invalid response is passed', function() { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); +}); diff --git a/test/spec/modules/decenteradsBidAdapter_spec.js b/test/spec/modules/decenteradsBidAdapter_spec.js new file mode 100644 index 00000000000..3c8569b3b61 --- /dev/null +++ b/test/spec/modules/decenteradsBidAdapter_spec.js @@ -0,0 +1,207 @@ +import { expect } from 'chai' +import { spec } from '../../../modules/decenteradsBidAdapter' +import { deepStrictEqual, notEqual, ok, strictEqual } from 'assert' + +describe('DecenteradsAdapter', () => { + const bid = { + bidId: '9ec5b177515ee2e5', + bidder: 'decenterads', + params: { + placementId: 0, + traffic: 'banner' + } + } + + describe('isBidRequestValid', () => { + it('Should return true if there are bidId, params and placementId parameters present', () => { + strictEqual(true, spec.isBidRequestValid(bid)) + }) + + it('Should return false if at least one of parameters is not present', () => { + const b = { ...bid } + delete b.params.placementId + strictEqual(false, spec.isBidRequestValid(b)) + }) + }) + + describe('buildRequests', () => { + const serverRequest = spec.buildRequests([bid]) + + it('Creates a ServerRequest object with method, URL and data', () => { + ok(serverRequest) + ok(serverRequest.method) + ok(serverRequest.url) + ok(serverRequest.data) + }) + + it('Returns POST method', () => { + strictEqual('POST', serverRequest.method) + }) + + it('Returns valid URL', () => { + strictEqual('//supply.decenterads.com/?c=o&m=multi', serverRequest.url) + }) + + it('Returns valid data if array of bids is valid', () => { + const { data } = serverRequest + strictEqual('object', typeof data) + deepStrictEqual(['deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'], Object.keys(data)) + strictEqual('number', typeof data.deviceWidth) + strictEqual('number', typeof data.deviceHeight) + strictEqual('string', typeof data.language) + strictEqual('string', typeof data.host) + strictEqual('string', typeof data.page) + notEqual(-1, [0, 1].indexOf(data.secure)) + + const placement = data.placements[0] + deepStrictEqual(['placementId', 'bidId', 'traffic'], Object.keys(placement)) + strictEqual(0, placement.placementId) + strictEqual('9ec5b177515ee2e5', placement.bidId) + strictEqual('banner', placement.traffic) + }) + + it('Returns empty data if no valid requests are passed', () => { + const { placements } = spec.buildRequests([]).data + + expect(spec.buildRequests([]).data.placements).to.be.an('array') + strictEqual(0, placements.length) + }) + }) + + describe('interpretResponse', () => { + const validData = [ + { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + vastUrl: 'decenterads.com', + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'decenterads.com', + title: 'Test', + image: 'decenterads.com', + creativeId: '2', + impressionTrackers: ['decenterads.com'], + ttl: 120, + cpm: 0.4, + requestId: '9ec5b177515ee2e5', + netRevenue: true, + currency: 'USD', + }] + } + ] + + for (const obj of validData) { + const { mediaType } = obj.body[0] + + it(`Should interpret ${mediaType} response`, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(1, response.length) + + const copy = { ...obj.body[0] } + delete copy.mediaType + deepStrictEqual(copy, response[0]) + }) + } + + const invalidData = [ + { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'decenterads.com', + title: 'Test', + impressionTrackers: ['decenterads.com'], + ttl: 120, + requestId: '9ec5b177515ee2e5', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + } + ] + + for (const obj of invalidData) { + const { mediaType } = obj.body[0] + + it(`Should return an empty array if invalid ${mediaType} response is passed `, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + } + + it('Should return an empty array if invalid response is passed', () => { + const response = spec.interpretResponse({ + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + }) + + describe('getUserSyncs', () => { + it('Returns valid URL and type', () => { + const expectedResult = [{ type: 'image', url: '//supply.decenterads.com/?c=o&m=cookie' }] + deepStrictEqual(expectedResult, spec.getUserSyncs()) + }) + }) +}) diff --git a/test/spec/modules/dfpAdServerVideo_spec.js b/test/spec/modules/dfpAdServerVideo_spec.js index c4a799b0b0a..6271c9b38f4 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -1,17 +1,25 @@ import { expect } from 'chai'; import parse from 'url-parse'; -import buildDfpVideoUrl from 'modules/dfpAdServerVideo'; +import { buildDfpVideoUrl, buildAdpodVideoUrl } from 'modules/dfpAdServerVideo'; import { parseQS } from 'src/url'; import adUnit from 'test/fixtures/video/adUnit'; +import * as utils from 'src/utils'; +import { config } from 'src/config'; +import { targeting } from 'src/targeting'; +import { auctionManager } from 'src/auctionManager'; +import * as adpod from 'modules/adpod'; const bid = { videoCacheKey: 'abc', - adserverTargeting: { }, + adserverTargeting: { + hb_uuid: 'abc', + hb_cache_id: 'abc', + }, }; -describe('The DFP video support module', () => { - it('should make a legal request URL when given the required params', () => { +describe('The DFP video support module', function () { + it('should make a legal request URL when given the required params', function () { const url = parse(buildDfpVideoUrl({ adUnit: adUnit, bid: bid, @@ -22,7 +30,7 @@ describe('The DFP video support module', () => { })); expect(url.protocol).to.equal('https:'); - expect(url.host).to.equal('pubads.g.doubleclick.net'); + expect(url.host).to.equal('securepubads.g.doubleclick.net'); const queryParams = parseQS(url.query); expect(queryParams).to.have.property('correlator'); @@ -36,8 +44,8 @@ describe('The DFP video support module', () => { expect(queryParams).to.have.property('url'); }); - it('can take an adserver url as a parameter', () => { - const bidCopy = Object.assign({ }, bid); + it('can take an adserver url as a parameter', function () { + const bidCopy = utils.deepClone(bid); bidCopy.vastUrl = 'vastUrl.example'; const url = parse(buildDfpVideoUrl({ @@ -52,7 +60,7 @@ describe('The DFP video support module', () => { expect(queryObject.description_url).to.equal('vastUrl.example'); }); - it('requires a params object or url', () => { + it('requires a params object or url', function () { const url = buildDfpVideoUrl({ adUnit: adUnit, bid: bid, @@ -61,7 +69,7 @@ describe('The DFP video support module', () => { expect(url).to.be.undefined; }); - it('overwrites url params when both url and params object are given', () => { + it('overwrites url params when both url and params object are given', function () { const url = parse(buildDfpVideoUrl({ adUnit: adUnit, bid: bid, @@ -73,7 +81,7 @@ describe('The DFP video support module', () => { expect(queryObject.iu).to.equal('my/adUnit'); }); - it('should override param defaults with user-provided ones', () => { + it('should override param defaults with user-provided ones', function () { const url = parse(buildDfpVideoUrl({ adUnit: adUnit, bid: bid, @@ -86,11 +94,11 @@ describe('The DFP video support module', () => { expect(parseQS(url.query)).to.have.property('output', 'vast'); }); - it('should include the cache key and adserver targeting in cust_params', () => { - const bidCopy = Object.assign({ }, bid); - bidCopy.adserverTargeting = { + it('should include the cache key and adserver targeting in cust_params', function () { + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { hb_adid: 'ad_id', - }; + }); const url = parse(buildDfpVideoUrl({ adUnit: adUnit, @@ -107,11 +115,84 @@ describe('The DFP video support module', () => { expect(customParams).to.have.property('hb_cache_id', bid.videoCacheKey); }); - it('should merge the user-provided cust_params with the default ones', () => { - const bidCopy = Object.assign({ }, bid); - bidCopy.adserverTargeting = { - hb_adid: 'ad_id', + describe('special targeting unit test', function () { + const allTargetingData = { + 'hb_format': 'video', + 'hb_source': 'client', + 'hb_size': '640x480', + 'hb_pb': '5.00', + 'hb_adid': '2c4f6cc3ba128a', + 'hb_bidder': 'testBidder2', + 'hb_format_testBidder2': 'video', + 'hb_source_testBidder2': 'client', + 'hb_size_testBidder2': '640x480', + 'hb_pb_testBidder2': '5.00', + 'hb_adid_testBidder2': '2c4f6cc3ba128a', + 'hb_bidder_testBidder2': 'testBidder2', + 'hb_format_appnexus': 'video', + 'hb_source_appnexus': 'client', + 'hb_size_appnexus': '640x480', + 'hb_pb_appnexus': '5.00', + 'hb_adid_appnexus': '44e0b5f2e5cace', + 'hb_bidder_appnexus': 'appnexus' }; + let targetingStub; + + before(function () { + targetingStub = sinon.stub(targeting, 'getAllTargeting'); + targetingStub.returns({'video1': allTargetingData}); + + config.setConfig({ + enableSendAllBids: true + }); + }); + + after(function () { + config.resetConfig(); + targetingStub.restore(); + }); + + it('should include all adserver targeting in cust_params if pbjs.enableSendAllBids is true', function () { + const adUnitsCopy = utils.deepClone(adUnit); + adUnitsCopy.bids.push({ + 'bidder': 'testBidder2', + 'params': { + 'placementId': '9333431', + 'video': { + 'skipppable': false, + 'playback_methods': ['auto_play_sound_off'] + } + } + }); + + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnitsCopy, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = parseQS(url.query); + const customParams = parseQS('?' + decodeURIComponent(queryObject.cust_params)); + + expect(customParams).to.have.property('hb_adid', 'ad_id'); + expect(customParams).to.have.property('hb_uuid', bid.videoCacheKey); + expect(customParams).to.have.property('hb_cache_id', bid.videoCacheKey); + expect(customParams).to.have.property('hb_bidder_appnexus', 'appnexus'); + expect(customParams).to.have.property('hb_bidder_testBidder2', 'testBidder2'); + }); + }); + + it('should merge the user-provided cust_params with the default ones', function () { + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); const url = parse(buildDfpVideoUrl({ adUnit: adUnit, @@ -130,11 +211,11 @@ describe('The DFP video support module', () => { expect(customParams).to.have.property('my_targeting', 'foo'); }); - it('should merge the user-provided cust-params with the default ones when using url object', () => { - const bidCopy = Object.assign({ }, bid); - bidCopy.adserverTargeting = { + it('should merge the user-provided cust-params with the default ones when using url object', function () { + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { hb_adid: 'ad_id', - }; + }); const url = parse(buildDfpVideoUrl({ adUnit: adUnit, @@ -152,8 +233,8 @@ describe('The DFP video support module', () => { expect(customParams).to.have.property('hb_cache_id', 'abc'); }); - it('should not overwrite an existing description_url for object input and cache disabled', () => { - const bidCopy = Object.assign({}, bid); + it('should not overwrite an existing description_url for object input and cache disabled', function () { + const bidCopy = utils.deepClone(bid); bidCopy.vastUrl = 'vastUrl.example'; const url = parse(buildDfpVideoUrl({ @@ -169,7 +250,7 @@ describe('The DFP video support module', () => { expect(queryObject.description_url).to.equal('descriptionurl.example'); }); - it('should work with nobid responses', () => { + it('should work with nobid responses', function () { const url = buildDfpVideoUrl({ adUnit: adUnit, params: { 'iu': 'my/adUnit' } @@ -177,4 +258,286 @@ describe('The DFP video support module', () => { expect(url).to.be.a('string'); }); + + it('should include hb_uuid and hb_cache_id in cust_params when both keys are exluded from overwritten bidderSettings', function () { + const bidCopy = utils.deepClone(bid); + delete bidCopy.adserverTargeting.hb_uuid; + delete bidCopy.adserverTargeting.hb_cache_id; + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = parseQS(url.query); + const customParams = parseQS('?' + decodeURIComponent(queryObject.cust_params)); + + expect(customParams).to.have.property('hb_uuid', bid.videoCacheKey); + expect(customParams).to.have.property('hb_cache_id', bid.videoCacheKey); + }); + + it('should include hb_uuid and hb_cache_id in cust params from overwritten standard bidderSettings', function () { + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_uuid: 'def', + hb_cache_id: 'def' + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = parseQS(url.query); + const customParams = parseQS('?' + decodeURIComponent(queryObject.cust_params)); + + expect(customParams).to.have.property('hb_uuid', 'def'); + expect(customParams).to.have.property('hb_cache_id', 'def'); + }); + + describe('adpod unit tests', function () { + let amStub; + let amGetAdUnitsStub; + let xhr; + let requests; + + before(function () { + let adUnits = [{ + code: 'adUnitCode-1', + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 60, + durationRangeSec: [15, 30], + requireExactDuration: true + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: 14542875, + } + } + ] + }]; + + amGetAdUnitsStub = sinon.stub(auctionManager, 'getAdUnits'); + amGetAdUnitsStub.returns(adUnits); + amStub = sinon.stub(auctionManager, 'getBidsReceived'); + }); + + beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + + config.setConfig({ + adpod: { + brandCategoryExclusion: true, + deferCaching: false + } + }); + }) + + afterEach(function() { + config.resetConfig(); + xhr.restore(); + }); + + after(function () { + amGetAdUnitsStub.restore(); + amStub.restore(); + }); + + it('should return masterTag url', function() { + amStub.returns(getBidsReceived()); + let url; + parse(buildAdpodVideoUrl({ + code: 'adUnitCode-1', + callback: handleResponse, + params: { + 'iu': 'my/adUnit', + 'description_url': 'someUrl.com', + } + })); + + function handleResponse(err, masterTag) { + if (err) { + return; + } + url = parse(masterTag); + + expect(url.protocol).to.equal('https:'); + expect(url.host).to.equal('securepubads.g.doubleclick.net'); + + const queryParams = parseQS(url.query); + expect(queryParams).to.have.property('correlator'); + expect(queryParams).to.have.property('description_url', 'someUrl.com'); + expect(queryParams).to.have.property('env', 'vp'); + expect(queryParams).to.have.property('gdfp_req', '1'); + expect(queryParams).to.have.property('iu', 'my/adUnit'); + expect(queryParams).to.have.property('output', 'xml_vast3'); + expect(queryParams).to.have.property('sz', '640x480'); + expect(queryParams).to.have.property('unviewed_position_start', '1'); + expect(queryParams).to.have.property('url'); + expect(queryParams).to.have.property('cust_params'); + + const custParams = parseQS(decodeURIComponent(queryParams.cust_params)); + expect(custParams).to.have.property('hb_cache_id', '123'); + expect(custParams).to.have.property('hb_pb_cat_dur', '15.00_395_15s,15.00_406_30s,10.00_395_15s'); + } + }); + + it('should return masterTag url with correct custom params when brandCategoryExclusion is false', function() { + config.setConfig({ + adpod: { + brandCategoryExclusion: false, + } + }); + function getBids() { + let bids = [ + createBid(10, 'adUnitCode-1', 15, '10.00_15s', '123', '395', '10.00'), + createBid(15, 'adUnitCode-1', 15, '15.00_15s', '123', '395', '15.00'), + createBid(25, 'adUnitCode-1', 30, '15.00_30s', '123', '406', '25.00'), + ]; + bids.forEach((bid) => { + delete bid.meta; + }); + return bids; + } + amStub.returns(getBids()); + let url; + parse(buildAdpodVideoUrl({ + code: 'adUnitCode-1', + callback: handleResponse, + params: { + 'iu': 'my/adUnit', + 'description_url': 'someUrl.com', + } + })); + + function handleResponse(err, masterTag) { + if (err) { + return; + } + url = parse(masterTag); + expect(url.protocol).to.equal('https:'); + expect(url.host).to.equal('securepubads.g.doubleclick.net'); + + const queryParams = parseQS(url.query); + expect(queryParams).to.have.property('correlator'); + expect(queryParams).to.have.property('description_url', 'someUrl.com'); + expect(queryParams).to.have.property('env', 'vp'); + expect(queryParams).to.have.property('gdfp_req', '1'); + expect(queryParams).to.have.property('iu', 'my/adUnit'); + expect(queryParams).to.have.property('output', 'xml_vast3'); + expect(queryParams).to.have.property('sz', '640x480'); + expect(queryParams).to.have.property('unviewed_position_start', '1'); + expect(queryParams).to.have.property('url'); + expect(queryParams).to.have.property('cust_params'); + + const custParams = parseQS(decodeURIComponent(queryParams.cust_params)); + expect(custParams).to.have.property('hb_cache_id', '123'); + expect(custParams).to.have.property('hb_pb_cat_dur', '10.00_15s,15.00_15s,15.00_30s'); + } + }); + + it('should handle error when cache fails', function() { + config.setConfig({ + adpod: { + brandCategoryExclusion: true, + deferCaching: true + } + }); + amStub.returns(getBidsReceived()); + + parse(buildAdpodVideoUrl({ + code: 'adUnitCode-1', + callback: handleResponse, + params: { + 'iu': 'my/adUnit', + 'description_url': 'someUrl.com', + } + })); + + requests[0].respond(503, { + 'Content-Type': 'plain/text', + }, 'The server could not save anything at the moment.'); + + function handleResponse(err, masterTag) { + expect(masterTag).to.be.null; + expect(err).to.be.an('error'); + } + }); + }) }); + +function getBidsReceived() { + return [ + createBid(10, 'adUnitCode-1', 15, '10.00_395_15s', '123', '395', '10.00'), + createBid(15, 'adUnitCode-1', 15, '15.00_395_15s', '123', '395', '15.00'), + createBid(25, 'adUnitCode-1', 30, '15.00_406_30s', '123', '406', '25.00'), + ] +} + +function createBid(cpm, adUnitCode, durationBucket, priceIndustryDuration, uuid, label, hbpb) { + return { + 'bidderCode': 'appnexus', + 'width': 640, + 'height': 360, + 'statusMessage': 'Bid available', + 'adId': '28f24ced14586c', + 'mediaType': 'video', + 'source': 'client', + 'requestId': '28f24ced14586c', + 'cpm': cpm, + 'creativeId': 97517771, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 3600, + 'adUnitCode': adUnitCode, + 'video': { + 'context': 'adpod', + 'durationBucket': durationBucket + }, + 'appnexus': { + 'buyerMemberId': 9325 + }, + 'vastUrl': 'http://some-vast-url.com', + 'vastImpUrl': 'http://some-vast-imp-url.com', + 'auctionId': 'ec266b31-d652-49c5-8295-e83fafe5532b', + 'responseTimestamp': 1548442460888, + 'requestTimestamp': 1548442460827, + 'bidder': 'appnexus', + 'timeToRespond': 61, + 'pbLg': '5.00', + 'pbMg': '5.00', + 'pbHg': '5.00', + 'pbAg': '5.00', + 'pbDg': '5.00', + 'pbCg': '', + 'size': '640x360', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '28f24ced14586c', + 'hb_pb': hbpb, + 'hb_size': '640x360', + 'hb_source': 'client', + 'hb_format': 'video', + 'hb_pb_cat_dur': priceIndustryDuration, + 'hb_cache_id': uuid + }, + 'customCacheKey': `${priceIndustryDuration}_${uuid}`, + 'meta': { + 'iabSubCatId': 'iab-1', + 'adServerCatId': label + }, + 'videoCacheKey': '4cf395af-8fee-4960-af0e-88d44e399f14' + } +} diff --git a/test/spec/modules/dgadsBidAdapter_spec.js b/test/spec/modules/dgadsBidAdapter_spec.js index 89affd94880..2454885217d 100644 --- a/test/spec/modules/dgadsBidAdapter_spec.js +++ b/test/spec/modules/dgadsBidAdapter_spec.js @@ -1,20 +1,21 @@ import {expect} from 'chai'; import * as utils from 'src/utils'; -import {spec} from 'modules/dgadsBidAdapter'; +import {spec, getCookieUid} from 'modules/dgadsBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; import { BANNER, NATIVE } from 'src/mediaTypes'; -describe('dgadsBidAdapter', () => { +describe('dgadsBidAdapter', function () { const adapter = newBidder(spec); + const UID_NAME = 'dgads_uid'; const VALID_ENDPOINT = 'https://ads-tr.bigmining.com/ad/p/bid'; - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'dgads', params: { @@ -22,11 +23,11 @@ describe('dgadsBidAdapter', () => { location_id: '1' } }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params(location_id) are not passed', () => { + it('should return false when required params(location_id) are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -35,7 +36,7 @@ describe('dgadsBidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when required params(site_id) are not passed', () => { + it('should return false when required params(site_id) are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -45,7 +46,7 @@ describe('dgadsBidAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { const bidRequests = [ { // banner bidder: 'dgads', @@ -97,31 +98,38 @@ describe('dgadsBidAdapter', () => { transactionId: 'c1f1eff6-23c6-4844-a321-575212939e37' } ]; - it('no bidRequests', () => { + it('no bidRequests', function () { const noBidRequests = []; expect(Object.keys(spec.buildRequests(noBidRequests)).length).to.equal(0); }); + it('getCookieUid return empty if cookie not found', function () { + expect(getCookieUid(UID_NAME)).to.equal(''); + }); const data = { location_id: '1', site_id: '1', transaction_id: 'c1f1eff6-23c6-4844-a321-575212939e37', - bid_id: '2db3101abaec66' + bid_id: '2db3101abaec66', + referer: utils.getTopWindowUrl(), + _uid: '' }; - it('sends bid request to VALID_ENDPOINT via POST', () => { + it('sends bid request to VALID_ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests)[0]; expect(request.url).to.equal(VALID_ENDPOINT); - expect(request.method).to.equal('POST'); + expect(request.method).to.equal('GET'); }); - it('should attache params to the request', () => { + it('should attache params to the request', function () { const request = spec.buildRequests(bidRequests)[0]; - expect(request.data['location_id']).to.equal(data['location_id']); - expect(request.data['site_id']).to.equal(data['site_id']); + expect(request.data['_loc']).to.equal(data['location_id']); + expect(request.data['_medium']).to.equal(data['site_id']); expect(request.data['transaction_id']).to.equal(data['transaction_id']); expect(request.data['bid_id']).to.equal(data['bid_id']); + expect(request.data['referer']).to.equal(data['referer']); + expect(request.data['_uid']).to.equal(data['_uid']); }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const bidRequests = { banner: { bidRequest: { @@ -252,11 +260,11 @@ describe('dgadsBidAdapter', () => { } }; - it('no bid responses', () => { + it('no bid responses', function () { const result = spec.interpretResponse({body: serverResponse.noAd}, bidRequests.banner); expect(result.length).to.equal(0); }); - it('handles banner responses', () => { + it('handles banner responses', function () { const result = spec.interpretResponse({body: serverResponse.banner}, bidRequests.banner)[0]; expect(result.requestId).to.equal(bidResponses.banner.requestId); expect(result.width).to.equal(bidResponses.banner.width); @@ -269,7 +277,7 @@ describe('dgadsBidAdapter', () => { expect(result.ad).to.equal(bidResponses.banner.ad); }); - it('handles native responses', () => { + it('handles native responses', function () { const result = spec.interpretResponse({body: serverResponse.native}, bidRequests.native)[0]; expect(result.requestId).to.equal(bidResponses.native.requestId); expect(result.creativeId).to.equal(bidResponses.native.creativeId); diff --git a/test/spec/modules/digitrustIdSystem_spec.js b/test/spec/modules/digitrustIdSystem_spec.js new file mode 100644 index 00000000000..55035bc4b4e --- /dev/null +++ b/test/spec/modules/digitrustIdSystem_spec.js @@ -0,0 +1,50 @@ +import { + digiTrustIdSubmodule, + surfaceTestHook +} from 'modules/digiTrustIdSystem'; + +let assert = require('chai').assert; +let expect = require('chai').expect; + +var testHook = null; + +describe('DigiTrust Id System', function () { + it('Should create the test hook', function (done) { + testHook = surfaceTestHook(); + assert.isNotNull(testHook, 'The test hook failed to surface'); + var conf = { + init: { + member: 'unit_test', + site: 'foo' + }, + callback: function (result) { + } + }; + testHook.initDigitrustFacade(conf); + window.DigiTrust.getUser(conf); + expect(window.DigiTrust).to.exist; + expect(window.DigiTrust.isMock).to.be.true; + done(); + }); + + it('Should report as client', function (done) { + delete window.DigiTrust; + testHook = surfaceTestHook(); + + var conf = { + init: { + member: 'unit_test', + site: 'foo' + }, + callback: function (result) { + expect(window.DigiTrust).to.exist; + expect(result).to.exist; + expect(window.DigiTrust.isMock).to.be.true; + } + }; + testHook.initDigitrustFacade(conf); + expect(window.DigiTrust).to.exist; + expect(window.DigiTrust.isClient).to.be.true; + done(); + }); +}); diff --git a/test/spec/modules/districtmDmxBidAdapter_spec.js b/test/spec/modules/districtmDmxBidAdapter_spec.js new file mode 100644 index 00000000000..64f76eb026d --- /dev/null +++ b/test/spec/modules/districtmDmxBidAdapter_spec.js @@ -0,0 +1,533 @@ +import {expect} from 'chai'; +import * as _ from 'lodash'; +import {spec, matchRequest, checkDeepArray, defaultSize} from '../../../modules/districtmDMXBidAdapter'; + +const bidRequest = [{ + 'bidder': 'districtmDMX', + 'params': { + 'dmxid': 100001, + 'memberid': 100003 + }, + 'adUnitCode': 'div-gpt-ad-12345678-1', + 'transactionId': 'f6d13fa6-ebc1-41ac-9afa-d8171d22d2c2', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '29a28a1bbc8a8d', + 'bidderRequestId': '124b579a136515', + 'auctionId': '3d62f2d3-56a2-4991-888e-f7754619ddcf' +}]; + +const bidderRequest = { + 'bidderCode': 'districtmDMX', + 'auctionId': '3d62f2d3-56a2-4991-888e-f7754619ddcf', + 'bidderRequestId': '124b579a136515', + 'bids': [{ + 'bidder': 'districtmDMX', + 'params': { + 'dmxid': 100001, + 'memberid': 100003 + }, + 'adUnitCode': 'div-gpt-ad-12345678-1', + 'transactionId': 'f6d13fa6-ebc1-41ac-9afa-d8171d22d2c2', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '29a28a1bbc8a8d', + 'bidderRequestId': '124b579a136515', + 'auctionId': '3d62f2d3-56a2-4991-888e-f7754619ddcf' + }], + 'auctionStart': 1529511035677, + 'timeout': 700, + 'gdprConsent': { + 'consentString': 'BOPqNzUOPqNzUAHABBAAA5AAAAAAAA', + 'vendorData': { + 'metadata': 'BOPqNzUOPqNzUAHABBAAA5AAAAAAAA', + 'hasGlobalScope': false, + 'gdprApplies': true, + 'purposeConsents': { + '1': false, + '2': false, + '3': false, + '4': false, + '5': false + }, + 'vendorConsents': { + '1': false, + '2': false, + '3': false, + '4': false, + '6': false, + '7': false, + '8': false, + '9': false, + '10': false, + '11': false, + '12': false, + '13': false, + '14': false, + '15': false, + '16': false, + '17': false, + '18': false, + '19': false, + '20': false, + '21': false, + '22': false, + '23': false, + '24': false, + '25': false, + '26': false, + '27': false, + '28': false, + '29': false, + '30': false, + '31': false, + '32': false, + '33': false, + '34': false, + '35': false, + '36': false, + '37': false, + '38': false, + '39': false, + '40': false, + '41': false, + '42': false, + '43': false, + '44': false, + '45': false, + '46': false, + '47': false, + '48': false, + '49': false, + '50': false, + '51': false, + '52': false, + '53': false, + '55': false, + '56': false, + '57': false, + '58': false, + '59': false, + '60': false, + '61': false, + '62': false, + '63': false, + '64': false, + '65': false, + '66': false, + '67': false, + '68': false, + '69': false, + '70': false, + '71': false, + '72': false, + '73': false, + '74': false, + '75': false, + '76': false, + '77': false, + '78': false, + '79': false, + '80': false, + '81': false, + '82': false, + '83': false, + '84': false, + '85': false, + '86': false, + '87': false, + '88': false, + '89': false, + '90': false, + '91': false, + '92': false, + '93': false, + '94': false, + '95': false, + '97': false, + '98': false, + '100': false, + '101': false, + '102': false, + '104': false, + '105': false, + '108': false, + '109': false, + '110': false, + '111': false, + '112': false, + '113': false, + '114': false, + '115': false, + '119': false, + '120': false, + '122': false, + '124': false, + '125': false, + '126': false, + '127': false, + '128': false, + '129': false, + '130': false, + '131': false, + '132': false, + '133': false, + '134': false, + '136': false, + '138': false, + '139': false, + '140': false, + '141': false, + '142': false, + '143': false, + '144': false, + '145': false, + '147': false, + '148': false, + '149': false, + '150': false, + '151': false, + '152': false, + '153': false, + '154': false, + '155': false, + '156': false, + '157': false, + '158': false, + '159': false, + '160': false, + '161': false, + '162': false, + '163': false, + '164': false, + '165': false, + '167': false, + '168': false, + '169': false, + '170': false, + '171': false, + '173': false, + '174': false, + '175': false, + '177': false, + '178': false, + '179': false, + '180': false, + '182': false, + '183': false, + '184': false, + '185': false, + '188': false, + '189': false, + '190': false, + '191': false, + '192': false, + '193': false, + '194': false, + '195': false, + '197': false, + '198': false, + '199': false, + '200': false, + '201': false, + '202': false, + '203': false, + '205': false, + '206': false, + '208': false, + '209': false, + '210': false, + '211': false, + '212': false, + '213': false, + '214': false, + '215': false, + '216': false, + '217': false, + '218': false, + '223': false, + '224': false, + '225': false, + '226': false, + '227': false, + '228': false, + '229': false, + '230': false, + '231': false, + '232': false, + '234': false, + '235': false, + '236': false, + '237': false, + '238': false, + '239': false, + '240': false, + '241': false, + '242': false, + '244': false, + '245': false, + '246': false, + '248': false, + '249': false, + '250': false, + '251': false, + '252': false, + '253': false, + '254': false, + '255': false, + '256': false, + '257': false, + '258': false, + '259': false, + '260': false, + '261': false, + '262': false, + '263': false, + '264': false, + '265': false, + '266': false, + '269': false, + '270': false, + '272': false, + '273': false, + '274': false, + '275': false, + '276': false, + '277': false, + '278': false, + '279': false, + '280': false, + '281': false, + '282': false, + '284': false, + '285': false, + '288': false, + '289': false, + '290': false, + '291': false, + '294': false, + '295': false, + '297': false, + '299': false, + '301': false, + '302': false, + '303': false, + '304': false, + '308': false, + '309': false, + '310': false, + '311': false, + '312': false, + '314': false, + '315': false, + '316': false, + '317': false, + '318': false, + '319': false, + '320': false, + '323': false, + '325': false, + '326': false, + '328': false, + '329': false, + '330': false, + '331': false, + '333': false, + '337': false, + '339': false, + '341': false, + '343': false, + '344': false, + '345': false, + '347': false, + '349': false, + '350': false, + '351': false, + '354': false, + '358': false, + '359': false, + '360': false, + '361': false, + '368': false, + '369': false, + '371': false, + '373': false, + '376': false, + '377': false, + '378': false, + '380': false, + '382': false, + '384': false, + '385': false, + '387': false, + '388': false, + '389': false, + '390': false, + '391': false, + '398': false, + '400': false, + '402': false, + '403': false, + '404': false, + '413': false, + '415': false, + '421': false, + '422': false + } + }, + 'gdprApplies': true + }, + 'start': 1529511035686, + 'doneCbCallCount': 0 +}; + +const responses = { + 'body': { + 'id': '1f45b37c-5298-4934-b517-4d911aadabfd', + 'cur': 'USD', + 'seatbid': [{ + 'bid': [{ + 'id': '29a28a1bbc8a8d', + 'impid': '29a28a1bbc8a8d', + 'price': '6.42', + 'adm': '
    ' + }] + }] + }, + 'headers': {} +}; + +const responsesNegative = { + 'body': { + 'id': '1f45b37c-5298-4934-b517-4d911aadabfd', + 'cur': 'USD', + 'seatbid': [{ + 'bid': [{ + 'id': '29a28a1bbc8a8d', + 'impid': '29a28a1bbc8a8d', + 'price': '-0.10', + 'adm': '
    ' + }] + }] + }, + 'headers': {} +}; + +const emptyResponse = { body: {} }; +const emptyResponseSeatBid = { body: { seatbid: [] } }; + +describe('DistrictM Adaptor', function () { + const districtm = spec; + describe('All needed functions are available', function () { + it(`isBidRequestValid is present and type function`, function () { + expect(districtm.isBidRequestValid).to.exist.and.to.be.a('function') + }); + + it(`BuildRequests is present and type function`, function () { + expect(districtm.buildRequests).to.exist.and.to.be.a('function') + }); + + it(`interpretResponse is present and type function`, function () { + expect(districtm.interpretResponse).to.exist.and.to.be.a('function') + }); + + it(`getUserSyncs is present and type function`, function () { + expect(districtm.getUserSyncs).to.exist.and.to.be.a('function') + }); + }); + + describe(`these properties are available or not`, function () { + it(`code should have a value of districtmDMX`, function () { + expect(districtm.code).to.be.equal('districtmDMX'); + }); + + it(`timeout should not be defined`, function () { + expect(districtm.onTimeout).to.be.an('undefined'); + }); + }); + + describe(`isBidRequestValid test response`, function () { + let params = { + dmxid: 10001, + memberid: 10003, + }; + it(`function should return true`, function () { + expect(districtm.isBidRequestValid({params})).to.be.equal(true); + }); + it(`function should return false`, function () { + expect(districtm.isBidRequestValid({ params: { memberid: 12345 } })).to.be.equal(false); + }); + it(`expect to have two property available dmxid and memberid`, function () { + expect(params).to.have.property('dmxid'); + expect(params).to.have.property('memberid'); + }); + }); + + describe(`getUserSyncs test usage`, function () { + it(`return value should be an array`, function () { + expect(districtm.getUserSyncs({ iframeEnabled: true })).to.be.an('array'); + }); + it(`array should have only one object and it should have a property type = 'iframe'`, function () { + expect(districtm.getUserSyncs({ iframeEnabled: true }).length).to.be.equal(1); + let [userSync] = districtm.getUserSyncs({ iframeEnabled: true }); + expect(userSync).to.have.property('type'); + expect(userSync.type).to.be.equal('iframe'); + }); + }); + + describe(`buildRequests test usage`, function () { + const buildRequestResults = districtm.buildRequests(bidRequest, bidderRequest); + it(`the function should return an array`, function () { + expect(buildRequestResults).to.be.an('object'); + }); + it(`the function should return array length of 1`, function () { + expect(buildRequestResults.data).to.be.a('string'); + }); + }); + + describe(`interpretResponse test usage`, function () { + const responseResults = districtm.interpretResponse(responses, {bidderRequest}); + const emptyResponseResults = districtm.interpretResponse(emptyResponse, {bidderRequest}); + const emptyResponseResultsNegation = districtm.interpretResponse(responsesNegative, {bidderRequest}); + const emptyResponseResultsEmptySeat = districtm.interpretResponse(emptyResponseSeatBid, {bidderRequest}); + it(`the function should return an array`, function () { + expect(responseResults).to.be.an('array'); + }); + it(`the function should return array length of 1`, function () { + expect(responseResults.length).to.be.equal(1); + }); + it(`the response return nothing`, function () { + expect(emptyResponseResults.length).to.be.equal(0); + }); + it(`the response seatbid return nothing`, function () { + expect(emptyResponseResultsEmptySeat.length).to.be.equal(0); + }); + + it(`on invalid CPM`, function () { + expect(emptyResponseResultsNegation.length).to.be.equal(0); + }); + }); + + describe(`Helper function testing`, function () { + const bid = matchRequest('29a28a1bbc8a8d', {bidderRequest}); + const {width, height} = defaultSize(bid); + it(`test matchRequest`, function () { + expect(matchRequest('29a28a1bbc8a8d', {bidderRequest})).to.be.an('object'); + }); + it(`test checkDeepArray`, function () { + expect(_.isEqual(checkDeepArray([728, 90]), [728, 90])).to.be.equal(true); + expect(_.isEqual(checkDeepArray([[728, 90]]), [728, 90])).to.be.equal(true); + expect(_.isEqual(checkDeepArray([[728, 90], [300, 250]]), [728, 90])).to.be.equal(true); + expect(_.isEqual(checkDeepArray([[300, 250], [300, 250]]), [728, 90])).to.be.equal(false); + expect(_.isEqual(checkDeepArray([300, 250]), [300, 250])).to.be.equal(true); + }); + it(`test defaultSize`, function () { + expect(width).to.be.equal(300); + expect(height).to.be.equal(250); + }); + }); +}); diff --git a/test/spec/modules/djaxBidAdapter_spec.js b/test/spec/modules/djaxBidAdapter_spec.js new file mode 100644 index 00000000000..82955ba43bc --- /dev/null +++ b/test/spec/modules/djaxBidAdapter_spec.js @@ -0,0 +1,159 @@ +import { expect } from 'chai'; +import { spec } from 'modules/djaxBidAdapter'; + +const ENDPOINT = 'https://demo.reviveadservermod.com/headerbidding_adminshare/www/admin/plugins/Prebid/getAd.php'; + +describe('The Djax bidding adapter', function () { + describe('isBidRequestValid', function () { + it('should return false when given an invalid bid', function () { + const bid = { + bidder: 'djax', + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }); + + it('should return true when given a publisherId in bid', function () { + const bid = { + bidder: 'djax', + params: { + publisherId: 2 + }, + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + 'bidder': 'djax', + 'params': { + 'publisherId': 2 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ] + }]; + + const request = spec.buildRequests(bidRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('check endpoint url', function () { + expect(request.url).to.equal(ENDPOINT) + }); + + it('sets the proper banner object', function () { + expect(bidRequests[0].params.publisherId).to.equal(2); + }) + }); + const response = { + body: [ + { + 'requestId': '2ee937f15015c6', + 'cpm': '0.2000', + 'width': 300, + 'height': 600, + 'creativeId': '4', + 'currency': 'USD', + 'netRevenue': true, + 'ad': 'ads.html', + 'mediaType': 'banner' + }, + { + 'requestId': '3e1af92622bdc', + 'cpm': '0.2000', + 'creativeId': '4', + 'context': 'outstream', + 'currency': 'USD', + 'netRevenue': true, + 'vastUrl': 'tezt.xml', + 'width': 640, + 'height': 480, + 'mediaType': 'video' + }] + }; + + const request = [ + { + 'bidder': 'djax', + 'params': { + 'publisherId': 2 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 600] + ] + } + }, + 'bidId': '2ee937f15015c6', + 'src': 'client', + }, + { + 'bidder': 'djax', + 'params': { + 'publisherId': 2 + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'playerSize': [ + [640, 480] + ] + } + }, + 'bidId': '3e1af92622bdc', + 'src': 'client', + } + ]; + + describe('interpretResponse', function () { + it('return empty array when no ad found', function () { + const response = {}; + const request = { bidRequests: [] }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(0); + }); + + it('check response for banner and video', function () { + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(2); + expect(bids[0].requestId).to.equal('2ee937f15015c6'); + expect(bids[0].cpm).to.equal('0.2000'); + expect(bids[1].cpm).to.equal('0.2000'); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(600); + expect(bids[1].vastUrl).to.not.equal(''); + expect(bids[0].ad).to.not.equal(''); + expect(bids[1].adResponse).to.not.equal(''); + expect(bids[1].renderer).not.to.be.an('undefined'); + }); + }); + + describe('On winning bid', function () { + const bids = spec.interpretResponse(response, request); + spec.onBidWon(bids); + }); + + describe('On bid Time out', function () { + const bids = spec.interpretResponse(response, request); + spec.onTimeout(bids); + }); + + describe('user sync', function () { + it('to check the user sync iframe', function () { + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + }); + }); +}); diff --git a/test/spec/modules/dspxBidAdapter_spec.js b/test/spec/modules/dspxBidAdapter_spec.js new file mode 100644 index 00000000000..7eeac43680a --- /dev/null +++ b/test/spec/modules/dspxBidAdapter_spec.js @@ -0,0 +1,130 @@ +import { expect } from 'chai'; +import { spec } from 'modules/dspxBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const ENDPOINT_URL = 'https://buyer.dspx.tv/request/'; + +describe('dspxAdapter', function () { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'dspx', + 'params': { + 'placement': '6682', + 'pfilter': { + 'floorprice': 1000000 + }, + 'bcat': 'IAB2,IAB4', + 'dvt': 'desktop' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'someIncorrectParam': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [{ + 'bidder': 'dspx', + 'params': { + 'placement': '6682', + 'pfilter': { + 'floorprice': 1000000, + 'private_auction': 0, + 'geo': { + 'country': 'DE' + } + }, + 'bcat': 'IAB2,IAB4', + 'dvt': 'desktop' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + + let bidderRequest = { + refererInfo: { + referer: 'some_referrer.net' + } + } + + const request = spec.buildRequests(bidRequests, bidderRequest); + it('sends bid request to our endpoint via GET', function () { + expect(request[0].method).to.equal('GET'); + let data = request[0].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); + expect(data).to.equal('_f=html&alternative=prebid_js&inventory_item_id=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bprivate_auction%5D=0&pfilter%5Bgeo%5D%5Bcountry%5D=DE&bcat=IAB2%2CIAB4&dvt=desktop'); + }); + }); + + describe('interpretResponse', function () { + let serverResponse = { + 'body': { + 'cpm': 5000000, + 'crid': 100500, + 'width': '300', + 'height': '250', + 'tag': '', + 'requestId': '220ed41385952a', + 'currency': 'EUR', + 'ttl': 60, + 'netRevenue': true, + 'zone': '6682' + } + }; + + let expectedResponse = [{ + requestId: '23beaa6af6cdde', + cpm: 0.5, + width: 0, + height: 0, + creativeId: 100500, + dealId: '', + currency: 'EUR', + netRevenue: true, + ttl: 300, + referrer: '', + ad: '' + }]; + + it('should get the correct bid response by display ad', function () { + let bidRequest = [{ + 'method': 'GET', + 'url': ENDPOINT_URL, + 'data': { + 'bid_id': '30b31c1838de1e' + } + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles empty bid response', function () { + let response = { + body: {} + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/ebdrBidAdapter_spec.js b/test/spec/modules/ebdrBidAdapter_spec.js index 3ec5a4f0a81..d742e28f2e6 100644 --- a/test/spec/modules/ebdrBidAdapter_spec.js +++ b/test/spec/modules/ebdrBidAdapter_spec.js @@ -3,10 +3,10 @@ import { spec } from 'modules/ebdrBidAdapter'; import { VIDEO, BANNER } from 'src/mediaTypes'; import * as utils from 'src/utils'; -describe('ebdrBidAdapter', () => { +describe('ebdrBidAdapter', function () { let bidRequests; - beforeEach(() => { + beforeEach(function () { bidRequests = [ { code: 'div-gpt-ad-1460505748561-0', @@ -51,13 +51,13 @@ describe('ebdrBidAdapter', () => { ]; }); - describe('spec.isBidRequestValid', () => { - it('should return true when the required params are passed', () => { + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed', function () { const bidRequest = bidRequests[0]; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return true when the only required param is missing', () => { + it('should return true when the only required param is missing', function () { const bidRequest = bidRequests[0]; bidRequest.params = { zoneid: '99998', @@ -66,7 +66,7 @@ describe('ebdrBidAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return true when the "bidfloor" param is missing', () => { + it('should return true when the "bidfloor" param is missing', function () { const bidRequest = bidRequests[0]; bidRequest.params = { zoneid: '99998', @@ -74,34 +74,34 @@ describe('ebdrBidAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when no bid params are passed', () => { + it('should return false when no bid params are passed', function () { const bidRequest = bidRequests[0]; bidRequest.params = {}; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when a bid request is not passed', () => { + it('should return false when a bid request is not passed', function () { expect(spec.isBidRequestValid()).to.equal(false); expect(spec.isBidRequestValid({})).to.equal(false); }); }); - describe('spec.buildRequests', () => { - describe('for banner bids', () => { - it('must handle an empty bid size', () => { + describe('spec.buildRequests', function () { + describe('for banner bids', function () { + it('must handle an empty bid size', function () { bidRequests[0].mediaTypes = { banner: {} }; const requests = spec.buildRequests(bidRequests); const bidRequest = {}; bidRequest['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: null, h: null }; expect(requests.bids['2c5e8a1a84522d']).to.deep.equals(bidRequest['2c5e8a1a84522d']); }); - it('should create a single GET', () => { + it('should create a single GET', function () { bidRequests[0].mediaTypes = { banner: {} }; bidRequests[1].mediaTypes = { banner: {} }; const requests = spec.buildRequests(bidRequests); expect(requests.method).to.equal('GET'); }); - it('must parse bid size from a nested array', () => { + it('must parse bid size from a nested array', function () { const width = 640; const height = 480; const bidRequest = bidRequests[0]; @@ -112,8 +112,8 @@ describe('ebdrBidAdapter', () => { expect(requests.bids['2c5e8a1a84522d']).to.deep.equal(data['2c5e8a1a84522d']); }); }); - describe('for video bids', () => { - it('must handle an empty bid size', () => { + describe('for video bids', function () { + it('must handle an empty bid size', function () { bidRequests[1].mediaTypes = { video: {} }; const requests = spec.buildRequests(bidRequests); const bidRequest = {}; @@ -121,7 +121,7 @@ describe('ebdrBidAdapter', () => { expect(requests.bids['23a01e95856577']).to.deep.equals(bidRequest['23a01e95856577']); }); - it('should create a GET request for each bid', () => { + it('should create a GET request for each bid', function () { const bidRequest = bidRequests[1]; const requests = spec.buildRequests([ bidRequest ]); expect(requests.method).to.equal('GET'); @@ -129,16 +129,16 @@ describe('ebdrBidAdapter', () => { }); }); - describe('spec.interpretResponse', () => { - describe('for video bids', () => { - it('should return no bids if the response is not valid', () => { + describe('spec.interpretResponse', function () { + describe('for video bids', function () { + it('should return no bids if the response is not valid', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return a valid video bid response', () => { + it('should return a valid video bid response', function () { const ebdrReq = {bids: {}}; bidRequests.forEach(bid => { let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); @@ -165,22 +165,22 @@ describe('ebdrBidAdapter', () => { }); }); - describe('for banner bids', () => { - it('should return no bids if the response is not valid', () => { + describe('for banner bids', function () { + it('should return no bids if the response is not valid', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response is empty', () => { + it('should return no bids if the response is empty', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return valid banner bid responses', () => { + it('should return valid banner bid responses', function () { const ebdrReq = {bids: {}}; bidRequests.forEach(bid => { let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); @@ -206,27 +206,27 @@ describe('ebdrBidAdapter', () => { }); }); }); - describe('spec.getUserSyncs', () => { + describe('spec.getUserSyncs', function () { let syncOptions - beforeEach(() => { + beforeEach(function () { syncOptions = { enabledBidders: ['ebdr'], // only these bidders are allowed to sync pixelEnabled: true } }); - it('sucess with usersync url', () => { + it('sucess with usersync url', function () { const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
    ', adomain: ['advertiserdomain.com'], iurl: '//match.bnmla.com/usersync?sspid=59&redir=', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; const result = []; result.push({type: 'image', url: '//match.bnmla.com/usersync?sspid=59&redir='}); expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); }); - it('sucess without usersync url', () => { + it('sucess without usersync url', function () { const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
    ', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; const result = []; expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); }); - it('empty response', () => { + it('empty response', function () { const serverResponse = {}; const result = []; expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); diff --git a/test/spec/modules/emoteevBidAdapter_spec.js b/test/spec/modules/emoteevBidAdapter_spec.js new file mode 100644 index 00000000000..b6a62c16963 --- /dev/null +++ b/test/spec/modules/emoteevBidAdapter_spec.js @@ -0,0 +1,868 @@ +import { + assert, expect +} from 'chai'; +import { + ADAPTER_VERSION, + DOMAIN, + DOMAIN_DEVELOPMENT, + DOMAIN_STAGING, + domain, + BIDDER_PATH, + bidderUrl, + buildRequests, + conformBidRequest, + DEFAULT_ENV, + DEVELOPMENT, + EVENTS_PATH, + eventsUrl, + FOOTER, + gdprConsent, + getDeviceDimensions, + getDeviceInfo, + getDocumentDimensions, + getUserSyncs, + getViewDimensions, + IN_CONTENT, + interpretResponse, + isBidRequestValid, + ON_ADAPTER_CALLED, + ON_BID_WON, + ON_BIDDER_TIMEOUT, + onBidWon, + onAdapterCalled, + onTimeout, + OVERLAY, + PRODUCTION, + requestsPayload, + resolveDebug, + resolveEnv, + spec, + STAGING, + USER_SYNC_IFRAME_PATH, + USER_SYNC_IMAGE_PATH, + userSyncIframeUrl, + userSyncImageUrl, + validateSizes, + validateContext, + validateExternalId, + VENDOR_ID, + WALLPAPER, +} from 'modules/emoteevBidAdapter'; +import * as url from '../../../src/url'; +import * as utils from '../../../src/utils'; +import {config} from '../../../src/config'; + +const cannedValidBidRequests = [{ + adUnitCode: '/19968336/header-bid-tag-1', + auctionId: 'fcbf2b27-a951-496f-b5bb-1324ce7c0558', + bidId: '2b8de6572e8193', + bidRequestsCount: 1, + bidder: 'emoteev', + bidderRequestId: '1203b39fecc6a5', + crumbs: {pubcid: 'f3371d16-4e8b-42b5-a770-7e5be1fdf03d'}, + params: { + adSpaceId: 5084, + context: IN_CONTENT, + externalId: 42 + }, + sizes: [[300, 250], [250, 300], [300, 600]], + transactionId: '58dbd732-7a39-45f1-b23e-1c24051a941c', +}]; +const cannedBidderRequest = { + auctionId: 'fcbf2b27-a951-496f-b5bb-1324ce7c0558', + auctionStart: 1544200122837, + bidderCode: 'emoteev', + bidderRequestId: '1203b39fecc6a5', + doneCbCallCount: 0, + refererInfo: { + canonicalUrl: undefined, + numIframes: 0, + reachedTop: true, + referer: 'http://localhost:9999/integrationExamples/gpt/hello_world_emoteev.html', + stack: ['http://localhost:9999/integrationExamples/gpt/hello_world_emoteev.html'] + }, + start: 1544200012839, + timeout: 3000, + gdprConsent: { + gdprApplies: true, + vendorData: {vendorConsents: {[VENDOR_ID]: true}}, + } +}; +const serverResponse = + { + body: [ + { + requestId: cannedValidBidRequests[0].bidId, + cpm: 1, + width: cannedValidBidRequests[0].sizes[0][0], + height: cannedValidBidRequests[0].sizes[0][1], + ad: '
    ', + ttl: 360, + creativeId: 123, + netRevenue: false, + currency: 'EUR', + } + ] + }; + +describe('emoteevBidAdapter', function () { + describe('isBidRequestValid', function () { + it('should return true when valid', function () { + const validBid = { + bidder: 'emoteev', + bidId: '23a45b4e3', + params: { + adSpaceId: 12345, + context: IN_CONTENT, + externalId: 42 + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + }; + expect(isBidRequestValid(validBid)).to.equal(true); + + expect(spec.isBidRequestValid(validBid)).to.exist.and.to.be.a('boolean'); + expect(spec.isBidRequestValid({})).to.exist.and.to.be.a('boolean'); + }); + + it('should return false when required params are invalid', function () { + expect(isBidRequestValid({ + bidder: '', // invalid bidder + params: { + adSpaceId: 12345, + context: IN_CONTENT, + externalId: 42 + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + })).to.equal(false); + expect(isBidRequestValid({ + bidder: 'emoteev', + params: { + adSpaceId: '', // invalid adSpaceId + context: IN_CONTENT, + externalId: 42 + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + })).to.equal(false); + expect(isBidRequestValid({ + bidder: 'emoteev', + params: { + adSpaceId: 12345, + context: 'something', // invalid context + externalId: 42 + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + })).to.equal(false); + expect(isBidRequestValid({ + bidder: 'emoteev', + params: { + adSpaceId: 12345, + context: IN_CONTENT, + externalId: 'lol' // invalid externalId + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + })).to.equal(false); + expect(isBidRequestValid({ + bidder: 'emoteev', + params: { + adSpaceId: 12345, + context: IN_CONTENT, + externalId: 42 + }, + mediaTypes: { + banner: { + sizes: [[750]] // invalid size + } + }, + })).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const + env = DEFAULT_ENV, + debug = true, + currency = 'EUR', + request = buildRequests(env, debug, currency, cannedValidBidRequests, cannedBidderRequest); + + expect(request).to.exist.and.have.all.keys( + 'method', + 'url', + 'data', + ); + + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(bidderUrl(env)); + + expect(spec.buildRequests(cannedValidBidRequests, cannedBidderRequest)).to.exist.and.to.be.an('object'); + }); + + describe('interpretResponse', function () { + it('bid objects from response', function () { + const bidResponses = interpretResponse(serverResponse); + expect(bidResponses).to.be.an('array').that.is.not.empty; + expect(bidResponses[0]).to.have.property('requestId', cannedValidBidRequests[0].bidId); + expect(bidResponses[0]).to.have.property('cpm', serverResponse.body[0].cpm); + expect(bidResponses[0]).to.have.property('width', serverResponse.body[0].width); + expect(bidResponses[0]).to.have.property('height', serverResponse.body[0].height); + expect(bidResponses[0]).to.have.property('ad', serverResponse.body[0].ad); + expect(bidResponses[0]).to.have.property('ttl', serverResponse.body[0].ttl); + expect(bidResponses[0]).to.have.property('creativeId', serverResponse.body[0].creativeId); + expect(bidResponses[0]).to.have.property('netRevenue', serverResponse.body[0].netRevenue); + expect(bidResponses[0]).to.have.property('currency', serverResponse.body[0].currency); + }); + }); + + describe('onAdapterCalled', function () { + const + bidRequest = cannedValidBidRequests[0], + url = onAdapterCalled(DEFAULT_ENV, bidRequest); + + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_ADAPTER_CALLED); + expect(url).to.have.nested.property('search.pubcId', bidRequest.crumbs.pubcid); + expect(url).to.have.nested.property('search.bidId', bidRequest.bidId); + expect(url).to.have.nested.property('search.adSpaceId', bidRequest.params.adSpaceId); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('onBidWon', function () { + const + pubcId = cannedValidBidRequests[0].crumbs.pubcid, + bidObject = serverResponse.body[0], + url = onBidWon(DEFAULT_ENV, pubcId, bidObject); + + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_BID_WON); + expect(url).to.have.nested.property('search.pubcId', pubcId); + expect(url).to.have.nested.property('search.bidId', bidObject.requestId); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('onTimeout', function () { + const + data = { + ...cannedValidBidRequests[0], + timeout: 123, + }, + url = onTimeout(DEFAULT_ENV, data); + + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_BIDDER_TIMEOUT); + expect(url).to.have.nested.property('search.bidId', data.bidId); + expect(url).to.have.nested.property('search.pubcId', data.crumbs.pubcid); + expect(url).to.have.nested.property('search.adSpaceId', data.params.adSpaceId); + expect(url).to.have.nested.property('search.timeout', data.timeout); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('getUserSyncs', function () { + expect(getUserSyncs( + DEFAULT_ENV, + { + iframeEnabled: false, + pixelEnabled: false + })).to.deep.equal([]); + expect(getUserSyncs( + PRODUCTION, + { + iframeEnabled: false, + pixelEnabled: true + })).to.deep.equal([{ + type: 'image', + url: userSyncImageUrl(PRODUCTION) + }]); + expect(getUserSyncs( + STAGING, + { + iframeEnabled: true, + pixelEnabled: false + })).to.deep.equal([{ + type: 'iframe', + url: userSyncIframeUrl(STAGING) + }]); + expect(getUserSyncs( + DEVELOPMENT, + { + iframeEnabled: true, + pixelEnabled: true + })).to.deep.equal([{ + type: 'image', + url: userSyncImageUrl(DEVELOPMENT) + }, { + type: 'iframe', + url: userSyncIframeUrl(DEVELOPMENT) + }]); + }); + + describe('domain', function () { + expect(domain(null)).to.deep.equal(DOMAIN); + expect(domain('anything')).to.deep.equal(DOMAIN); + expect(domain(PRODUCTION)).to.deep.equal(DOMAIN); + expect(domain(STAGING)).to.deep.equal(DOMAIN_STAGING); + expect(domain(DEVELOPMENT)).to.deep.equal(DOMAIN_DEVELOPMENT); + }); + + describe('eventsUrl', function () { + expect(eventsUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: EVENTS_PATH + })); + expect(eventsUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: EVENTS_PATH + })); + expect(eventsUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: EVENTS_PATH + })); + expect(eventsUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: EVENTS_PATH + })); + expect(eventsUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: EVENTS_PATH + })); + }); + + describe('bidderUrl', function () { + expect(bidderUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: BIDDER_PATH + })); + expect(bidderUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: BIDDER_PATH + })); + expect(bidderUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: BIDDER_PATH + })); + expect(bidderUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: BIDDER_PATH + })); + expect(bidderUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: BIDDER_PATH + })); + }); + + describe('userSyncIframeUrl', function () { + expect(userSyncIframeUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: USER_SYNC_IFRAME_PATH + })); + }); + + describe('userSyncImageUrl', function () { + expect(userSyncImageUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: USER_SYNC_IMAGE_PATH + })); + }); + + describe('conformBidRequest', function () { + expect(conformBidRequest(cannedValidBidRequests[0])).to.deep.equal({ + params: cannedValidBidRequests[0].params, + crumbs: cannedValidBidRequests[0].crumbs, + sizes: cannedValidBidRequests[0].sizes, + bidId: cannedValidBidRequests[0].bidId, + bidderRequestId: cannedValidBidRequests[0].bidderRequestId, + }); + }); + + describe('gdprConsent', function () { + describe('gdpr applies, consent given', function () { + const bidderRequest = { + ...cannedBidderRequest, + gdprConsent: { + gdprApplies: true, + vendorData: {vendorConsents: {[VENDOR_ID]: true}}, + } + }; + expect(gdprConsent(bidderRequest)).to.deep.equal(true); + }); + describe('gdpr applies, consent withdrawn', function () { + const bidderRequest = { + ...cannedBidderRequest, + gdprConsent: { + gdprApplies: true, + vendorData: {vendorConsents: {[VENDOR_ID]: false}}, + } + }; + expect(gdprConsent(bidderRequest)).to.deep.equal(false); + }); + describe('gdpr applies, consent unknown', function () { + const bidderRequest = { + ...cannedBidderRequest, + gdprConsent: { + gdprApplies: true, + vendorData: {}, + } + }; + expect(gdprConsent(bidderRequest)).to.deep.equal(undefined); + }); + }); + + describe('requestsPayload', function () { + const + currency = 'EUR', + debug = true; + + const payload = requestsPayload(debug, currency, cannedValidBidRequests, cannedBidderRequest); + + expect(payload).to.exist.and.have.all.keys( + 'akPbjsVersion', + 'bidRequests', + 'currency', + 'debug', + 'language', + 'refererInfo', + 'deviceInfo', + 'userAgent', + 'gdprApplies', + 'gdprConsent', + ); + + expect(payload.bidRequests[0]).to.exist.and.have.all.keys( + 'params', + 'crumbs', + 'sizes', + 'bidId', + 'bidderRequestId', + ); + + expect(payload.akPbjsVersion).to.deep.equal(ADAPTER_VERSION); + expect(payload.bidRequests[0].params).to.deep.equal(cannedValidBidRequests[0].params); + expect(payload.bidRequests[0].crumbs).to.deep.equal(cannedValidBidRequests[0].crumbs); + expect(payload.bidRequests[0].mediaTypes).to.deep.equal(cannedValidBidRequests[0].mediaTypes); + expect(payload.bidRequests[0].bidId).to.deep.equal(cannedValidBidRequests[0].bidId); + expect(payload.bidRequests[0].bidderRequestId).to.deep.equal(cannedValidBidRequests[0].bidderRequestId); + expect(payload.currency).to.deep.equal(currency); + expect(payload.debug).to.deep.equal(debug); + expect(payload.language).to.deep.equal(navigator.language); + expect(payload.deviceInfo).to.exist.and.have.all.keys( + 'browserWidth', + 'browserHeight', + 'deviceWidth', + 'deviceHeight', + 'documentWidth', + 'documentHeight', + 'webGL', + ); + expect(payload.userAgent).to.deep.equal(navigator.userAgent); + expect(payload.gdprApplies).to.deep.equal(cannedBidderRequest.gdprConsent.gdprApplies); + }); + + describe('getViewDimensions', function () { + const window = { + innerWidth: 1024, + innerHeight: 768 + }; + const documentWithElement = { + documentElement: + { + clientWidth: 512, + clientHeight: 384 + } + }; + const documentWithBody = { + body: + { + clientWidth: 512, + clientHeight: 384 + } + }; + expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ + width: 1024, + height: 768 + }); + expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); + expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ + width: 1024, + height: 768 + }); + expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); + expect(getViewDimensions({}, documentWithElement)).to.deep.equal({width: 512, height: 384}); + expect(getViewDimensions({}, documentWithBody)).to.deep.equal({width: 512, height: 384}); + }); + + describe('getDeviceDimensions', function () { + const window = {screen: {width: 1024, height: 768}}; + expect(getDeviceDimensions(window)).to.deep.equal({width: 1024, height: 768}); + expect(getDeviceDimensions({})).to.deep.equal({width: '', height: ''}); + }); + + describe('getDocumentDimensions', function () { + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 1, + clientHeight: 1, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 0, + scrollHeight: 0, + }, + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 1, + clientHeight: 1, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 0, + scrollHeight: 0, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 0, + clientHeight: 0, + offsetWidth: 1, + offsetHeight: 1, + scrollWidth: 0, + scrollHeight: 0, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 0, + clientHeight: 0, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 1, + scrollHeight: 1, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: undefined, + clientHeight: undefined, + offsetWidth: undefined, + offsetHeight: undefined, + scrollWidth: undefined, + scrollHeight: undefined, + }, + body: { + scrollHeight: undefined, + offsetHeight: undefined, + } + })).to.deep.equal({width: '', height: ''}); + }); + + // describe('isWebGLEnabled', function () { + // it('handles no webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').returns(undefined); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles webgl exception', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').throws(DOMException); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles experimental webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').returns(true); + // expect(isWebGLEnabled(document)).to.equal(true); + // }); + // + // it('handles experimental webgl exception', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').throws(DOMException); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(true); + // expect(isWebGLEnabled(document)).to.equal(true); + // }); + // }); + + describe('getDeviceInfo', function () { + expect(getDeviceInfo( + {width: 1, height: 2}, + {width: 3, height: 4}, + {width: 5, height: 6}, + true + )).to.deep.equal({ + deviceWidth: 1, + deviceHeight: 2, + browserWidth: 3, + browserHeight: 4, + documentWidth: 5, + documentHeight: 6, + webGL: true + }); + }); + + describe('resolveEnv', function () { + it('defaults to production', function () { + expect(resolveEnv({}, null)).to.deep.equal(DEFAULT_ENV); + }); + expect(resolveEnv({}, PRODUCTION)).to.deep.equal(PRODUCTION); + expect(resolveEnv({}, STAGING)).to.deep.equal(STAGING); + expect(resolveEnv({}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); + expect(resolveEnv({emoteev: {env: PRODUCTION}}, null)).to.deep.equal(PRODUCTION); + expect(resolveEnv({emoteev: {env: STAGING}}, null)).to.deep.equal(STAGING); + expect(resolveEnv({emoteev: {env: DEVELOPMENT}}, null)).to.deep.equal(DEVELOPMENT); + it('prioritizes parameter over configuration', function () { + expect(resolveEnv({emoteev: {env: STAGING}}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); + }); + }); + + describe('resolveDebug', function () { + it('defaults to production', function () { + expect(resolveDebug({}, null)).to.deep.equal(false); + }); + expect(resolveDebug({}, 'false')).to.deep.equal(false); + expect(resolveDebug({}, 'true')).to.deep.equal(true); + expect(resolveDebug({debug: true}, null)).to.deep.equal(true); + it('prioritizes parameter over configuration', function () { + expect(resolveDebug({debug: true}, 'false')).to.deep.equal(false); + }); + }); + + describe('side effects', function () { + let triggerPixelSpy; + let getCookieSpy; + let getConfigSpy; + let getParameterByNameSpy; + beforeEach(function () { + triggerPixelSpy = sinon.spy(utils, 'triggerPixel'); + getCookieSpy = sinon.spy(utils, 'getCookie'); + getConfigSpy = sinon.spy(config, 'getConfig'); + getParameterByNameSpy = sinon.spy(utils, 'getParameterByName'); + }); + afterEach(function () { + triggerPixelSpy.restore(); + getCookieSpy.restore(); + getConfigSpy.restore(); + getParameterByNameSpy.restore(); + }); + + describe('isBidRequestValid', function () { + it('has intended side-effects', function () { + const validBidRequest = { + bidder: 'emoteev', + bidId: '23a45b4e3', + params: { + adSpaceId: 12345, + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + }; + spec.isBidRequestValid(validBidRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + }); + describe('isBidRequestValid empty request', function() { + it('has intended side-effects empty request', function () { + const invalidBidRequest = {}; + spec.isBidRequestValid(invalidBidRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + }); + describe('buildRequests', function () { + it('has intended side-effects', function () { + spec.buildRequests(cannedValidBidRequests, cannedBidderRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(utils.getCookie); + sinon.assert.callCount(config.getConfig, 3); + sinon.assert.callCount(utils.getParameterByName, 2); + }); + }); + describe('interpretResponse', function () { + it('has intended side-effects', function () { + spec.interpretResponse(serverResponse); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + }); + describe('onBidWon', function () { + it('has intended side-effects', function () { + const bidObject = serverResponse.body[0]; + spec.onBidWon(bidObject); + sinon.assert.calledOnce(utils.triggerPixel); + sinon.assert.calledOnce(utils.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + describe('onTimeout', function () { + it('has intended side-effects', function () { + spec.onTimeout(cannedValidBidRequests[0]); + sinon.assert.calledOnce(utils.triggerPixel); + sinon.assert.notCalled(utils.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + describe('getUserSyncs', function () { + it('has intended side-effects', function () { + spec.getUserSyncs({}); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(utils.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + }); + + describe('validateSizes', function () { + it('only accepts valid array of sizes', function () { + expect(validateSizes([])).to.deep.equal(false); + expect(validateSizes([[]])).to.deep.equal(false); + expect(validateSizes([[450, 450], undefined])).to.deep.equal(false); + expect(validateSizes([[450, 450], 'size'])).to.deep.equal(false); + expect(validateSizes([[1, 1]])).to.deep.equal(true); + expect(validateSizes([[1, 1], [450, 450]])).to.deep.equal(true); + }); + }); + + describe('validateContext', function () { + it('only accepts valid context', function () { + expect(validateContext(IN_CONTENT)).to.deep.equal(true); + expect(validateContext(FOOTER)).to.deep.equal(true); + expect(validateContext(OVERLAY)).to.deep.equal(true); + expect(validateContext(WALLPAPER)).to.deep.equal(true); + expect(validateContext(null)).to.deep.equal(false); + expect(validateContext('anything else')).to.deep.equal(false); + }); + }); + + describe('validateExternalId', function () { + it('only accepts a positive integer or null', function () { + expect(validateExternalId(0)).to.deep.equal(false); + expect(validateExternalId(42)).to.deep.equal(true); + expect(validateExternalId(42.0)).to.deep.equal(true); // edge case: valid externalId + expect(validateExternalId(3.14159)).to.deep.equal(false); + expect(validateExternalId('externalId')).to.deep.equal(false); + expect(validateExternalId(undefined)).to.deep.equal(true); + expect(validateExternalId(null)).to.deep.equal(true); + }); + }); +}); diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js new file mode 100644 index 00000000000..16f17174f88 --- /dev/null +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -0,0 +1,541 @@ +import { expect } from 'chai'; +import { spec } from 'modules/emx_digitalBidAdapter'; +import * as utils from 'src/utils'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('emx_digital Adapter', function () { + describe('callBids', function () { + const adapter = newBidder(spec); + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + describe('banner request validity', function () { + let bid = { + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + let badBid = { + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251' + }, + 'mediaTypes': { + 'banner': { + } + }, + 'adUnitCode': 'adunit-code', + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + let noBid = {}; + let otherBid = { + 'bidder': 'emxdigital', + 'params': { + 'tagid': '25251' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + let noMediaSizeBid = { + 'bidder': 'emxdigital', + 'params': { + 'tagid': '25251' + }, + 'mediaTypes': { + 'banner': {} + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(spec.isBidRequestValid(badBid)).to.equal(false); + expect(spec.isBidRequestValid(noBid)).to.equal(false); + expect(spec.isBidRequestValid(otherBid)).to.equal(false); + expect(spec.isBidRequestValid(noMediaSizeBid)).to.equal(false); + }); + }); + + describe('video request validity', function () { + let bid = { + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251', + 'video': {} + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [640, 480] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + let noInstreamBid = { + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251', + 'video': { + 'protocols': [1, 7] + } + }, + 'mediaTypes': { + 'video': { + 'context': 'something_random' + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + + let outstreamBid = { + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251', + 'video': {} + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'playerSize': [640, 480] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(spec.isBidRequestValid(noInstreamBid)).to.equal(false); + expect(spec.isBidRequestValid(outstreamBid)).to.equal(true); + }); + + it('should contain tagid param', function () { + expect(spec.isBidRequestValid({ + bidder: 'emx_digital', + params: {}, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + bidder: 'emx_digital', + params: { + tagid: '' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + bidder: 'emx_digital', + params: { + tagid: '123' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + })).to.equal(true); + }); + }); + }); + + describe('buildRequests', function () { + let bidderRequest = { + 'bidderCode': 'emx_digital', + 'auctionId': 'e19f1eff-8b27-42a6-888d-9674e5a6130c', + 'bidderRequestId': '22edbae3120bf6', + 'timeout': 1500, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'https://example.com/index.html?pbjs_debug=true' + }, + 'bids': [{ + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251' + }, + 'adUnitCode': 'adunit-code', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250], + [300, 600] + ] + } + }, + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'auctionId': 'e19f1eff-8b27-42a6-888d-9674e5a6130c', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + }] + }; + let request = spec.buildRequests(bidderRequest.bids, bidderRequest); + + it('sends bid request to ENDPOINT via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('contains the correct options', function () { + expect(request.options.withCredentials).to.equal(true); + }); + + it('contains a properly formatted endpoint url', function () { + const url = request.url.split('?'); + const queryParams = url[1].split('&'); + expect(queryParams[0]).to.match(new RegExp('^t=\d*', 'g')); + expect(queryParams[1]).to.match(new RegExp('^ts=\d*', 'g')); + }); + + it('builds with bid floor', function () { + const bidRequestWithBidFloor = utils.deepClone(bidderRequest.bids); + bidRequestWithBidFloor[0].params.bidfloor = 1; + const requestWithFloor = spec.buildRequests(bidRequestWithBidFloor, bidderRequest); + const data = JSON.parse(requestWithFloor.data); + expect(data.imp[0].bidfloor).to.equal(bidRequestWithBidFloor[0].params.bidfloor); + }); + + it('builds request properly', function () { + const data = JSON.parse(request.data); + expect(Array.isArray(data.imp)).to.equal(true); + expect(data.id).to.equal(bidderRequest.auctionId); + expect(data.imp.length).to.equal(1); + expect(data.imp[0].id).to.equal('30b31c2501de1e'); + expect(data.imp[0].tid).to.equal('d7b773de-ceaa-484d-89ca-d9f51b8d61ec'); + expect(data.imp[0].tagid).to.equal('25251'); + expect(data.imp[0].secure).to.equal(0); + expect(data.imp[0].vastXml).to.equal(undefined); + }); + + it('properly sends site information and protocol', function () { + request = spec.buildRequests(bidderRequest.bids, bidderRequest); + request = JSON.parse(request.data); + expect(request.site.domain).to.equal(utils.getTopWindowLocation().hostname); + expect(decodeURIComponent(request.site.page)).to.equal(bidderRequest.refererInfo.referer); + expect(request.site.ref).to.equal(window.top.document.referrer); + }); + + it('builds correctly formatted request banner object', function () { + let bidRequestWithBanner = utils.deepClone(bidderRequest.bids); + let request = spec.buildRequests(bidRequestWithBanner, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].video).to.equal(undefined); + expect(data.imp[0].banner).to.exist.and.to.be.a('object'); + expect(data.imp[0].banner.w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][0]); + expect(data.imp[0].banner.h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][1]); + expect(data.imp[0].banner.format[0].w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][0]); + expect(data.imp[0].banner.format[0].h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][1]); + expect(data.imp[0].banner.format[1].w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[1][0]); + expect(data.imp[0].banner.format[1].h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[1][1]); + }); + + it('builds correctly formatted request video object for instream', function () { + let bidRequestWithVideo = utils.deepClone(bidderRequest.bids); + bidRequestWithVideo[0].mediaTypes = { + video: { + context: 'instream', + playerSize: [[640, 480]] + }, + }; + bidRequestWithVideo[0].params.video = {}; + let request = spec.buildRequests(bidRequestWithVideo, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist.and.to.be.a('object'); + expect(data.imp[0].video.w).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][0]); + expect(data.imp[0].video.h).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][1]); + }); + + it('builds correctly formatted request video object for outstream', function () { + let bidRequestWithOutstreamVideo = utils.deepClone(bidderRequest.bids); + bidRequestWithOutstreamVideo[0].mediaTypes = { + video: { + context: 'outstream', + playerSize: [[640, 480]] + }, + }; + bidRequestWithOutstreamVideo[0].params.video = {}; + let request = spec.buildRequests(bidRequestWithOutstreamVideo, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist.and.to.be.a('object'); + expect(data.imp[0].video.w).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][0]); + expect(data.imp[0].video.h).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][1]); + }); + + it('shouldn\'t contain a user obj without GDPR information', function () { + let request = spec.buildRequests(bidderRequest.bids, bidderRequest) + request = JSON.parse(request.data) + expect(request).to.not.have.property('user'); + }); + + it('should have the right gdpr info when enabled', function () { + let consentString = 'OIJSZsOAFsABAB8EMXZZZZZ+A=='; + bidderRequest.gdprConsent = { + 'consentString': consentString, + 'gdprApplies': true + }; + let request = spec.buildRequests(bidderRequest.bids, bidderRequest); + + request = JSON.parse(request.data) + expect(request.regs.ext).to.have.property('gdpr', 1); + expect(request.user.ext).to.have.property('consent', consentString); + }); + + it('should\'t contain consent string if gdpr isn\'t applied', function () { + bidderRequest.gdprConsent = { + 'gdprApplies': false + }; + let request = spec.buildRequests(bidderRequest.bids, bidderRequest); + request = JSON.parse(request.data) + expect(request.regs.ext).to.have.property('gdpr', 0); + expect(request).to.not.have.property('user'); + }); + }); + + describe('interpretResponse', function () { + let bid = { + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251', + 'video': {} + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [640, 480] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + + const serverResponse = { + 'id': '12819a18-56e1-4256-b836-b69a10202668', + 'seatbid': [{ + 'bid': [{ + 'adid': '123456abcde', + 'adm': '', + 'crid': '3434abab34', + 'h': 250, + 'id': '987654321cba', + 'price': 0.5, + 'ttl': 300, + 'w': 300 + }], + 'seat': '1356' + }, { + 'bid': [{ + 'adid': '123456abcdf', + 'adm': '', + 'crid': '3434abab35', + 'h': 600, + 'id': '987654321cba', + 'price': 0.5, + 'ttl': 300, + 'w': 300 + }] + }] + }; + + const expectedResponse = [{ + 'requestId': '12819a18-56e1-4256-b836-b69a10202668', + 'cpm': 0.5, + 'width': 300, + 'height': 250, + 'creativeId': '3434abab34', + 'dealId': null, + 'currency': 'USD', + 'netRevneue': true, + 'mediaType': 'banner', + 'ad': '', + 'ttl': 300 + }, { + 'requestId': '12819a18-56e1-4256-b836-b69a10202668', + 'cpm': 0.7, + 'width': 300, + 'height': 600, + 'creativeId': '3434abab35', + 'dealId': null, + 'currency': 'USD', + 'netRevneue': true, + 'mediaType': 'banner', + 'ad': '', + 'ttl': 300 + }]; + + it('should properly format bid response', function () { + let result = spec.interpretResponse({ + body: serverResponse + }); + expect(Object.keys(result[0]).length).to.equal(Object.keys(expectedResponse[0]).length); + expect(Object.keys(result[0]).requestId).to.equal(Object.keys(expectedResponse[0]).requestId); + expect(Object.keys(result[0]).bidderCode).to.equal(Object.keys(expectedResponse[0]).bidderCode); + expect(Object.keys(result[0]).cpm).to.equal(Object.keys(expectedResponse[0]).cpm); + expect(Object.keys(result[0]).creativeId).to.equal(Object.keys(expectedResponse[0]).creativeId); + expect(Object.keys(result[0]).width).to.equal(Object.keys(expectedResponse[0]).width); + expect(Object.keys(result[0]).height).to.equal(Object.keys(expectedResponse[0]).height); + expect(Object.keys(result[0]).ttl).to.equal(Object.keys(expectedResponse[0]).ttl); + expect(Object.keys(result[0]).adId).to.equal(Object.keys(expectedResponse[0]).adId); + expect(Object.keys(result[0]).currency).to.equal(Object.keys(expectedResponse[0]).currency); + expect(Object.keys(result[0]).netRevenue).to.equal(Object.keys(expectedResponse[0]).netRevenue); + expect(Object.keys(result[0]).ad).to.equal(Object.keys(expectedResponse[0]).ad); + }); + + it('should return multiple bids', function () { + let result = spec.interpretResponse({ + body: serverResponse + }); + expect(Array.isArray(result.seatbid)) + + const ad0 = result[0]; + const ad1 = result[1]; + expect(ad0.ad).to.equal(serverResponse.seatbid[0].bid[0].adm); + expect(ad0.cpm).to.equal(serverResponse.seatbid[0].bid[0].price); + expect(ad0.creativeId).to.equal(serverResponse.seatbid[0].bid[0].crid); + expect(ad0.currency).to.equal('USD'); + expect(ad0.netRevenue).to.equal(true); + expect(ad0.requestId).to.equal(serverResponse.seatbid[0].bid[0].id); + expect(ad0.ttl).to.equal(300); + + expect(ad1.ad).to.equal(serverResponse.seatbid[1].bid[0].adm); + expect(ad1.cpm).to.equal(serverResponse.seatbid[1].bid[0].price); + expect(ad1.creativeId).to.equal(serverResponse.seatbid[1].bid[0].crid); + expect(ad1.currency).to.equal('USD'); + expect(ad1.netRevenue).to.equal(true); + expect(ad1.requestId).to.equal(serverResponse.seatbid[1].bid[0].id); + expect(ad1.ttl).to.equal(300); + }); + + it('returns a banner bid for non-xml creatives', function () { + let result = spec.interpretResponse({ + body: serverResponse + }, { bidRequest: bid } + ); + const ad0 = result[0]; + const ad1 = result[1]; + expect(ad0.mediaType).to.equal('banner'); + expect(ad0.ad.indexOf('
    '; + serverResponse.seatbid[1].bid[0].adm = '
    '; + + let result = spec.interpretResponse({ + body: serverResponse + }, { bidRequest: bid } + ); + const ad0 = result[0]; + const ad1 = result[1]; + expect(ad0.mediaType).to.equal('video'); + expect(ad0.ad.indexOf(' -1).to.equal(true); + expect(ad0.vastXml).to.equal(serverResponse.seatbid[0].bid[0].adm); + expect(ad0.ad).to.exist.and.to.be.a('string'); + expect(ad1.mediaType).to.equal('video'); + expect(ad1.ad.indexOf(' -1).to.equal(true); + expect(ad1.vastXml).to.equal(serverResponse.seatbid[1].bid[0].adm); + expect(ad1.ad).to.exist.and.to.be.a('string'); + }); + + it('handles nobid responses', function () { + let serverResponse = { + 'bids': [] + }; + + let result = spec.interpretResponse({ + body: serverResponse + }); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + let syncOptionsIframe = { iframeEnabled: true }; + let syncOptionsPixel = { pixelEnabled: true }; + it('Should push the correct sync type depending on the config', function () { + let iframeSync = spec.getUserSyncs(syncOptionsIframe); + expect(iframeSync.length).to.equal(1); + expect(iframeSync[0].type).to.equal('iframe'); + }); + }); +}); diff --git a/test/spec/modules/eplanningAnalyticsAdapter_spec.js b/test/spec/modules/eplanningAnalyticsAdapter_spec.js index cd538815954..8609024c7d9 100644 --- a/test/spec/modules/eplanningAnalyticsAdapter_spec.js +++ b/test/spec/modules/eplanningAnalyticsAdapter_spec.js @@ -2,29 +2,29 @@ import eplAnalyticsAdapter from 'modules/eplanningAnalyticsAdapter'; import includes from 'core-js/library/fn/array/includes'; import { expect } from 'chai'; import {parse as parseURL} from 'src/url'; -let adaptermanager = require('src/adaptermanager'); +let adapterManager = require('src/adapterManager').default; let events = require('src/events'); let constants = require('src/constants.json'); -describe('eplanning analytics adapter', () => { +describe('eplanning analytics adapter', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = request => { requests.push(request) }; sinon.stub(events, 'getEvents').returns([]); }); - afterEach(() => { + afterEach(function () { xhr.restore(); events.getEvents.restore(); eplAnalyticsAdapter.disableAnalytics(); }); - describe('track', () => { - it('builds and sends auction data', () => { + describe('track', function () { + it('builds and sends auction data', function () { sinon.spy(eplAnalyticsAdapter, 'track'); let auctionTimestamp = 1496510254313; @@ -75,12 +75,12 @@ describe('eplanning analytics adapter', () => { } ]; - adaptermanager.registerAnalyticsAdapter({ + adapterManager.registerAnalyticsAdapter({ code: 'eplanning', adapter: eplAnalyticsAdapter }); - adaptermanager.enableAnalytics({ + adapterManager.enableAnalytics({ provider: 'eplanning', options: initOptions }); diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index 68b9e1b263f..93290229ce3 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -3,13 +3,13 @@ import { spec } from 'modules/eplanningBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; import * as utils from 'src/utils'; -describe('E-Planning Adapter', () => { +describe('E-Planning Adapter', function () { const adapter = newBidder('spec'); const CI = '12345'; - const ADUNIT_CODE = 'adunit-code'; + const ADUNIT_CODE = 'adunit-co:de'; const ADUNIT_CODE2 = 'adunit-code-dos'; const CLEAN_ADUNIT_CODE2 = 'adunitcodedos'; - const CLEAN_ADUNIT_CODE = 'adunitcode'; + const CLEAN_ADUNIT_CODE = 'adunitco_de'; const BID_ID = '123456789'; const BID_ID2 = '987654321'; const CPM = 1.3; @@ -167,55 +167,55 @@ describe('E-Planning Adapter', () => { } }; - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - it('should return true when bid has ci parameter', () => { + describe('isBidRequestValid', function () { + it('should return true when bid has ci parameter', function () { expect(spec.isBidRequestValid(validBid)).to.equal(true); }); - it('should return false when bid does not have ci parameter and is not a test bid', () => { + it('should return false when bid does not have ci parameter and is not a test bid', function () { expect(spec.isBidRequestValid(invalidBid)).to.equal(false); }); - it('should return true when bid does not have ci parameter but is a test bid', () => { + it('should return true when bid does not have ci parameter but is a test bid', function () { expect(spec.isBidRequestValid(testBid)).to.equal(true); }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [validBid]; - it('should create the url correctly', () => { + it('should create the url correctly', function () { const url = spec.buildRequests(bidRequests).url; expect(url).to.equal('//ads.us.e-planning.net/hb/1/' + CI + '/1/localhost/ROS'); }); - it('should return GET method', () => { + it('should return GET method', function () { const method = spec.buildRequests(bidRequests).method; expect(method).to.equal('GET'); }); - it('should return r parameter with value pbjs', () => { + it('should return r parameter with value pbjs', function () { const r = spec.buildRequests(bidRequests).data.r; expect(r).to.equal('pbjs'); }); - it('should return pbv parameter with value prebid version', () => { + it('should return pbv parameter with value prebid version', function () { const pbv = spec.buildRequests(bidRequests).data.pbv; expect(pbv).to.equal('$prebid.version$'); }); - it('should return e parameter with value according to the adunit sizes', () => { + it('should return e parameter with value according to the adunit sizes', function () { const e = spec.buildRequests(bidRequests).data.e; expect(e).to.equal(CLEAN_ADUNIT_CODE + ':300x250,300x600'); }); - it('should return correct e parameter with more than one adunit', () => { + it('should return correct e parameter with more than one adunit', function () { const NEW_CODE = ADUNIT_CODE + '2'; const CLEAN_NEW_CODE = CLEAN_ADUNIT_CODE + '2'; const anotherBid = { @@ -232,7 +232,7 @@ describe('E-Planning Adapter', () => { expect(e).to.equal(CLEAN_ADUNIT_CODE + ':300x250,300x600+' + CLEAN_NEW_CODE + ':100x100'); }); - it('should return correct e parameter when the adunit has no size', () => { + it('should return correct e parameter when the adunit has no size', function () { const noSizeBid = { 'bidder': 'eplanning', 'params': { @@ -245,12 +245,12 @@ describe('E-Planning Adapter', () => { expect(e).to.equal(CLEAN_ADUNIT_CODE + ':1x1'); }); - it('should return ur parameter with current window url', () => { + it('should return ur parameter with current window url', function () { const ur = spec.buildRequests(bidRequests).data.ur; expect(ur).to.equal(utils.getTopWindowUrl()); }); - it('should return fr parameter when there is a referrer', () => { + it('should return fr parameter when there is a referrer', function () { const referrer = 'thisisafakereferrer'; const stubGetReferrer = sinon.stub(utils, 'getTopWindowReferrer'); stubGetReferrer.returns(referrer); @@ -259,30 +259,43 @@ describe('E-Planning Adapter', () => { stubGetReferrer.restore() }); - it('should return the testing url when the request has the t parameter', () => { + it('should return crs parameter with document charset', function () { + let expected; + try { + expected = window.top.document.characterSet; + } catch (e) { + expected = document.characterSet; + } + + const chset = spec.buildRequests(bidRequests).data.crs; + + expect(chset).to.equal(expected); + }); + + it('should return the testing url when the request has the t parameter', function () { const url = spec.buildRequests([testBid]).url; const expectedUrl = '//' + TEST_ISV + '/layers/t_pbjs_2.json'; expect(url).to.equal(expectedUrl); }); - it('should return the parameter ncb with value 1', () => { + it('should return the parameter ncb with value 1', function () { const ncb = spec.buildRequests(bidRequests).data.ncb; expect(ncb).to.equal('1'); }); }); - describe('interpretResponse', () => { - it('should return an empty array when there is no ads in the response', () => { + describe('interpretResponse', function () { + it('should return an empty array when there is no ads in the response', function () { const bidResponses = spec.interpretResponse(responseWithNoAd); expect(bidResponses).to.be.empty; }); - it('should return an empty array when there is no spaces in the response', () => { + it('should return an empty array when there is no spaces in the response', function () { const bidResponses = spec.interpretResponse(responseWithNoSpace); expect(bidResponses).to.be.empty; }); - it('should correctly map the parameters in the response', () => { + it('should correctly map the parameters in the response', function () { const bidResponse = spec.interpretResponse(response, { adUnitToBidId: { [CLEAN_ADUNIT_CODE]: BID_ID } })[0]; const expectedResponse = { requestId: BID_ID, @@ -299,7 +312,7 @@ describe('E-Planning Adapter', () => { }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs', function () { const sOptionsAllEnabled = { pixelEnabled: true, iframeEnabled: true @@ -317,30 +330,30 @@ describe('E-Planning Adapter', () => { iframeEnabled: true }; - it('should return an empty array if the response has no syncs', () => { + it('should return an empty array if the response has no syncs', function () { const noSyncsResponse = { cs: [] }; const syncs = spec.getUserSyncs(sOptionsAllEnabled, [noSyncsResponse]); expect(syncs).to.be.empty; }); - it('should return an empty array if there is no sync options enabled', () => { + it('should return an empty array if there is no sync options enabled', function () { const syncs = spec.getUserSyncs(sOptionsAllDisabled, [response]); expect(syncs).to.be.empty; }); - it('should only return pixels if iframe is not enabled', () => { + it('should only return pixels if iframe is not enabled', function () { const syncs = spec.getUserSyncs(sOptionsOnlyPixel, [response]); syncs.forEach(sync => expect(sync.type).to.equal('image')); }); - it('should only return iframes if pixel is not enabled', () => { + it('should only return iframes if pixel is not enabled', function () { const syncs = spec.getUserSyncs(sOptionsOnlyIframe, [response]); syncs.forEach(sync => expect(sync.type).to.equal('iframe')); }); }); - describe('adUnits mapping to bidId', () => { - it('should correctly map the bidId to the adunit', () => { + describe('adUnits mapping to bidId', function () { + it('should correctly map the bidId to the adunit', function () { const requests = spec.buildRequests([validBid, validBid2]); const responses = spec.interpretResponse(responseWithTwoAdunits, requests); expect(responses[0].requestId).to.equal(BID_ID); diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js new file mode 100644 index 00000000000..e4cccbf5cf3 --- /dev/null +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -0,0 +1,433 @@ +import {assert, expect} from 'chai'; +import * as url from 'src/url'; +import {spec} from 'modules/etargetBidAdapter'; +import { BANNER, VIDEO } from 'src/mediaTypes'; + +describe('etarget adapter', function () { + let serverResponse, bidRequest, bidResponses; + let bids = []; + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'etarget', + 'params': { + 'refid': '55410', + 'country': '1' + } + }; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + it('should pass multiple bids via single request', function () { + let request = spec.buildRequests(bids); + let parsedUrl = parseUrl(request.url); + assert.lengthOf(parsedUrl.items, 7); + }); + + it('should handle global request parameters', function () { + let parsedUrl = parseUrl(spec.buildRequests([bids[0]]).url); + assert.equal(parsedUrl.path, '//sk.search.etargetnet.com/hb'); + }); + + it('should set correct request method', function () { + let request = spec.buildRequests([bids[0]]); + assert.equal(request.method, 'POST'); + }); + + it('should correctly form bid items', function () { + let bidList = bids; + let request = spec.buildRequests(bidList); + let parsedUrl = parseUrl(request.url); + assert.deepEqual(parsedUrl.items, [ + { + refid: '1', + country: '1', + transactionId: '5f33781f-9552-4ca1', + url: 'some// there' + }, + { + refid: '2', + country: '1', + someVar: 'someValue', + pt: 'gross', + transactionId: '5f33781f-9552-4iuy' + }, + { + refid: '3', + country: '1', + pdom: 'home', + transactionId: '5f33781f-9552-7ev3' + }, + { + refid: '3', + country: '1', + pdom: 'home', + transactionId: '5f33781f-9552-7ev3' + }, + { + refid: '3', + country: '1', + pdom: 'home', + transactionId: '5f33781f-9552-7ev3' + }, + { + refid: '5', + country: '1', + pt: 'net', + transactionId: '5f33781f-9552-7ev3', + }, + { + refid: '6', + country: '1', + pt: 'gross', + transactionId: '5f33781f-9552-7ev3' + } + ]); + }); + + it('should not change original validBidRequests object', function () { + var resultBids = JSON.parse(JSON.stringify(bids[0])); + let request = spec.buildRequests([bids[0]]); + assert.deepEqual(resultBids, bids[0]); + }); + + describe('gdpr', function () { + it('should send GDPR Consent data to etarget if gdprApplies', function () { + let resultBids = JSON.parse(JSON.stringify(bids[0])); + let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); + let parsedUrl = parseUrl(request.url).query; + + assert.equal(parsedUrl.gdpr, 'true'); + assert.equal(parsedUrl.gdpr_consent, 'concentDataString'); + }); + + it('should not send GDPR Consent data to etarget if gdprApplies is false or undefined', function () { + let resultBids = JSON.parse(JSON.stringify(bids[0])); + let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: false, consentString: 'concentDataString'}}); + let parsedUrl = parseUrl(request.url).query; + + assert.ok(!parsedUrl.gdpr); + assert.ok(!parsedUrl.gdpr_consent); + + request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: undefined, consentString: 'concentDataString'}}); + assert.ok(!parsedUrl.gdpr); + assert.ok(!parsedUrl.gdpr_consent); + }); + + it('should return GDPR Consent data with request data', function () { + let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); + + assert.deepEqual(request.gdpr, { + gdpr: true, + gdpr_consent: 'concentDataString' + }); + + request = spec.buildRequests([bids[0]]); + assert.ok(!request.gdpr); + }); + }); + }); + + describe('interpretResponse', function () { + it('should respond with empty response when there is empty serverResponse', function () { + let result = spec.interpretResponse({ body: {} }, {}); + assert.deepEqual(result, []); + }); + it('should respond with empty response when response from server is not banner', function () { + serverResponse.body[0].response = 'not banner'; + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + let result = spec.interpretResponse(serverResponse, bidRequest); + + assert.deepEqual(result, []); + }); + it('should interpret server response correctly with one bid', function () { + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + let result = spec.interpretResponse(serverResponse, bidRequest)[0]; + + assert.equal(result.requestId, '2a0cf4e'); + assert.equal(result.cpm, 13.9); + assert.equal(result.width, 300); + assert.equal(result.height, 250); + assert.equal(result.currency, 'EUR'); + assert.equal(result.netRevenue, true); + assert.equal(result.ttl, 360); + assert.equal(result.ad, ''); + assert.equal(result.transactionId, '5f33781f-9552-4ca1'); + }); + + it('should set correct netRevenue', function () { + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[1]]; + bidRequest.netRevenue = 'net'; + let result = spec.interpretResponse(serverResponse, bidRequest)[0]; + + assert.equal(result.netRevenue, true); + }); + + it('should create bid response item for every requested item', function () { + let result = spec.interpretResponse(serverResponse, bidRequest); + assert.lengthOf(result, 5); + }); + + it('should create bid response with vast xml', function () { + const result = spec.interpretResponse(serverResponse, bidRequest)[3]; + assert.equal(result.vastXml, ''); + }); + + it('should create bid response with vast url', function () { + const result = spec.interpretResponse(serverResponse, bidRequest)[4]; + assert.equal(result.vastUrl, 'vast://url'); + }); + + it('should set mediaType on bid response', function () { + const expected = [ BANNER, BANNER, BANNER, VIDEO, VIDEO ]; + const result = spec.interpretResponse(serverResponse, bidRequest); + for (let i = 0; i < result.length; i++) { + assert.equal(result[i].mediaType, expected[i]); + } + }); + + it('should set default netRevenue as gross', function () { + bidRequest.netRevenue = 'gross'; + const result = spec.interpretResponse(serverResponse, bidRequest); + for (let i = 0; i < result.length; i++) { + assert.equal(result[i].netRevenue, true); + } + }); + + it('should set gdpr if it exist in bidRequest', function () { + bidRequest.gdpr = { + gdpr: true, + gdpr_consent: 'ERW342EIOWT34234KMGds' + }; + let result = spec.interpretResponse(serverResponse, bidRequest); + for (let i = 0; i < result.length; i++) { + assert.equal(result[i].gdpr, true); + assert.equal(result[i].gdpr_consent, 'ERW342EIOWT34234KMGds'); + }; + + bidRequest.gdpr = undefined; + result = spec.interpretResponse(serverResponse, bidRequest); + for (let i = 0; i < result.length; i++) { + assert.ok(!result[i].gdpr); + assert.ok(!result[i].gdpr_consent); + }; + }); + + describe('verifySizes', function () { + it('should respond with empty response when sizes doesn\'t match', function () { + serverResponse.body[0].response = 'banner'; + serverResponse.body[0].width = 100; + serverResponse.body[0].height = 150; + + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + let result = spec.interpretResponse(serverResponse, bidRequest); + + assert.equal(serverResponse.body.length, 1); + assert.equal(serverResponse.body[0].response, 'banner'); + assert.deepEqual(result, []); + }); + it('should respond with empty response when sizes as a strings doesn\'t match', function () { + serverResponse.body[0].response = 'banner'; + serverResponse.body[0].width = 100; + serverResponse.body[0].height = 150; + + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + + bidRequest.bids[0].sizes = [['101', '150']]; + let result = spec.interpretResponse(serverResponse, bidRequest); + + assert.equal(serverResponse.body.length, 1); + assert.equal(serverResponse.body[0].response, 'banner'); + assert.deepEqual(result, []); + }); + it('should support size dimensions as a strings', function () { + serverResponse.body[0].response = 'banner'; + serverResponse.body[0].width = 300; + serverResponse.body[0].height = 600; + + serverResponse.body = [serverResponse.body[0]]; + bidRequest.bids = [bidRequest.bids[0]]; + + bidRequest.bids[0].sizes = [['300', '250'], ['250', '300'], ['300', '600'], ['600', '300']] + let result = spec.interpretResponse(serverResponse, bidRequest); + + assert.equal(result[0].width, 300); + assert.equal(result[0].height, 600); + }); + }) + }); + + beforeEach(function () { + let sizes = [[250, 300], [300, 250], [300, 600]]; + let placementCode = ['div-01', 'div-02', 'div-03', 'div-04', 'div-05']; + let params = [{refid: 1, country: 1, url: 'some// there'}, {refid: 2, country: 1, someVar: 'someValue', pt: 'gross'}, {refid: 3, country: 1, pdom: 'home'}, {refid: 5, country: 1, pt: 'net'}, {refid: 6, country: 1, pt: 'gross'}]; + bids = [ + { + adUnitCode: placementCode[0], + auctionId: '7aefb970-2045', + bidId: '2a0cf4e', + bidder: 'etarget', + bidderRequestId: '1ab8d9', + params: params[0], + tid: 45, + placementCode: placementCode[0], + sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], + transactionId: '5f33781f-9552-4ca1' + }, + { + adUnitCode: placementCode[1], + auctionId: '7aefb970-2045', + bidId: '2a0cf5b', + bidder: 'etarget', + bidderRequestId: '1ab8d9', + params: params[1], + placementCode: placementCode[1], + sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], + transactionId: '5f33781f-9552-4iuy' + }, + { + adUnitCode: placementCode[2], + auctionId: '7aefb970-2045', + bidId: '2a0cf6n', + bidder: 'etarget', + bidderRequestId: '1ab8d9', + params: params[2], + placementCode: placementCode[2], + sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], + transactionId: '5f33781f-9552-7ev3' + }, + { + adUnitCode: placementCode[3], + auctionId: '7aefb970-2045', + bidId: '2a0cf6n', + bidder: 'etarget', + bidderRequestId: '1ab8d9', + params: params[2], + placementCode: placementCode[2], + sizes: [], + transactionId: '5f33781f-9552-7ev3' + }, + { + adUnitCode: placementCode[4], + auctionId: '7aefb970-2045', + bidId: '2a0cf6n', + bidder: 'etarget', + bidderRequestId: '1ab8d9', + params: params[2], + placementCode: placementCode[2], + sizes: [], + transactionId: '5f33781f-9552-7ev3' + }, + { + adUnitCode: placementCode[4], + auctionId: '7aefb970-2045', + bidId: '2a0cf6n', + bidder: 'etarget', + bidderRequestId: '1ab8d9', + params: params[3], + placementCode: placementCode[2], + sizes: [], + transactionId: '5f33781f-9552-7ev3' + }, + { + adUnitCode: placementCode[4], + auctionId: '7aefb970-2045', + bidId: '2a0cf6n', + bidder: 'etarget', + bidderRequestId: '1ab8d9', + params: params[4], + placementCode: placementCode[2], + sizes: [], + transactionId: '5f33781f-9552-7ev3' + } + ]; + serverResponse = { + body: [ + { + banner: '', + deal_id: '123abc', + height: 250, + response: 'banner', + width: 300, + win_bid: 13.9, + win_cur: 'EUR' + }, + { + banner: '', + deal_id: '123abc', + height: 300, + response: 'banner', + width: 250, + win_bid: 13.9, + win_cur: 'EUR' + }, + { + banner: '', + deal_id: '123abc', + height: 300, + response: 'banner', + width: 600, + win_bid: 10, + win_cur: 'EUR' + }, + { + deal_id: '123abc', + height: 300, + response: 'video', + width: 600, + win_bid: 10, + win_cur: 'EUR', + vast_content: '' + }, + { + deal_id: '123abc', + height: 300, + response: 'video', + width: 600, + win_bid: 10, + win_cur: 'EUR', + vast_link: 'vast://url' + } + ], + headers: {} + }; + bidRequest = { + bidder: 'etarget', + bids: bids, + method: 'POST', + url: 'url', + netRevenue: 'net' + }; + }); +}); + +function parseUrl(url) { + const parts = url.split('/'); + const query = parts.pop().split('&'); + return { + path: parts.join('/'), + items: query + .filter((i) => !~i.indexOf('=')) + .map((i) => atob(decodeURIComponent(i)) + .split('&') + .reduce(toObject, {})), + query: query + .filter((i) => ~i.indexOf('=')) + .map((i) => i.replace('?', '')) + .reduce(toObject, {}) + }; +} + +function toObject(cache, string) { + const keyValue = string.split('='); + cache[keyValue[0]] = keyValue[1]; + return cache; +} diff --git a/test/spec/modules/eywamediaBidAdapter_spec.js b/test/spec/modules/eywamediaBidAdapter_spec.js new file mode 100644 index 00000000000..945c0dd0d01 --- /dev/null +++ b/test/spec/modules/eywamediaBidAdapter_spec.js @@ -0,0 +1,253 @@ +import { expect } from 'chai'; +import { spec } from 'modules/eywamediaBidAdapter'; + +describe('EywamediaAdapter', function () { + let serverResponse, bidRequests, bidRequest, bidResponses; + const ENDPOINT = 'https://adtarbostg.eywamedia.com/auctions/prebidjs/3000'; + + bidRequests = [ + { + 'auctionId': 'fc917230-a5e1-4a42-b7d9-8fb776124e43', + 'sizes': [[300, 250]], + 'bidRequestsCount': 1, + 'params': { + 'publisherId': '1234_abcd' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'crumbs': { + 'pubcid': '8b640d4e-1f6d-4fd3-b63f-2570572d8100' + }, + 'bidId': '28b09d0543d671', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b1', + 'src': 'client', + 'bidder': 'eywamedia', + 'bidderRequestId': '14d8cbc769114b' + }, + { + 'auctionId': 'fc917230-a5e1-4a42-b7d9-8fb776124e43', + 'sizes': [[728, 90]], + 'bidRequestsCount': 1, + 'params': { + 'publisherId': '1234_abcd' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90]] + } + }, + 'crumbs': { + 'pubcid': '8b640d4e-1f6d-4fd3-b63f-2570572d8100' + }, + 'bidId': '28b09d0543d672', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b2', + 'src': 'client', + 'bidder': 'eywamedia', + 'bidderRequestId': '14d8cbc769114b' + } + ]; + + bidRequest = { + 'auctionId': 'c88115a4-7e71-43d0-9c96-a9b43ebd143d', + 'auctionStart': 1564725164517, + 'bidderCode': 'eywamedia', + 'bidderRequestId': '191afa18994fdd', + 'bids': [], + 'refererInfo': { + 'canonicalUrl': '', + 'numIframes': 0, + 'reachedTop': true, + 'referer': '' + }, + 'stack': [ + '' + ], + 'start': 1564725164520, + 'timeout': 3000 + }; + + let testBid = { + 'bidder': 'eywamedia', + 'params': { + 'publisherId': '1234_abcd' + } + }; + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(testBid)); + }); + + it('should return false when required params are missing', function () { + testBid.params = { + test: '231212312' + }; + assert.isFalse(spec.isBidRequestValid(testBid)); + }); + }); + + describe('buildRequests', function () { + it('should attempt to send bid requests to the endpoint via POST', function () { + const requests = spec.buildRequests(bidRequests, bidRequest); + expect(requests.method).to.equal('POST'); + expect(requests.url).to.be.equal(ENDPOINT); + }); + + it('should not blow up if crumbs is undefined', function () { + let bidArray = [ + { ...testBid, crumbs: undefined } + ] + expect(function () { spec.buildRequests(bidArray, bidRequest) }).not.to.throw() + }) + + it('should return true when required params found', function () { + testBid.params.publisherId = '1234_abcd'; + assert(spec.isBidRequestValid(testBid)); + }); + }); + + describe('interpretResponse', function () { + beforeEach(function () { + serverResponse = { + 'body': + [ + { + 'ad': '', + 'adSlot': '', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'adUrl': 'http://eywamedia.com', + 'bidId': '28b09d0543d671', + 'bidder': 'eywamedia', + 'bidderCode': 'eywamedia', + 'cpm': 1, + 'height': 250, + 'requestTimestamp': 1564725162, + 'respType': 'banner', + 'size': '300X250', + 'statusMessage': 'Bid available', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b1', + 'usesGenericKeys': true, + 'width': 300 + }, + { + 'ad': '', + 'adSlot': '', + 'adUnitCode': 'div-gpt-ad-1460505748561-1', + 'adUrl': 'http://eywamedia.com', + 'bidId': '28b09d0543d672', + 'bidder': 'eywamedia', + 'bidderCode': 'eywamedia', + 'cpm': 1, + 'height': 90, + 'requestTimestamp': 1564725164, + 'respType': 'banner', + 'size': '728X90', + 'statusMessage': 'Bid available', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b2', + 'usesGenericKeys': true, + 'width': 728 + } + ], + 'headers': 'header?' + }; + + bidRequest = { + 'data': + { + 'bidPayload': + [ + { + 'auctionId': 'fc917230-a5e1-4a42-b7d9-8fb776124e43', + 'sizes': [[300, 250]], + 'bidRequestsCount': 1, + 'params': { + 'publisherId': '1234_abcd' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'crumbs': { + 'pubcid': '8b640d4e-1f6d-4fd3-b63f-2570572d8100' + }, + 'bidId': '28b09d0543d671', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b1', + 'src': 'client', + 'bidder': 'eywamedia', + 'bidderRequestId': '14d8cbc769114b' + }, + { + 'auctionId': 'fc917230-a5e1-4a42-b7d9-8fb776124e43', + 'sizes': [[728, 90]], + 'bidRequestsCount': 1, + 'params': { + 'publisherId': '1234_abcd' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90]] + } + }, + 'crumbs': { + 'pubcid': '8b640d4e-1f6d-4fd3-b63f-2570572d8100' + }, + 'bidId': '28b09d0543d672', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b2', + 'src': 'client', + 'bidder': 'eywamedia', + 'bidderRequestId': '14d8cbc769114b' + } + ] + } + }; + bidResponses = [ + { + 'ad': '', + 'bidderCode': 'eywamedia', + 'cpm': 1, + 'creativeId': '28b09d0543d671', + 'currency': 'USD', + 'height': 250, + 'mediaType': 'banner', + 'netRevenue': true, + 'requestId': '28b09d0543d671', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b1', + 'ttl': 360, + 'width': 300 + }, + { + 'ad': '', + 'bidderCode': 'eywamedia', + 'cpm': 1, + 'creativeId': '28b09d0543d672', + 'currency': 'USD', + 'height': 90, + 'mediaType': 'banner', + 'netRevenue': true, + 'requestId': '28b09d0543d672', + 'transactionId': 'd909c39c-ecc9-41a4-897c-2d2fdfdf41b2', + 'ttl': 360, + 'width': 728 + } + ] + }); + + it('should respond with empty response when there is empty serverResponse', function () { + let result = spec.interpretResponse({ body: {} }, bidRequest); + assert.deepEqual(result, []); + }); + + it('should respond with multile response when there is multiple serverResponse', function () { + let result = spec.interpretResponse(serverResponse, bidRequest); + assert.deepEqual(result, bidResponses); + }); + }); +}); diff --git a/test/spec/modules/fairtradeBidAdapter_spec.js b/test/spec/modules/fairtradeBidAdapter_spec.js index 07c26e8f0c1..ecd5db3c051 100644 --- a/test/spec/modules/fairtradeBidAdapter_spec.js +++ b/test/spec/modules/fairtradeBidAdapter_spec.js @@ -5,13 +5,13 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('FairTradeAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'fairtrade', 'params': { @@ -24,11 +24,11 @@ describe('FairTradeAdapter', function () { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -38,7 +38,7 @@ describe('FairTradeAdapter', function () { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': 'fairtrade', @@ -75,7 +75,7 @@ describe('FairTradeAdapter', function () { } ]; - it('should attach valid params to the tag', () => { + it('should attach valid params to the tag', function () { const request = spec.buildRequests([bidRequests[0]]); const payload = request.data; expect(payload).to.be.an('object'); @@ -85,7 +85,7 @@ describe('FairTradeAdapter', function () { expect(payload).to.have.property('r', '22edbae2733bf6'); }); - it('auids must not be duplicated', () => { + it('auids must not be duplicated', function () { const request = spec.buildRequests(bidRequests); const payload = request.data; expect(payload).to.be.an('object'); @@ -95,7 +95,7 @@ describe('FairTradeAdapter', function () { expect(payload).to.have.property('r', '22edbae2733bf6'); }); - it('pt parameter must be "gross" if params.priceType === "gross"', () => { + it('pt parameter must be "gross" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; const request = spec.buildRequests(bidRequests); const payload = request.data; @@ -107,7 +107,7 @@ describe('FairTradeAdapter', function () { delete bidRequests[1].params.priceType; }); - it('pt parameter must be "net" or "gross"', () => { + it('pt parameter must be "net" or "gross"', function () { bidRequests[1].params.priceType = 'some'; const request = spec.buildRequests(bidRequests); const payload = request.data; @@ -120,7 +120,7 @@ describe('FairTradeAdapter', function () { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 165, 'h': 250, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 166, 'h': 90, 'w': 728}], 'seat': '1'}, @@ -131,7 +131,7 @@ describe('FairTradeAdapter', function () { {'seat': '1'}, ]; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const bidRequests = [ { 'bidder': 'fairtrade', @@ -165,7 +165,7 @@ describe('FairTradeAdapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('should get correct multi bid response', () => { + it('should get correct multi bid response', function () { const bidRequests = [ { 'bidder': 'fairtrade', @@ -245,7 +245,7 @@ describe('FairTradeAdapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('handles wrong and nobid responses', () => { + it('handles wrong and nobid responses', function () { const bidRequests = [ { 'bidder': 'fairtrade', diff --git a/test/spec/modules/feedadBidAdapter_spec.js b/test/spec/modules/feedadBidAdapter_spec.js new file mode 100644 index 00000000000..3432a40eca4 --- /dev/null +++ b/test/spec/modules/feedadBidAdapter_spec.js @@ -0,0 +1,433 @@ +import {expect} from 'chai'; +import {spec} from 'modules/feedadBidAdapter'; +import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes'; +import * as sinon from 'sinon'; + +const CODE = 'feedad'; + +describe('FeedAdAdapter', function () { + describe('Public API', function () { + it('should have the FeedAd bidder code', function () { + expect(spec.code).to.equal(CODE); + }); + it('should only support video and banner ads', function () { + expect(spec.supportedMediaTypes).to.be.a('array'); + expect(spec.supportedMediaTypes).to.include(BANNER); + expect(spec.supportedMediaTypes).to.include(VIDEO); + expect(spec.supportedMediaTypes).not.to.include(NATIVE); + }); + it('should export the BidderSpec functions', function () { + expect(spec.isBidRequestValid).to.be.a('function'); + expect(spec.buildRequests).to.be.a('function'); + expect(spec.interpretResponse).to.be.a('function'); + expect(spec.onTimeout).to.be.a('function'); + expect(spec.onBidWon).to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should detect missing params', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [] + }); + expect(result).to.equal(false); + }); + it('should detect missing client token', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {placementId: 'placement'} + }); + expect(result).to.equal(false); + }); + it('should detect zero length client token', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: '', placementId: 'placement'} + }); + expect(result).to.equal(false); + }); + it('should detect missing placement id', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken'} + }); + expect(result).to.equal(false); + }); + it('should detect zero length placement id', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId: ''} + }); + expect(result).to.equal(false); + }); + it('should detect too long placement id', function () { + var placementId = ''; + for (var i = 0; i < 300; i++) { + placementId += 'a'; + } + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId} + }); + expect(result).to.equal(false); + }); + it('should detect invalid placement id', function () { + [ + 'placement id with spaces', + 'some|id', + 'PLACEMENTID', + 'placeme:ntId' + ].forEach(id => { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId: id} + }); + expect(result).to.equal(false); + }); + }); + it('should accept valid parameters', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }); + expect(result).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const bidderRequest = { + refererInfo: { + referer: 'the referer' + }, + some: 'thing' + }; + + it('should accept empty lists', function () { + let result = spec.buildRequests([], bidderRequest); + expect(result).to.be.empty; + }); + it('should filter native media types', function () { + let bid = { + code: 'feedad', + mediaTypes: { + native: { + sizes: [[300, 250], [300, 600]], + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result).to.be.empty; + }); + it('should filter video media types without outstream context', function () { + let bid = { + code: 'feedad', + mediaTypes: { + video: { + context: 'instream' + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result).to.be.empty; + }); + it('should pass through outstream video media', function () { + let bid = { + code: 'feedad', + mediaTypes: { + video: { + context: 'outstream' + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.data.bids).to.be.lengthOf(1); + expect(result.data.bids[0]).to.deep.equal(bid); + }); + it('should pass through banner media', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.data.bids).to.be.lengthOf(1); + expect(result.data.bids[0]).to.deep.equal(bid); + }); + it('should detect empty media types', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: undefined, + video: undefined, + native: undefined + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result).to.be.empty; + }); + it('should use POST', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.method).to.equal('POST'); + }); + it('should use the correct URL', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.url).to.equal('https://api.feedad.com/1/prebid/web/bids'); + }); + it('should specify the content type explicitly', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.options).to.deep.equal({ + contentType: 'application/json' + }) + }); + it('should include the bidder request', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid, bid, bid], bidderRequest); + expect(result.data).to.deep.include(bidderRequest); + }); + it('should detect missing bidder request parameter', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid, bid, bid]); + expect(result).to.be.empty; + }); + }); + + describe('interpretResponse', function () { + const body = [{ + foo: 'bar', + sub: { + obj: 5 + } + }, { + bar: 'foo' + }]; + + it('should convert string bodies to JSON', function () { + let result = spec.interpretResponse({body: JSON.stringify(body)}); + expect(result).to.deep.equal(body); + }); + + it('should pass through body objects', function () { + let result = spec.interpretResponse({body}); + expect(result).to.deep.equal(body); + }); + }); + + describe('event tracking calls', function () { + const clientToken = 'clientToken'; + const placementId = 'placement id'; + const auctionId = 'the auction id'; + const bidId = 'the bid id'; + const transactionId = 'the transaction id'; + const referer = 'the referer'; + const bidderRequest = { + refererInfo: { + referer: referer + }, + some: 'thing' + }; + const bid = { + 'bidder': 'feedad', + 'params': { + 'clientToken': 'fupp', + 'placementId': 'prebid-test' + }, + 'crumbs': { + 'pubcid': '6254a85f-bded-489a-9736-83c45d45ef1d' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': transactionId, + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': bidId, + 'bidderRequestId': '10739fe6fe2127', + 'auctionId': '5ac67dff-d971-4b56-84a3-345a87a1f786', + 'src': 'client', + 'bidRequestsCount': 1 + }; + const timeoutData = { + 'bidId': bidId, + 'bidder': 'feedad', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'auctionId': auctionId, + 'params': [ + { + 'clientToken': clientToken, + 'placementId': placementId + } + ], + 'timeout': 3000 + }; + const bidWonData = { + 'bidderCode': 'feedad', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '3a4529aa05114d', + 'requestId': bidId, + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.5, + 'ad': 'ad content', + 'ttl': 60, + 'creativeId': 'feedad-21-0', + 'netRevenue': true, + 'currency': 'EUR', + 'auctionId': auctionId, + 'responseTimestamp': 1558365914596, + 'requestTimestamp': 1558365914506, + 'bidder': 'feedad', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'timeToRespond': 90, + 'pbLg': '0.50', + 'pbMg': '0.50', + 'pbHg': '0.50', + 'pbAg': '0.50', + 'pbDg': '0.50', + 'pbCg': '', + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'feedad', + 'hb_adid': '3a4529aa05114d', + 'hb_pb': '0.50', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner' + }, + 'status': 'rendered', + 'params': [ + { + 'clientToken': clientToken, + 'placementId': placementId + } + ] + }; + const cases = [ + ['onTimeout', timeoutData, 'prebid_bidTimeout'], + ['onBidWon', bidWonData, 'prebid_bidWon'], + ]; + + cases.forEach(([name, data, eventKlass]) => { + let subject = spec[name]; + describe(name + ' handler', function () { + let xhr; + let requests; + + beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = xhr => requests.push(xhr); + }); + + afterEach(function () { + xhr.restore(); + }); + + it('should do nothing on empty data', function () { + subject(undefined); + subject(null); + expect(requests.length).to.equal(0); + }); + + it('should do nothing when bid metadata is not set', function () { + subject(data); + expect(requests.length).to.equal(0); + }); + + it('should send tracking params when correct metadata was set', function () { + spec.buildRequests([bid], bidderRequest); + let expectedData = { + app_hybrid: false, + client_token: clientToken, + placement_id: placementId, + klass: eventKlass, + prebid_auction_id: auctionId, + prebid_bid_id: bidId, + prebid_transaction_id: transactionId, + referer, + sdk_version: '1.0.0' + }; + subject(data); + expect(requests.length).to.equal(1); + let call = requests[0]; + expect(call.url).to.equal('https://api.feedad.com/1/prebid/web/events'); + expect(JSON.parse(call.requestBody)).to.deep.equal(expectedData); + expect(call.method).to.equal('POST'); + expect(call.requestHeaders).to.include({'Content-Type': 'application/json;charset=utf-8'}); + }) + }); + }); + }); +}); diff --git a/test/spec/modules/fidelityBidAdapter_spec.js b/test/spec/modules/fidelityBidAdapter_spec.js index 28ea18aacac..e8e008103e6 100644 --- a/test/spec/modules/fidelityBidAdapter_spec.js +++ b/test/spec/modules/fidelityBidAdapter_spec.js @@ -2,16 +2,16 @@ import { expect } from 'chai'; import { spec } from 'modules/fidelityBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('FidelityAdapter', () => { +describe('FidelityAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'fidelity', 'params': { @@ -26,11 +26,11 @@ describe('FidelityAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return true when required params found', () => { + it('should return true when required params found', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -39,7 +39,7 @@ describe('FidelityAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -49,10 +49,10 @@ describe('FidelityAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidderRequest = { bidderCode: 'fidelity', - auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', bidderRequestId: '178e34bad3658f', bids: [ { @@ -66,7 +66,7 @@ describe('FidelityAdapter', () => { sizes: [[300, 250], [320, 50]], bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', - auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' } ], @@ -75,7 +75,7 @@ describe('FidelityAdapter', () => { timeout: 5000 }; - it('should add source and verison to the tag', () => { + it('should add source and verison to the tag', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const payload = request.data; expect(payload.from).to.exist; @@ -85,21 +85,41 @@ describe('FidelityAdapter', () => { expect(payload.zoneid).to.exist; expect(payload.floor).to.exist; expect(payload.charset).to.exist; - expect(payload.defloc).to.exist; - expect(payload.altloc).to.exist; expect(payload.subid).to.exist; expect(payload.flashver).to.exist; expect(payload.tmax).to.exist; + expect(payload.defloc).to.exist; + }); + + it('should add gdpr consent information to the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: consentString, + vendorData: { + vendorConsents: { + '408': 1 + }, + }, + }; + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const payload = request.data; + expect(payload.gdpr).to.exist.and.to.be.a('number'); + expect(payload.gdpr).to.equal(1); + expect(payload.consent_str).to.exist.and.to.be.a('string'); + expect(payload.consent_str).to.equal(consentString); + expect(payload.consent_given).to.exist.and.to.be.a('number'); + expect(payload.consent_given).to.equal(1); }); - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.url).to.equal('//t.fidelity-media.com/delivery/hb.php'); expect(request.method).to.equal('GET'); }); }) - describe('interpretResponse', () => { + describe('interpretResponse', function () { let response = { 'id': '543210', 'seatbid': [ { @@ -114,7 +134,7 @@ describe('FidelityAdapter', () => { } ] }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { let expectedResponse = [ { requestId: 'bidId-123456-1', @@ -133,7 +153,7 @@ describe('FidelityAdapter', () => { expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = { 'id': '543210', 'seatbid': [ ] @@ -144,10 +164,10 @@ describe('FidelityAdapter', () => { }); }); - describe('user sync', () => { + describe('user sync', function () { const syncUrl = '//x.fidelity-media.com/delivery/matches.php?type=iframe'; - it('should register the sync iframe', () => { + it('should register the sync iframe', function () { expect(spec.getUserSyncs({})).to.be.undefined; expect(spec.getUserSyncs({iframeEnabled: false})).to.be.undefined; const options = spec.getUserSyncs({iframeEnabled: true}); diff --git a/test/spec/modules/fintezaAnalyticsAdapter_spec.js b/test/spec/modules/fintezaAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..33b24f5f50f --- /dev/null +++ b/test/spec/modules/fintezaAnalyticsAdapter_spec.js @@ -0,0 +1,220 @@ +import fntzAnalyticsAdapter from 'modules/fintezaAnalyticsAdapter'; +import includes from 'core-js/library/fn/array/includes'; +import { expect } from 'chai'; +import { parse as parseURL } from 'src/url'; +let adapterManager = require('src/adapterManager').default; +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('finteza analytics adapter', function () { + const clientId = 'fntz-client-32145'; + + let xhr; + let requests; + + beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => { requests.push(request) }; + sinon.stub(events, 'getEvents').returns([]); + sinon.spy(fntzAnalyticsAdapter, 'track'); + + adapterManager.registerAnalyticsAdapter({ + code: 'finteza', + adapter: fntzAnalyticsAdapter + }); + + adapterManager.enableAnalytics({ + provider: 'finteza', + options: { + id: clientId, // Client ID (required) + bidRequestTrack: 'Bid Request %BIDDER%', + bidResponseTimeTrack: 'Bid Response Time %bidder%', + bidResponsePriceTrack: 'Bid Response Price %bidder%', + bidTimeoutTrack: 'Bid Timeout %Bidder%', + bidWonTrack: 'Bid Won %BIDDER%', + } + }); + }); + + afterEach(function () { + xhr.restore(); + events.getEvents.restore(); + fntzAnalyticsAdapter.track.restore(); + fntzAnalyticsAdapter.disableAnalytics(); + }); + + describe('track', () => { + describe('bid request', () => { + it('builds and sends data', function () { + const bidderCode = 'Bidder789'; + const pauctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; + + const bidRequest = { + bidderCode: bidderCode, + auctionId: pauctionId, + bidderRequestId: '1a6fc81528d0f6', + bids: [{ + bidder: bidderCode, + placementCode: 'container-1', + bidId: '208750227436c1', + bidderRequestId: '1a6fc81528d0f6', + auctionId: pauctionId, + startTime: 1509369418389, + sizes: [[300, 250]], + }], + auctionStart: 1509369418387, + timeout: 3000, + start: 1509369418389 + }; + + // Emit the events with the "real" arguments + events.emit(constants.EVENTS.BID_REQUESTED, bidRequest); + + expect(requests.length).to.equal(1); + + expect(requests[0].method).to.equal('GET'); + + const url = parseURL(requests[0].url); + + expect(url.protocol).to.equal('https'); + expect(url.hostname).to.equal('content.mql5.com'); + expect(url.pathname).to.equal('/tr'); + expect(url.search.id).to.equal(clientId); + expect(decodeURIComponent(url.search.event)).to.equal(`Bid Request ${bidderCode.toUpperCase()}`); + + sinon.assert.callCount(fntzAnalyticsAdapter.track, 1); + }); + }); + + describe('bid response', () => { + it('builds and sends data', function () { + const bidderCode = 'Bidder789'; + const pauctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; + + const timeToRespond = 443; + const cpm = 0.015; + + const bidResponse = { + bidderCode: bidderCode, + adId: '208750227436c1', + cpm: cpm, + auctionId: pauctionId, + responseTimestamp: 1509369418832, + requestTimestamp: 1509369418389, + bidder: bidderCode, + timeToRespond: timeToRespond, + size: '300x250', + width: 300, + height: 250, + }; + + // Emit the events with the "real" arguments + events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + + expect(requests.length).to.equal(2); + + expect(requests[0].method).to.equal('GET'); + + let url = parseURL(requests[0].url); + + expect(url.protocol).to.equal('https'); + expect(url.hostname).to.equal('content.mql5.com'); + expect(url.pathname).to.equal('/tr'); + expect(url.search.id).to.equal(clientId); + expect(decodeURIComponent(url.search.event)).to.equal(`Bid Response Price ${bidderCode.toLowerCase()}`); + expect(url.search.value).to.equal(String(cpm)); + expect(url.search.unit).to.equal('usd'); + + expect(requests[1].method).to.equal('GET'); + + url = parseURL(requests[1].url); + + expect(url.protocol).to.equal('https'); + expect(url.hostname).to.equal('content.mql5.com'); + expect(url.pathname).to.equal('/tr'); + expect(url.search.id).to.equal(clientId); + expect(decodeURIComponent(url.search.event)).to.equal(`Bid Response Time ${bidderCode.toLowerCase()}`); + expect(url.search.value).to.equal(String(timeToRespond)); + expect(url.search.unit).to.equal('ms'); + + sinon.assert.callCount(fntzAnalyticsAdapter.track, 1); + }); + }); + + describe('bid won', () => { + it('builds and sends data', function () { + const bidderCode = 'Bidder789'; + const pauctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; + + const cpm = 0.015; + + const bidWon = { + bidderCode: bidderCode, + cpm: cpm, + adId: 'adIdData', + ad: 'adContent', + auctionId: pauctionId, + width: 300, + height: 250 + } + + // Emit the events with the "real" arguments + events.emit(constants.EVENTS.BID_WON, bidWon); + + expect(requests.length).to.equal(1); + + expect(requests[0].method).to.equal('GET'); + + const url = parseURL(requests[0].url); + + expect(url.protocol).to.equal('https'); + expect(url.hostname).to.equal('content.mql5.com'); + expect(url.pathname).to.equal('/tr'); + expect(url.search.id).to.equal(clientId); + expect(decodeURIComponent(url.search.event)).to.equal(`Bid Won ${bidderCode.toUpperCase()}`); + expect(url.search.value).to.equal(String(cpm)); + expect(url.search.unit).to.equal('usd'); + + sinon.assert.callCount(fntzAnalyticsAdapter.track, 1); + }); + }); + + describe('bid timeout', () => { + it('builds and sends data', function () { + const bidderCode = 'biDDer789'; + const pauctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; + + const timeout = 2540; + + const bidTimeout = [ + { + bidId: '208750227436c1', + bidder: bidderCode, + auctionId: pauctionId, + timeout: timeout, + } + ]; + + // Emit the events with the "real" arguments + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + + expect(requests.length).to.equal(1); + + expect(requests[0].method).to.equal('GET'); + + const url = parseURL(requests[0].url); + + expect(url.protocol).to.equal('https'); + expect(url.hostname).to.equal('content.mql5.com'); + expect(url.pathname).to.equal('/tr'); + expect(url.search.id).to.equal(clientId); + expect(decodeURIComponent(url.search.event)).to.equal(`Bid Timeout Bidder789`); + expect(url.search.value).to.equal(String(timeout)); + expect(url.search.unit).to.equal('ms'); + + sinon.assert.callCount(fntzAnalyticsAdapter.track, 1); + }); + }); + }); +}); diff --git a/test/spec/modules/freeWheelAdserverVideo_spec.js b/test/spec/modules/freeWheelAdserverVideo_spec.js new file mode 100644 index 00000000000..f958a2733db --- /dev/null +++ b/test/spec/modules/freeWheelAdserverVideo_spec.js @@ -0,0 +1,266 @@ +import { expect } from 'chai'; +import { adpodUtils } from 'modules/freeWheelAdserverVideo'; +import { auctionManager } from 'src/auctionManager'; +import { config } from 'src/config'; + +describe('freeWheel adserver module', function() { + let amStub; + let amGetAdUnitsStub; + let xhr; + let requests; + + before(function () { + let adUnits = [{ + code: 'preroll_1', + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 60, + durationRangeSec: [15, 30], + requireExactDuration: true + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: 14542875, + } + } + ] + }, { + code: 'midroll_1', + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 60, + durationRangeSec: [15, 30], + requireExactDuration: true + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: 14542875, + } + } + ] + }]; + + amGetAdUnitsStub = sinon.stub(auctionManager, 'getAdUnits'); + amGetAdUnitsStub.returns(adUnits); + amStub = sinon.stub(auctionManager, 'getBidsReceived'); + }); + + beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + + config.setConfig({ + adpod: { + brandCategoryExclusion: false, + deferCaching: false + } + }); + }) + + afterEach(function() { + config.resetConfig(); + xhr.restore(); + }); + + after(function () { + amGetAdUnitsStub.restore(); + amStub.restore(); + }); + + it('should return targeting for all adunits', function() { + amStub.returns(getBidsReceived()); + let targeting; + adpodUtils.getTargeting({ + callback: function(errorMsg, targetingResult) { + targeting = targetingResult; + } + }); + + expect(targeting['preroll_1'].length).to.equal(3); + expect(targeting['midroll_1'].length).to.equal(3); + }); + + it('should return targeting for passed adunit code', function() { + amStub.returns(getBidsReceived()); + let targeting; + adpodUtils.getTargeting({ + codes: ['preroll_1'], + callback: function(errorMsg, targetingResult) { + targeting = targetingResult; + } + }); + + expect(targeting['preroll_1']).to.exist; + expect(targeting['midroll_1']).to.not.exist; + }); + + it('should only use adpod bids', function() { + let bannerBid = [{ + 'ad': 'creative', + 'cpm': '1.99', + 'width': 300, + 'height': 250, + 'requestId': '1', + 'creativeId': 'some-id', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + 'bidderCode': 'appnexus', + 'statusMessage': 'Bid available', + 'adId': '28f24ced14586c', + 'adUnitCode': 'preroll_1' + }]; + amStub.returns(getBidsReceived().concat(bannerBid)); + let targeting; + adpodUtils.getTargeting({ + callback: function(errorMsg, targetingResult) { + targeting = targetingResult; + } + }); + + expect(targeting['preroll_1'].length).to.equal(3); + expect(targeting['midroll_1'].length).to.equal(3); + }); + + it('should return unique category bids when competitive exclusion is enabled', function() { + config.setConfig({ + adpod: { + brandCategoryExclusion: true, + deferCaching: false + } + }); + amStub.returns([ + createBid(10, 'preroll_1', 30, '10.00_395_30s', '123', '395'), + createBid(15, 'preroll_1', 30, '15.00_395_30s', '123', '395'), + createBid(15, 'midroll_1', 60, '15.00_406_60s', '123', '406'), + createBid(10, 'preroll_1', 30, '10.00_395_30s', '123', '395') + ]); + let targeting; + adpodUtils.getTargeting({ + callback: function(errorMsg, targetingResult) { + targeting = targetingResult; + } + }); + + expect(targeting['preroll_1'].length).to.equal(3); + expect(targeting['midroll_1'].length).to.equal(2); + }); + + it('should only select bids less than adpod duration', function() { + amStub.returns([ + createBid(10, 'preroll_1', 90, '10.00_395_90s', '123', '395'), + createBid(15, 'preroll_1', 90, '15.00_395_90s', '123', '395'), + createBid(15, 'midroll_1', 90, '15.00_406_90s', '123', '406') + ]); + let targeting; + adpodUtils.getTargeting({ + callback: function(errorMsg, targetingResult) { + targeting = targetingResult; + } + }); + + expect(targeting['preroll_1']).to.be.empty; + expect(targeting['midroll_1']).to.be.empty; + }); + + it('should select bids when deferCaching is enabled', function() { + config.setConfig({ + adpod: { + deferCaching: true + } + }); + amStub.returns(getBidsReceived()); + let targeting; + adpodUtils.getTargeting({ + callback: function(errorMsg, targetingResult) { + targeting = targetingResult; + } + }); + + requests[0].respond( + 200, + { 'Content-Type': 'text/plain' }, + JSON.stringify({'responses': getBidsReceived().slice(0, 4)}) + ); + + expect(targeting['preroll_1'].length).to.equal(3); + expect(targeting['midroll_1'].length).to.equal(3); + }); +}); + +function getBidsReceived() { + return [ + createBid(10, 'preroll_1', 15, '10.00_395_15s', '123', '395'), + createBid(15, 'preroll_1', 15, '15.00_395_15s', '123', '395'), + createBid(15, 'midroll_1', 30, '15.00_406_30s', '123', '406'), + createBid(5, 'midroll_1', 5, '5.00_406_5s', '123', '406'), + createBid(20, 'midroll_1', 60, '20.00_406_60s', '123', '406'), + ] +} + +function createBid(cpm, adUnitCode, durationBucket, priceIndustryDuration, uuid, industry) { + return { + 'bidderCode': 'appnexus', + 'width': 640, + 'height': 360, + 'statusMessage': 'Bid available', + 'adId': '28f24ced14586c', + 'mediaType': 'video', + 'source': 'client', + 'requestId': '28f24ced14586c', + 'cpm': cpm, + 'creativeId': 97517771, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 3600, + 'adUnitCode': adUnitCode, + 'video': { + 'context': 'adpod', + 'durationBucket': durationBucket + }, + 'appnexus': { + 'buyerMemberId': 9325 + }, + 'vastUrl': 'http://some-vast-url.com', + 'vastImpUrl': 'http://some-vast-imp-url.com', + 'auctionId': 'ec266b31-d652-49c5-8295-e83fafe5532b', + 'responseTimestamp': 1548442460888, + 'requestTimestamp': 1548442460827, + 'bidder': 'appnexus', + 'timeToRespond': 61, + 'pbLg': '5.00', + 'pbMg': '5.00', + 'pbHg': '5.00', + 'pbAg': '5.00', + 'pbDg': '5.00', + 'pbCg': '', + 'size': '640x360', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '28f24ced14586c', + 'hb_pb': '5.00', + 'hb_size': '640x360', + 'hb_source': 'client', + 'hb_format': 'video', + 'hb_pb_cat_dur': priceIndustryDuration, + 'hb_cache_id': uuid + }, + 'customCacheKey': `${priceIndustryDuration}_${uuid}`, + 'meta': { + 'iabSubCatId': 'iab-1', + 'adServerCatId': industry + }, + 'videoCacheKey': '4cf395af-8fee-4960-af0e-88d44e399f14' + } +} diff --git a/test/spec/modules/freewheelSSPBidAdapter_spec.js b/test/spec/modules/freewheel-sspBidAdapter_spec.js similarity index 51% rename from test/spec/modules/freewheelSSPBidAdapter_spec.js rename to test/spec/modules/freewheel-sspBidAdapter_spec.js index 33bd647efaa..45754d0250c 100644 --- a/test/spec/modules/freewheelSSPBidAdapter_spec.js +++ b/test/spec/modules/freewheel-sspBidAdapter_spec.js @@ -1,191 +1,239 @@ -import { expect } from 'chai'; -import { spec } from 'modules/freewheelSSPBidAdapter'; -import { newBidder } from 'src/adapters/bidderFactory'; - -const ENDPOINT = '//ads.stickyadstv.com/www/delivery/swfIndex.php'; - -describe('freewheelSSP BidAdapter Test', () => { - const adapter = newBidder(spec); - - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', () => { - let bid = { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }; - - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', () => { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - wrong: 'missing zone id' - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', () => { - let bidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should add parameters to the tag', () => { - const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload.reqType).to.equal('AdsSetup'); - expect(payload.protocolVersion).to.equal('2.0'); - expect(payload.zoneId).to.equal('277225'); - expect(payload.componentId).to.equal('mustang'); - expect(payload.playerSize).to.equal('300x600'); - }); - - it('sends bid request to ENDPOINT via GET', () => { - const request = spec.buildRequests(bidRequests); - expect(request.url).to.contain(ENDPOINT); - expect(request.method).to.equal('GET'); - }); - }) - - describe('interpretResponse', () => { - let bidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - let formattedBidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225', - 'format': 'floorad' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[600, 250], [300, 600]], - 'bidId': '30b3other1c1838de1e', - 'bidderRequestId': '22edbae273other3bf6', - 'auctionId': '1d1a03079test0a475', - }, - { - 'bidder': 'stickyadstv', - 'params': { - 'zoneId': '277225', - 'format': 'test' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 600]], - 'bidId': '2', - 'bidderRequestId': '3', - 'auctionId': '4', - } - ]; - - let response = '' + - '' + - ' ' + - ' Adswizz' + - ' ' + - ' ' + - ' ' + - ' 00:00:09' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' 0.2000' + - ' ' + - ' ' + - ' ' + - ''; - - let ad = '
    '; - let formattedAd = '
    '; - - it('should get correct bid response', () => { - var request = spec.buildRequests(bidRequests); - - let expectedResponse = [ - { - requestId: '30b31c1838de1e', - cpm: '0.2000', - width: 300, - height: 600, - creativeId: '28517153', - currency: 'EUR', - netRevenue: true, - ttl: 360, - ad: ad - } - ]; - - let result = spec.interpretResponse(response, request); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('should get correct bid response with formated ad', () => { - var request = spec.buildRequests(formattedBidRequests); - - let expectedResponse = [ - { - requestId: '30b31c1838de1e', - cpm: '0.2000', - width: 300, - height: 600, - creativeId: '28517153', - currency: 'EUR', - netRevenue: true, - ttl: 360, - ad: formattedAd - } - ]; - - let result = spec.interpretResponse(response, request); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', () => { - var reqest = spec.buildRequests(formattedBidRequests); - let response = ''; - - let result = spec.interpretResponse(response, reqest); - expect(result.length).to.equal(0); - }); - }); -}); +import { expect } from 'chai'; +import { spec } from 'modules/freewheel-sspBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//ads.stickyadstv.com/www/delivery/swfIndex.php'; + +describe('freewheel-ssp BidAdapter Test', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'freewheel-ssp', + 'params': { + 'zoneId': '277225' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + wrong: 'missing zone id' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'freewheel-ssp', + 'params': { + 'zoneId': '277225', + 'gdpr_consented_providers': '123,345', + 'vastUrlParams': {'ownerId': 'kombRJ'} + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'gdprConsent': { + 'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + 'gdprApplies': true + } + } + ]; + + it('should add parameters to the tag', function () { + const request = spec.buildRequests(bidRequests, bidRequests[0]); + const payload = request.data; + expect(payload.reqType).to.equal('AdsSetup'); + expect(payload.protocolVersion).to.equal('2.0'); + expect(payload.zoneId).to.equal('277225'); + expect(payload.componentId).to.equal('mustang'); + expect(payload.ownerId).to.equal('kombRJ'); + expect(payload.playerSize).to.equal('300x600'); + expect(payload._fw_gdpr).to.equal(true); + expect(payload._fw_gdpr_consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + expect(payload._fw_gdpr_consented_providers).to.equal('123,345'); + }); + + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests, bidRequests[0]); + expect(request.url).to.contain(ENDPOINT); + expect(request.method).to.equal('GET'); + }); + }); + + describe('interpretResponse - formated', function () { + let intextBidRequests = [ + { + 'bidder': 'freewheel-ssp', + 'params': { + 'zoneId': '277225', + 'format': 'intext-roll' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[600, 250], [300, 600]], + 'bidId': '30b3other1c1838de1e', + 'bidderRequestId': '22edbae273other3bf6', + 'auctionId': '1d1a03079test0a475', + }, + { + 'bidder': 'stickyadstv', + 'params': { + 'zoneId': '277225', + 'format': 'test' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 600]], + 'bidId': '2', + 'bidderRequestId': '3', + 'auctionId': '4', + } + ]; + + let expandBidRequests = [ + { + 'bidder': 'freewheel-ssp', + 'params': { + 'zoneId': '277225', + 'format': 'expand-banner', + 'vastUrlParams': {'ownerId': 'kombRJ'} + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[600, 250], [300, 600]], + 'bidId': '30b3other1c1838de1e', + 'bidderRequestId': '22edbae273other3bf6', + 'auctionId': '1d1a03079test0a475', + } + ]; + + let bannerBidRequests = [ + { + 'bidder': 'freewheel-ssp', + 'params': { + 'zoneId': '277225' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[600, 250], [300, 600]], + 'bidId': '30b3other1c1838de1e', + 'bidderRequestId': '22edbae273other3bf6', + 'auctionId': '1d1a03079test0a475', + } + ]; + + let response = '' + + '' + + ' ' + + ' Adswizz' + + ' ' + + ' ' + + ' ' + + ' 00:00:09' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 0.2000' + + ' ' + + ' ' + + ' ' + + ''; + + let ad = '
    '; + let intextAd = '
    '; + let expandAd = '
    '; + + it('should get correct intext bid response', function () { + var request = spec.buildRequests(bannerBidRequests, bannerBidRequests[0]); + + let expectedResponse = [ + { + requestId: '30b31c1838de1e', + cpm: '0.2000', + width: 300, + height: 600, + creativeId: '28517153', + currency: 'EUR', + netRevenue: true, + ttl: 360, + ad: ad + } + ]; + + let result = spec.interpretResponse(response, request); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('should get correct expand bid response', function () { + var request = spec.buildRequests(expandBidRequests, expandBidRequests[0]); + + let expectedResponse = [ + { + requestId: '30b31c1838de1e', + cpm: '0.2000', + width: 300, + height: 600, + creativeId: '28517153', + currency: 'EUR', + netRevenue: true, + ttl: 360, + ad: expandAd + } + ]; + + let result = spec.interpretResponse(response, request); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('should get correct bid response with formated ad', function () { + var request = spec.buildRequests(intextBidRequests, intextBidRequests[0]); + + let expectedResponse = [ + { + requestId: '30b31c1838de1e', + cpm: '0.2000', + width: 300, + height: 600, + creativeId: '28517153', + currency: 'EUR', + netRevenue: true, + ttl: 360, + ad: intextAd + } + ]; + + let result = spec.interpretResponse(response, request); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + var reqest = spec.buildRequests(intextBidRequests, intextBidRequests[0]); + let response = ''; + + let result = spec.interpretResponse(response, reqest); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/fyberBidAdapter_spec.js b/test/spec/modules/fyberBidAdapter_spec.js new file mode 100644 index 00000000000..a16c069d2a0 --- /dev/null +++ b/test/spec/modules/fyberBidAdapter_spec.js @@ -0,0 +1,154 @@ +import { expect } from 'chai'; +import { spec } from 'modules/fyberBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import bidRequest from '../../fixtures/video/bidRequest.json'; + +const bidId = '21b2499bf34cf8'; +const mock = { + bid: { + bidder: 'fyber', + params: { + appId: 'MyCompany_MyApp', + spotType: 'rectangle', + gdprPrivacyConsent: true, + qa: { + // url: 'http://ia-test08.inner-active.mobi:8080/simpleM2M/requestJsonAd', + cpm: 10 + }, + customParams: { + // referrer: 'referrer', + // page: 'aaaa', + portal: 7002 + // KEYWORDS: 'bbb' + } + } + }, + bidsRequest: [ + { + adUnitCode: '/19968336/header-bid-tag-1', + auctionId: 'f270d8dd-29c6-4aca-8648-7d722590b899', + bidId, + bidder: 'fyber', + bidderRequestId: '1bcd667e09f48e', + params: { + spotType: 'rectangle', + gdprPrivacyConsent: true, + qa: {cpm: 10}, + customParams: {portal: 7002}, + appId: 'MyCompany_MyApp' + }, + sizes: [[300, 250], [300, 600]], + transactionId: 'a0253346-df4e-4f1a-b004-1f50e8e6af69' + } + ], + validResponse: { + body: { + ad: { + html: '

    Fyber Ad

    ' + }, + config: { + tracking: { + clicks: ['c1'], + impressions: ['i1'] + } + } + }, + headers: { + get(headerName) { + if (headerName === 'X-IA-Pricing-Value') { + return 10; + } + return headerName; + } + } + }, + invalidResponse: { + body: {}, + headers: { + get(headerName) { + return headerName; + } + } + } +}; + +describe('FyberAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('callBids exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('Verifies bidder code', function () { + it('Verifies bidder code', function () { + expect(spec.code).to.equal('fyber'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + const bid = Object.assign({}, mock.bid); + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params{spotType} not found', function () { + const bid = Object.assign({}, mock.bid); + delete bid.params.spotType; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required{appId} params not found', function () { + const bid = Object.assign({}, mock.bid); + delete bid.params.appId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidsRequest = Object.assign([], mock.bidsRequest); + const requests = spec.buildRequests(bidsRequest); + + it('Verify only one build request', function () { + expect(requests.length).to.equal(1); + }); + + const request = requests[0]; + + it('Verify build request http method', function () { + expect(request.method).to.equal('GET'); + }); + + it('Verify build request bidId', function () { + expect(request.bidId).to.equal(bidId); + }); + }); + + describe('interpretResponse', function () { + const request = Object.assign([], mock.bidsRequest)[0]; + const validResponse = Object.assign({}, mock.validResponse); + const validResult = spec.interpretResponse(validResponse, request); + + it('Verify only one bid response', function () { + expect(validResult.length).to.equal(1); + }); + + const bidResponse = validResult[0]; + + it('Verify CPM', function () { + expect(bidResponse.cpm).to.equal(10000); + }); + + it('Verify requestId', function () { + expect(bidResponse.requestId).to.equal(bidId); + }); + + const invalidResponse = Object.assign({}, mock.invalidResponse); + const invalidResult = spec.interpretResponse(invalidResponse, request); + + it('Verify empty bid response', function () { + expect(invalidResult.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/gammaBidAdapter_spec.js b/test/spec/modules/gammaBidAdapter_spec.js index 1f3f225336e..99593e6e06f 100644 --- a/test/spec/modules/gammaBidAdapter_spec.js +++ b/test/spec/modules/gammaBidAdapter_spec.js @@ -6,92 +6,78 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('gammaBidAdapter', function() { const adapter = newBidder(spec); - describe('isBidRequestValid', () => { - let bid = { - 'bidder': 'gamma', - 'params': { - siteId: '1465446377', - zoneId: '1515999290' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - }; + let bid = { + 'bidder': 'gamma', + 'params': { + siteId: '1465446377', + zoneId: '1515999290' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + }; + let bidArray = [bid]; - it('should return true when required params found', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when require params are not passed', () => { + it('should return false when require params are not passed', function () { let bid = Object.assign({}, bid); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when params not passed correctly', () => { + it('should return false when params not passed correctly', function () { bid.params.siteId = ''; bid.params.zoneId = ''; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests', () => { - let bidRequests = [ - { - 'bidder': 'gamma', - 'params': { - siteId: '1465446377', - zoneId: '1515999290' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1' - } - ]; - - const request = spec.buildRequests(bidRequests); - - it('sends bid request to our endpoint via GET', () => { - expect(request.method).to.equal('GET'); - }); - - it('bidRequest url', () => { - expect(request.url).to.match(new RegExp(`hb.gammaplatform.com`)); + describe('buildRequests', function () { + it('should attempt to send bid requests to the endpoint via GET', function () { + const requests = spec.buildRequests(bidArray); + requests.forEach(function(requestItem) { + expect(requestItem.method).to.equal('GET'); + expect(requestItem.url).to.match(new RegExp(`hb.gammaplatform.com`)); + }); }); }); - describe('interpretResponse', () => { - let serverResponse = { - body: { - 'id': '23beaa6af6cdde', - 'bid': '5611802021800040585', - 'type': 'banner', - 'cur': 'USD', - 'seatbid': [{ - 'seat': '5611802021800040585', - 'bid': [{ - 'id': '1515999070', - 'impid': '1', - 'price': 0.45, - 'adm': '', - 'adid': '1515999070', - 'dealid': 'gax-paj2qarjf2g', - 'h': 250, - 'w': 300 + describe('interpretResponse', function () { + let serverResponse; + + beforeEach(function () { + serverResponse = { + body: { + 'id': '23beaa6af6cdde', + 'bid': '5611802021800040585', + 'type': 'banner', + 'cur': 'USD', + 'seatbid': [{ + 'seat': '5611802021800040585', + 'bid': [{ + 'id': '1515999070', + 'impid': '1', + 'price': 0.45, + 'adm': '', + 'adid': '1515999070', + 'dealid': 'gax-paj2qarjf2g', + 'h': 250, + 'w': 300 + }] }] - }] - } - }; + } + }; + }) - it('should get the correct bid response', () => { + it('should get the correct bid response', function () { let expectedResponse = [{ 'requestId': '23beaa6af6cdde', 'cpm': 0.45, @@ -108,7 +94,7 @@ describe('gammaBidAdapter', function() { expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); }); - it('handles empty bid response', () => { + it('handles empty bid response', function () { let response = { body: {} }; diff --git a/test/spec/modules/gamoshiBidAdapter_spec.js b/test/spec/modules/gamoshiBidAdapter_spec.js new file mode 100644 index 00000000000..a2c4eebc213 --- /dev/null +++ b/test/spec/modules/gamoshiBidAdapter_spec.js @@ -0,0 +1,507 @@ +import {expect} from 'chai'; +import {spec} from 'modules/gamoshiBidAdapter'; +import {helper} from 'modules/gamoshiBidAdapter'; +import * as utils from 'src/utils'; +import {newBidder} from '../../../src/adapters/bidderFactory'; +import {deepClone} from 'src/utils'; + +const supplyPartnerId = '123'; +const adapter = newBidder(spec); +describe('GamoshiAdapter', function () { + describe('Get top Frame', function () { + it('check if you are in the top frame', function () { + expect(helper.getTopFrame()).to.equal(0); + }); + it('verify domain parsing', function () { + expect(helper.getTopWindowDomain('http://www.domain.com')).to.equal('www.domain.com'); + }); + }); + describe('Is String start with search ', function () { + it('check if a string started with', function () { + expect(helper.startsWith('gamoshi.com', 'gamo')).to.equal(true); + }); + }); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should validate supply-partner ID', function () { + expect(spec.isBidRequestValid({params: {}})).to.equal(false); + expect(spec.isBidRequestValid({params: {supplyPartnerId: 123}})).to.equal(false); + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); + }); + + it('should validate RTB endpoint', function () { + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // RTB endpoint has a default + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', rtbEndpoint: 123}})).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + supplyPartnerId: '123', + rtbEndpoint: 'https://some.url.com' + } + })).to.equal(true); + }); + + it('should validate bid floor', function () { + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // bidfloor has a default + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: '123'}})).to.equal(false); + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: 0.1}})).to.equal(true); + }); + + it('should validate adpos', function () { + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // adpos has a default + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', adpos: '123'}})).to.equal(false); + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', adpos: 0.1}})).to.equal(true); + }); + + it('should validate instl', function () { + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // adpos has a default + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: '123'}})).to.equal(false); + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: -1}})).to.equal(false); + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: 0}})).to.equal(true); + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: 1}})).to.equal(true); + expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: 2}})).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequest = { + 'adUnitCode': 'adunit-code', + 'auctionId': '1d1a030790a475', + 'mediaTypes': { + banner: {} + }, + 'params': { + 'supplyPartnerId': supplyPartnerId + }, + 'sizes': [[300, 250], [300, 600]], + 'transactionId': 'a123456789', + refererInfo: {referer: 'http://examplereferer.com'}, + gdprConsent: { + consentString: 'some string', + gdprApplies: true + } + }; + it('returns an array', function () { + let response; + response = spec.buildRequests([]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + response = spec.buildRequests([bidRequest], bidRequest); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + const adUnit1 = Object.assign({}, utils.deepClone(bidRequest), {auctionId: '1', adUnitCode: 'a'}); + const adUnit2 = Object.assign({}, utils.deepClone(bidRequest), {auctionId: '1', adUnitCode: 'b'}); + response = spec.buildRequests([adUnit1, adUnit2], bidRequest); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(2); + }); + + it('targets correct endpoint', function () { + let response; + response = spec.buildRequests([bidRequest], bidRequest)[0]; + expect(response.method).to.equal('POST'); + expect(response.url).to.match(new RegExp(`^https://rtb\\.gamoshi\\.io/r/${supplyPartnerId}/bidr\\?rformat=open_rtb&reqformat=rtb_json&bidder=prebid$`, 'g')); + expect(response.data.id).to.equal(bidRequest.auctionId); + const bidRequestWithEndpoint = utils.deepClone(bidRequest); + bidRequestWithEndpoint.params.rtbEndpoint = 'https://rtb2.gamoshi.io/a12'; + response = spec.buildRequests([bidRequestWithEndpoint], bidRequest)[0]; + expect(response.url).to.match(new RegExp(`^https://rtb2\\.gamoshi\\.io/a12/r/${supplyPartnerId}/bidr\\?rformat=open_rtb&reqformat=rtb_json&bidder=prebid$`, 'g')); + }); + + it('builds request correctly', function () { + let stub = sinon.stub(utils, 'getTopWindowUrl').returns('http://www.test.com/page.html'); + let bidRequest2 = deepClone(bidRequest); + bidRequest2.refererInfo.referer = 'http://www.test.com/page.html'; + let response = spec.buildRequests([bidRequest], bidRequest2)[0]; + expect(response.data.site.domain).to.equal('www.test.com'); + expect(response.data.site.page).to.equal('http://www.test.com/page.html'); + expect(response.data.site.ref).to.equal('http://www.test.com/page.html'); + expect(response.data.imp.length).to.equal(1); + expect(response.data.imp[0].id).to.equal(bidRequest.transactionId); + expect(response.data.imp[0].instl).to.equal(0); + expect(response.data.imp[0].tagid).to.equal(bidRequest.adUnitCode); + expect(response.data.imp[0].bidfloor).to.equal(0); + expect(response.data.imp[0].bidfloorcur).to.equal('USD'); + const bidRequestWithInstlEquals1 = utils.deepClone(bidRequest); + bidRequestWithInstlEquals1.params.instl = 1; + response = spec.buildRequests([bidRequestWithInstlEquals1], bidRequest2)[0]; + expect(response.data.imp[0].instl).to.equal(bidRequestWithInstlEquals1.params.instl); + const bidRequestWithInstlEquals0 = utils.deepClone(bidRequest); + bidRequestWithInstlEquals0.params.instl = 1; + response = spec.buildRequests([bidRequestWithInstlEquals0], bidRequest2)[0]; + expect(response.data.imp[0].instl).to.equal(bidRequestWithInstlEquals0.params.instl); + const bidRequestWithBidfloorEquals1 = utils.deepClone(bidRequest); + bidRequestWithBidfloorEquals1.params.bidfloor = 1; + response = spec.buildRequests([bidRequestWithBidfloorEquals1], bidRequest2)[0]; + expect(response.data.imp[0].bidfloor).to.equal(bidRequestWithBidfloorEquals1.params.bidfloor); + stub.restore(); + }); + + it('builds request banner object correctly', function () { + let response; + const bidRequestWithBanner = utils.deepClone(bidRequest); + bidRequestWithBanner.mediaTypes = { + banner: { + sizes: [[300, 250], [120, 600]] + } + }; + response = spec.buildRequests([bidRequestWithBanner], bidRequest)[0]; + expect(response.data.imp[0].banner.w).to.equal(bidRequestWithBanner.mediaTypes.banner.sizes[0][0]); + expect(response.data.imp[0].banner.h).to.equal(bidRequestWithBanner.mediaTypes.banner.sizes[0][1]); + expect(response.data.imp[0].banner.pos).to.equal(0); + expect(response.data.imp[0].banner.topframe).to.equal(0); + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithBanner); + bidRequestWithPosEquals1.params.pos = 1; + response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; + expect(response.data.imp[0].banner.pos).to.equal(bidRequestWithPosEquals1.params.pos); + }); + + it('builds request video object correctly', function () { + let response; + const bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes = { + video: { + playerSize: [302, 252] + } + }; + response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[0].video.w).to.equal(bidRequestWithVideo.mediaTypes.video.playerSize[0][0]); + expect(response.data.imp[0].video.h).to.equal(bidRequestWithVideo.mediaTypes.video.playerSize[0][1]); + expect(response.data.imp[0].video.pos).to.equal(0); + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals1.params.pos = 1; + response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; + expect(response.data.imp[0].video.pos).to.equal(bidRequestWithPosEquals1.params.pos); + }); + + it('builds request video object correctly with context', function () { + let response; + const bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes = { + video: { + context: 'instream' + } + }; + response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[0].video.ext.context).to.equal('instream'); + bidRequestWithVideo.mediaTypes.video.context = 'outstream'; + + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals1.mediaTypes.video.context = 'outstream'; + response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; + expect(response.data.imp[0].video.ext.context).to.equal('outstream'); + + const bidRequestWithPosEquals2 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals2.mediaTypes.video.context = null; + response = spec.buildRequests([bidRequestWithPosEquals2], bidRequest)[0]; + expect(response.data.imp[0].video.ext.context).to.equal(null); + }); + + it('builds request video object correctly with multi-dimensions size array', function () { + let response; + const bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes.video = { + playerSize: [[304, 254], [305, 255]], + context: 'instream' + }; + + response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[1].video.ext.context).to.equal('instream'); + bidRequestWithVideo.mediaTypes.video.context = 'outstream'; + + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals1.mediaTypes.video.context = 'outstream'; + response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; + expect(response.data.imp[1].video.ext.context).to.equal('outstream'); + + const bidRequestWithPosEquals2 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals2.mediaTypes.video.context = null; + response = spec.buildRequests([bidRequestWithPosEquals2], bidRequest)[0]; + expect(response.data.imp[1].video.ext.context).to.equal(null); + }); + + it('builds request with gdpr consent', function () { + let response = spec.buildRequests([bidRequest], bidRequest)[0]; + expect(response.data.ext).to.have.property('gdpr_consent'); + expect(response.data.ext.gdpr_consent.consent_string).to.equal('some string'); + expect(response.data.ext.gdpr_consent.consent_required).to.equal(true); + }); + }); + + describe('interpretResponse', function () { + const bannerBidRequest = { + 'adUnitCode': 'adunit-code', + 'auctionId': '1d1a030790a475', + 'mediaTypes': { + banner: {} + }, + 'params': { + 'supplyPartnerId': supplyPartnerId + }, + 'sizes': [[300, 250], [300, 600]], + 'transactionId': 'a123456789', + 'bidId': '111', + refererInfo: {referer: 'http://examplereferer.com'} + }; + + const videoBidRequest = { + 'adUnitCode': 'adunit-code', + 'auctionId': '1d1a030790a475', + 'mediaTypes': { + video: {} + }, + 'params': { + 'supplyPartnerId': supplyPartnerId + }, + 'sizes': [[300, 250], [300, 600]], + 'transactionId': 'a123456789', + 'bidId': '111', + refererInfo: {referer: 'http://examplereferer.com'} + }; + + const rtbResponse = { + 'id': 'imp_5b05b9fde4b09084267a556f', + 'bidid': 'imp_5b05b9fde4b09084267a556f', + 'cur': 'USD', + 'ext': { + 'utrk': [ + {'type': 'iframe', 'url': '//rtb.gamoshi.io/user/sync/1'}, + {'type': 'image', 'url': '//rtb.gamoshi.io/user/sync/2'} + ] + }, + 'seatbid': [ + { + 'seat': 'seat1', + 'group': 0, + 'bid': [ + { + 'id': '0', + 'impid': '1', + 'price': 2.016, + 'adid': '579ef31bfa788b9d2000d562', + 'nurl': 'https://rtb.gamoshi.io/pix/monitoring/win_notice/imp_5b05b9fde4b09084267a556f/im.gif?r=imp_5b05b9fde4b09084267a556f&i=1&a=579ef31bfa788b9d2000d562&b=0&p=${AUCTION_PRICE}', + 'adm': '', + 'adomain': ['aaa.com'], + 'cid': '579ef268fa788b9d2000d55c', + 'crid': '579ef31bfa788b9d2000d562', + 'attr': [], + 'h': 600, + 'w': 120, + 'ext': { + 'vast_url': 'http://my.vast.com', + 'utrk': [ + {'type': 'iframe', 'url': '//p.partner1.io/user/sync/1'} + ] + } + } + ] + }, + { + 'seat': 'seat2', + 'group': 0, + 'bid': [ + { + 'id': '1', + 'impid': '1', + 'price': 3, + 'adid': '542jlhdfd2112jnjf3x', + 'nurl': 'https://rtb.gamoshi.io/pix/monitoring/win_notice/imp_5b05b9fde4b09084267a556f/im.gif?r=imp_5b05b9fde4b09084267a556f&i=1&a=579ef31bfa788b9d2000d562&b=0&p=${AUCTION_PRICE}', + 'adm': ' ', + 'adomain': ['bbb.com'], + 'cid': 'fgdlwjh2498ydjhg1', + 'crid': 'kjh34297ydh2133d', + 'attr': [], + 'h': 250, + 'w': 300, + 'ext': { + 'utrk': [ + {'type': 'image', 'url': '//p.partner2.io/user/sync/1'} + ] + } + } + ] + } + ] + }; + + const TTL = 360; + + it('returns an empty array on missing response', function () { + let response; + + response = spec.interpretResponse(undefined, {bidRequest: bannerBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + + response = spec.interpretResponse({}, {bidRequest: bannerBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + }); + + it('aggregates banner bids from all seat bids', function () { + const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: bannerBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + const ad0 = response[0]; + expect(ad0.requestId).to.equal(bannerBidRequest.bidId); + expect(ad0.cpm).to.equal(rtbResponse.seatbid[1].bid[0].price); + expect(ad0.width).to.equal(rtbResponse.seatbid[1].bid[0].w); + expect(ad0.height).to.equal(rtbResponse.seatbid[1].bid[0].h); + expect(ad0.ttl).to.equal(TTL); + expect(ad0.creativeId).to.equal(rtbResponse.seatbid[1].bid[0].crid); + expect(ad0.netRevenue).to.equal(true); + expect(ad0.currency).to.equal(rtbResponse.seatbid[1].bid[0].cur || rtbResponse.cur || 'USD'); + expect(ad0.ad).to.equal(rtbResponse.seatbid[1].bid[0].adm); + expect(ad0.vastXml).to.be.an('undefined'); + expect(ad0.vastUrl).to.be.an('undefined'); + }); + + it('aggregates video bids from all seat bids', function () { + const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: videoBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + const ad0 = response[0]; + expect(ad0.requestId).to.equal(videoBidRequest.bidId); + expect(ad0.cpm).to.equal(rtbResponse.seatbid[0].bid[0].price); + expect(ad0.width).to.equal(rtbResponse.seatbid[0].bid[0].w); + expect(ad0.height).to.equal(rtbResponse.seatbid[0].bid[0].h); + expect(ad0.ttl).to.equal(TTL); + expect(ad0.creativeId).to.equal(rtbResponse.seatbid[0].bid[0].crid); + expect(ad0.netRevenue).to.equal(true); + expect(ad0.currency).to.equal(rtbResponse.seatbid[0].bid[0].cur || rtbResponse.cur || 'USD'); + expect(ad0.ad).to.be.an('undefined'); + expect(ad0.vastXml).to.equal(rtbResponse.seatbid[0].bid[0].adm); + expect(ad0.vastUrl).to.equal(rtbResponse.seatbid[0].bid[0].ext.vast_url); + }); + + it('aggregates user-sync pixels', function () { + const response = spec.getUserSyncs({}, [{body: rtbResponse}]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(4); + expect(response[0].type).to.equal(rtbResponse.ext.utrk[0].type); + expect(response[0].url).to.equal(rtbResponse.ext.utrk[0].url + '?gc=missing'); + expect(response[1].type).to.equal(rtbResponse.ext.utrk[1].type); + expect(response[1].url).to.equal(rtbResponse.ext.utrk[1].url + '?gc=missing'); + expect(response[2].type).to.equal(rtbResponse.seatbid[0].bid[0].ext.utrk[0].type); + expect(response[2].url).to.equal(rtbResponse.seatbid[0].bid[0].ext.utrk[0].url + '?gc=missing'); + expect(response[3].type).to.equal(rtbResponse.seatbid[1].bid[0].ext.utrk[0].type); + expect(response[3].url).to.equal(rtbResponse.seatbid[1].bid[0].ext.utrk[0].url + '?gc=missing'); + }); + + it('supports configuring outstream renderers', function () { + const videoResponse = { + 'id': '64f32497-b2f7-48ec-9205-35fc39894d44', + 'bidid': 'imp_5c24924de4b0d106447af333', + 'cur': 'USD', + 'seatbid': [ + { + 'seat': '3668', + 'group': 0, + 'bid': [ + { + 'id': 'gb_1', + 'impid': 'afbb5852-7cea-4a81-aa9a-a41aab505c23', + 'price': 5.0, + 'adid': '1274', + 'nurl': 'https://rtb.gamoshi.io/pix/1275/win_notice/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&p=${AUCTION_PRICE}', + 'adomain': [], + 'adm': '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', + 'cid': '3668', + 'crid': '1274', + 'cat': [], + 'attr': [], + 'h': 250, + 'w': 300, + 'ext': { + 'vast_url': 'https://rtb.gamoshi.io/pix/1275/vast_o/imp_5c24924de4b0d106447af333/im.xml?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&w=300&h=250&vatu=aHR0cHM6Ly9zdGF0aWMuZ2FtYmlkLmlvL2RlbW8vdmFzdC54bWw&vwarv', + 'imptrackers': [ + 'https://rtb.gamoshi.io/pix/1275/imp/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1'] + } + } + ] + } + ], + 'ext': { + 'utrk': [{ + 'type': 'image', + 'url': 'https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675' + }] + } + }; + const videoRequest = deepClone(videoBidRequest); + videoRequest.mediaTypes.video.context = 'outstream'; + const result = spec.interpretResponse({body: videoResponse}, {bidRequest: videoRequest}); + expect(result[0].renderer).to.not.equal(undefined); + }); + + it('validates in/existing of gdpr consent', function () { + let videoResponse = { + 'id': '64f32497-b2f7-48ec-9205-35fc39894d44', + 'bidid': 'imp_5c24924de4b0d106447af333', + 'cur': 'USD', + 'seatbid': [ + { + 'seat': '3668', + 'group': 0, + 'bid': [ + { + 'id': 'gb_1', + 'impid': 'afbb5852-7cea-4a81-aa9a-a41aab505c23', + 'price': 5.0, + 'adid': '1274', + 'nurl': 'https://rtb.gamoshi.io/pix/1275/win_notice/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&p=${AUCTION_PRICE}', + 'adomain': [], + 'adm': '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', + 'cid': '3668', + 'crid': '1274', + 'cat': [], + 'attr': [], + 'h': 250, + 'w': 300, + 'ext': { + 'vast_url': 'https://rtb.gamoshi.io/pix/1275/vast_o/imp_5c24924de4b0d106447af333/im.xml?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&w=300&h=250&vatu=aHR0cHM6Ly9zdGF0aWMuZ2FtYmlkLmlvL2RlbW8vdmFzdC54bWw&vwarv', + 'imptrackers': [ + 'https://rtb.gamoshi.io/pix/1275/imp/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1'] + } + } + ] + } + ], + 'ext': { + 'utrk': [{ + 'type': 'image', + 'url': 'https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675' + }] + } + }; + let gdprConsent = { + gdprApplies: true, + consentString: 'consent string' + }; + let result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal('https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675&gc=consent%20string'); + + gdprConsent.gdprApplies = false; + result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal('https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675&gc=missing'); + + videoResponse.ext.utrk[0].url = 'https://rtb.gamoshi.io/pix/1275/scm'; + result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal('https://rtb.gamoshi.io/pix/1275/scm?gc=missing'); + }); + }); +}); diff --git a/test/spec/modules/getintentBidAdapter_spec.js b/test/spec/modules/getintentBidAdapter_spec.js index 17f9a95fec4..ebbda7e108e 100644 --- a/test/spec/modules/getintentBidAdapter_spec.js +++ b/test/spec/modules/getintentBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai' import { spec } from 'modules/getintentBidAdapter' -describe('GetIntent Adapter Tests:', () => { +describe('GetIntent Adapter Tests:', function () { const bidRequests = [{ bidId: 'bid12345', params: { @@ -34,7 +34,7 @@ describe('GetIntent Adapter Tests:', () => { mediaType: 'video' }; - it('Verify build request', () => { + it('Verify build request', function () { const serverRequests = spec.buildRequests(bidRequests); let serverRequest = serverRequests[0]; expect(serverRequest.url).to.equal('//px.adhigh.net/rtb/direct_banner'); @@ -48,7 +48,7 @@ describe('GetIntent Adapter Tests:', () => { expect(serverRequest.data.size).to.equal('50x50,100x100'); }); - it('Verify build video request', () => { + it('Verify build video request', function () { const serverRequests = spec.buildRequests([videoBidRequest]); let serverRequest = serverRequests[0]; expect(serverRequest.url).to.equal('//px.adhigh.net/rtb/direct_vast'); @@ -64,7 +64,7 @@ describe('GetIntent Adapter Tests:', () => { expect(serverRequest.data.skippable).to.equal(true); }); - it('Verify parse response', () => { + it('Verify parse response', function () { const serverResponse = { body: { bid_id: 'bid12345', @@ -90,7 +90,7 @@ describe('GetIntent Adapter Tests:', () => { expect(bid.ad).to.equal('Ad markup'); }); - it('Verify parse video response', () => { + it('Verify parse video response', function () { const serverResponse = { body: { bid_id: 'bid789', @@ -116,22 +116,22 @@ describe('GetIntent Adapter Tests:', () => { expect(bid.vastUrl).to.equal('//vast.xml/url'); }); - it('Verify bidder code', () => { + it('Verify bidder code', function () { expect(spec.code).to.equal('getintent'); }); - it('Verify bidder aliases', () => { + it('Verify bidder aliases', function () { expect(spec.aliases).to.have.lengthOf(1); expect(spec.aliases[0]).to.equal('getintentAdapter'); }); - it('Verify supported media types', () => { + it('Verify supported media types', function () { expect(spec.supportedMediaTypes).to.have.lengthOf(2); expect(spec.supportedMediaTypes[0]).to.equal('video'); expect(spec.supportedMediaTypes[1]).to.equal('banner'); }); - it('Verify if bid request valid', () => { + it('Verify if bid request valid', function () { expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); expect(spec.isBidRequestValid(bidRequests[1])).to.equal(true); expect(spec.isBidRequestValid({})).to.equal(false); diff --git a/test/spec/modules/giantsBidAdapter_spec.js b/test/spec/modules/giantsBidAdapter_spec.js new file mode 100644 index 00000000000..bab2415745d --- /dev/null +++ b/test/spec/modules/giantsBidAdapter_spec.js @@ -0,0 +1,301 @@ +import { expect } from 'chai'; +import { spec } from 'modules/giantsBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { deepClone } from 'src/utils'; +import * as utils from 'src/utils'; + +const ENDPOINT = '//d.admp.io/hb/multi?url='; + +describe('GiantsAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'giants', + 'params': { + 'zoneId': '584072408' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'zoneId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'giants', + 'params': { + 'zoneId': '584072408' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should parse out private sizes', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + zoneId: '584072408', + privateSizes: [300, 250] + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].private_sizes).to.exist; + expect(payload.tags[0].private_sizes).to.deep.equal([{width: 300, height: 250}]); + }); + + it('should add source and verison to the tag', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.sdk).to.exist; + expect(payload.sdk).to.deep.equal({ + source: 'pbjs', + version: '$prebid.version$' + }); + }); + + it('should populate the ad_types array on all requests', function () { + ['banner', 'video', 'native'].forEach(type => { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes[type] = {}; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.deep.equal([type]); + }); + }); + + it('should populate the ad_types array on outstream requests', function () { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes.video = {context: 'outstream'}; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.deep.equal(['video']); + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT + utils.getTopWindowUrl()); + expect(request.method).to.equal('POST'); + }); + + it('should attach valid video params to the tag', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + zoneId: '584072408', + video: { + id: 123, + minduration: 100, + foobar: 'invalid' + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ + id: 123, + minduration: 100 + }); + }); + + it('sets minimum native asset params when not provided on adunit', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + image: {required: true}, + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].native.layouts[0]).to.deep.equal({ + main_image: {required: true, sizes: [{}]}, + }); + }); + + it('does not overwrite native ad unit params with mimimum params', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + image: { + aspect_ratios: [{ + min_width: 100, + ratio_width: 2, + ratio_height: 3, + }] + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].native.layouts[0]).to.deep.equal({ + main_image: { + required: true, + aspect_ratios: [{ + min_width: 100, + ratio_width: 2, + ratio_height: 3, + }] + }, + }); + }); + + it('should convert keyword params to proper form and attaches to request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + zoneId: '584072408', + keywords: { + single: 'val', + singleArr: ['val'], + singleArrNum: [5], + multiValMixed: ['value1', 2, 'value3'], + singleValNum: 123, + badValue: {'foo': 'bar'} // should be dropped + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }]); + }); + + it('should add payment rules to the request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + zoneId: '584072408', + usePaymentRule: true + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].use_pmt_rule).to.equal(true); + }); + }) + + describe('interpretResponse', function () { + let response = { + 'version': '3.0.0', + 'tags': [ + { + 'uuid': '3db3773286ee59', + 'creative_id': '584944065', + 'height': 600, + 'width': 300, + 'zoneId': '584072408', + 'adUrl': '//d.admp.io/pbc/v1/cache-banner/f7aca005-8171-4299-90bf-0750a864a61c', + 'cpm': 0.5 + } + ] + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '3db3773286ee59', + 'cpm': 0.5, + 'creativeId': 29681110, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'width': 300, + 'height': 250, + 'ad': '', + 'mediaType': 'banner' + } + ]; + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + let response = { + 'version': '0.0.1', + 'tags': [{ + 'uuid': '84ab500420319d', + 'tag_id': 5976557, + 'auction_id': '297492697822162468', + 'nobid': true + }] + }; + let bidderRequest; + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/gjirafaBidAdapter_spec.js b/test/spec/modules/gjirafaBidAdapter_spec.js index 17fbdc33591..a18ea1b99c3 100644 --- a/test/spec/modules/gjirafaBidAdapter_spec.js +++ b/test/spec/modules/gjirafaBidAdapter_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { spec } from 'modules/gjirafaBidAdapter'; -describe('gjirafaAdapterTest', () => { - describe('bidRequestValidity', () => { - it('bidRequest with placementId, minCPM and minCPC params', () => { +describe('gjirafaAdapterTest', function () { + describe('bidRequestValidity', function () { + it('bidRequest with placementId, minCPM and minCPC params', function () { expect(spec.isBidRequestValid({ bidder: 'gjirafa', params: { @@ -14,7 +14,7 @@ describe('gjirafaAdapterTest', () => { })).to.equal(true); }); - it('bidRequest with only placementId param', () => { + it('bidRequest with only placementId param', function () { expect(spec.isBidRequestValid({ bidder: 'gjirafa', params: { @@ -23,7 +23,7 @@ describe('gjirafaAdapterTest', () => { })).to.equal(true); }); - it('bidRequest with minCPM and minCPC params', () => { + it('bidRequest with minCPM and minCPC params', function () { expect(spec.isBidRequestValid({ bidder: 'gjirafa', params: { @@ -33,7 +33,7 @@ describe('gjirafaAdapterTest', () => { })).to.equal(true); }); - it('bidRequest with no placementId, minCPM or minCPC params', () => { + it('bidRequest with no placementId, minCPM or minCPC params', function () { expect(spec.isBidRequestValid({ bidder: 'gjirafa', params: { @@ -42,7 +42,7 @@ describe('gjirafaAdapterTest', () => { }); }); - describe('bidRequest', () => { + describe('bidRequest', function () { const bidRequests = [{ 'bidder': 'gjirafa', 'params': { @@ -70,14 +70,22 @@ describe('gjirafaAdapterTest', () => { 'auctionId': 'f9012acc-b6b7-4748-9098-97252914f9dc' }]; - it('bidRequest HTTP method', () => { + const bidderRequest = { + 'bids': bidRequests, + 'gdprConsent': { + 'consentString': 'consentString', + 'gdprApplies': true + } + }; + + it('bidRequest HTTP method', function () { const requests = spec.buildRequests(bidRequests); requests.forEach(function(requestItem) { expect(requestItem.method).to.equal('GET'); }); }); - it('bidRequest url', () => { + it('bidRequest url', function () { const endpointUrl = 'https://gjc.gjirafa.com/Home/GetBid'; const requests = spec.buildRequests(bidRequests); requests.forEach(function(requestItem) { @@ -85,21 +93,29 @@ describe('gjirafaAdapterTest', () => { }); }); - it('bidRequest data', () => { + it('bidRequest data', function () { const requests = spec.buildRequests(bidRequests); requests.forEach(function(requestItem) { - expect(requestItem.data).to.exists; + expect(requestItem.data).to.exist; }); }); - it('bidRequest sizes', () => { + it('bidRequest sizes', function () { const requests = spec.buildRequests(bidRequests); expect(requests[0].data.sizes).to.equal('728x90;980x200;980x150;970x90;970x250'); expect(requests[1].data.sizes).to.equal('300x250'); }); + + it('should add GDPR data', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].data.consent_string).to.exist; + expect(requests[0].data.consent_required).to.exist; + expect(requests[1].data.consent_string).to.exist; + expect(requests[1].data.consent_required).to.exist; + }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const bidRequest = { 'method': 'GET', 'url': 'https://gjc.gjirafa.com/Home/GetBid', @@ -131,7 +147,7 @@ describe('gjirafaAdapterTest', () => { headers: {} }; - it('all keys present', () => { + it('all keys present', function () { const result = spec.interpretResponse(bidResponse, bidRequest); let keys = [ diff --git a/test/spec/modules/googleAnalyticsAdapter_spec.js b/test/spec/modules/googleAnalyticsAdapter_spec.js index 4260a831cad..20517f4138d 100644 --- a/test/spec/modules/googleAnalyticsAdapter_spec.js +++ b/test/spec/modules/googleAnalyticsAdapter_spec.js @@ -1,5 +1,6 @@ +import ga from 'modules/googleAnalyticsAdapter'; + var assert = require('assert'); -var ga = require('modules/googleAnalyticsAdapter'); describe('Ga', function () { describe('enableAnalytics', function () { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js new file mode 100644 index 00000000000..a191d2211f5 --- /dev/null +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -0,0 +1,578 @@ +import { expect } from 'chai'; +import { spec } from 'modules/gridBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('TheMediaGrid Adapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'uid': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + function parseRequest(url) { + const res = {}; + url.split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } + const bidderRequest = {refererInfo: {referer: 'http://example.com'}}; + const referrer = bidderRequest.refererInfo.referer; + let bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '2' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should attach valid params to the tag', function () { + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('auids', '1'); + expect(payload).to.have.property('sizes', '300x250,300x600'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + }); + + it('auids must not be duplicated', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('auids', '1,1,2'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + + it('if gdprApplies is false gdpr_applies must be 0', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '0'); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + }); + + describe('interpretResponse', function () { + const responses = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 2, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
    test content 5
    ', 'h': 250, 'w': 300}], 'seat': '1'}, + undefined, + {'bid': [], 'seat': '1'}, + {'seat': '1'}, + ]; + + it('should get correct bid response', function () { + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 1, + 'dealId': 11, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should get correct multi bid response', function () { + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71a5b', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '2' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4dff80cc4ee346', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '5703af74d0472a', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '300bfeb0d71a5b', + 'cpm': 1.15, + 'creativeId': 1, + 'dealId': 11, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + }, + { + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 2, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + }, + { + 'requestId': '5703af74d0472a', + 'cpm': 0.15, + 'creativeId': 1, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 3
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should get correct video bid response', function () { + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '11' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '12' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2bc598e42b6a', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + } + ]; + const response = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'} + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 11, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': false, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': response}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('handles wrong and nobid responses', function () { + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '3' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d7190gf', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '4' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71321', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '5' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '300bfeb0d7183bb', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + } + ]; + const request = spec.buildRequests(bidRequests); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + expect(result.length).to.equal(0); + }); + + it('complicated case', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 4
    ', 'auid': 1, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 5
    ', 'auid': 2, 'h': 600, 'w': 350}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2164be6358b9', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '326bde7fbf69', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '2' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4e111f1b66e4', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '2' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '1751cd90161', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '2164be6358b9', + 'cpm': 1.15, + 'creativeId': 1, + 'dealId': 11, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + }, + { + 'requestId': '4e111f1b66e4', + 'cpm': 0.5, + 'creativeId': 2, + 'dealId': 12, + 'width': 300, + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + }, + { + 'requestId': '26d6f897b516', + 'cpm': 0.15, + 'creativeId': 1, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 3
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + }, + { + 'requestId': '326bde7fbf69', + 'cpm': 0.15, + 'creativeId': 1, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 4
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('dublicate uids and sizes in one slot', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 1, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 1, 'h': 250, 'w': 300}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '5126e301f4be', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57b2ebe70e16', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '225fcd44b18c', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '5126e301f4be', + 'cpm': 1.15, + 'creativeId': 1, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + }, + { + 'requestId': '57b2ebe70e16', + 'cpm': 0.5, + 'creativeId': 1, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 2
    ', + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + }); +}); diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 276972a163f..cedef568d56 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -4,33 +4,34 @@ import { spec } from 'modules/gumgumBidAdapter'; const ENDPOINT = 'https://g2.gumgum.com/hbid/imp'; -describe('gumgumAdapter', () => { +describe('gumgumAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'gumgum', 'params': { - 'inScreen': '10433394' + 'inScreen': '10433394', + 'bidfloor': 0.05 }, 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], + 'sizes': [[300, 250], [300, 600], [1, 1]], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return true when required params found', () => { + it('should return true when required params found', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -40,7 +41,7 @@ describe('gumgumAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when no unit type is specified', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -48,9 +49,19 @@ describe('gumgumAdapter', () => { }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); + + it('should return false when bidfloor is not a number', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'inSlot': '789', + 'bidfloor': '0.50' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': 'gumgum', @@ -63,16 +74,82 @@ describe('gumgumAdapter', () => { } ]; - it('sends bid request to ENDPOINT via GET', () => { - const requests = spec.buildRequests(bidRequests); - const request = requests[0]; + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests)[0]; expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET'); expect(request.id).to.equal('30b31c1838de1e'); }); + it('should correctly set the request paramters depending on params field', function () { + const request = Object.assign({}, bidRequests[0]); + delete request.params; + request.params = { + 'inScreen': '10433394', + 'bidfloor': 0.05 + }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(2); + expect(bidRequest.data).to.include.any.keys('t'); + expect(bidRequest.data).to.include.any.keys('fp'); + }); + it('should correctly set the request paramters depending on params field', function () { + const request = Object.assign({}, bidRequests[0]); + delete request.params; + request.params = { + 'ICV': '10433395' + }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(5); + expect(bidRequest.data).to.include.any.keys('ni'); + }); + it('should not add additional parameters depending on params field', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data).to.not.include.any.keys('ni'); + expect(request.data).to.not.include.any.keys('t'); + expect(request.data).to.not.include.any.keys('eAdBuyId'); + expect(request.data).to.not.include.any.keys('adBuyId'); + }); + it('should add consent parameters if gdprConsent is present', function () { + const gdprConsent = { consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', gdprApplies: true }; + const fakeBidRequest = { gdprConsent: gdprConsent }; + const bidRequest = spec.buildRequests(bidRequests, fakeBidRequest)[0]; + expect(bidRequest.data.gdprApplies).to.eq(1); + expect(bidRequest.data.gdprConsent).to.eq('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + }); + it('should handle gdprConsent is present but values are undefined case', function () { + const gdprConsent = { consent_string: undefined, gdprApplies: undefined }; + const fakeBidRequest = { gdprConsent: gdprConsent }; + const bidRequest = spec.buildRequests(bidRequests, fakeBidRequest)[0]; + expect(bidRequest.data).to.not.include.any.keys('gdprConsent') + }); + it('should add a tdid parameter if request contains unified id from TradeDesk', function () { + const unifiedId = { + 'userId': { + 'tdid': 'tradedesk-id' + } + } + const request = Object.assign(unifiedId, bidRequests[0]); + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.tdid).to.eq(unifiedId.userId.tdid); + }); + it('should not add a tdid parameter if unified id is not found', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data).to.not.include.any.keys('tdid'); + }); + it('should send ns parameter if browser contains navigator.connection property', function () { + const bidRequest = spec.buildRequests(bidRequests)[0]; + const connection = window.navigator && window.navigator.connection; + if (connection) { + const downlink = connection.downlink || connection.bandwidth; + expect(bidRequest.data).to.include.any.keys('ns'); + expect(bidRequest.data.ns).to.eq(Math.round(downlink * 1024)); + } else { + expect(bidRequest.data).to.not.include.any.keys('ns'); + } + }); }) - describe('interpretResponse', () => { + describe('interpretResponse', function () { let serverResponse = { 'ad': { 'id': 29593, @@ -97,13 +174,13 @@ describe('gumgumAdapter', () => { } let bidRequest = { id: 12345, - sizes: [[300, 250]], + sizes: [[300, 250], [1, 1]], url: ENDPOINT, method: 'GET', pi: 3 } - it('should get correct bid response', () => { + it('should get correct bid response', function () { let expectedResponse = { 'ad': '

    I am an ad

    ', 'cpm': 0, @@ -120,7 +197,7 @@ describe('gumgumAdapter', () => { expect(spec.interpretResponse({ body: serverResponse }, bidRequest)).to.deep.equal([expectedResponse]); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = { 'ad': {}, 'pag': { @@ -134,8 +211,45 @@ describe('gumgumAdapter', () => { let result = spec.interpretResponse({ body: response }, bidRequest); expect(result.length).to.equal(0); }); + + it('returns 1x1 when eligible product and size available', function () { + let inscreenBidRequest = { + id: 12346, + sizes: [[300, 250], [1, 1]], + url: ENDPOINT, + method: 'GET', + data: { + pi: 2, + t: 'ggumtest' + } + } + let inscreenServerResponse = { + 'ad': { + 'id': 2065333, + 'height': 90, + 'ipd': 2000, + 'markup': '

    I am an inscreen ad

    ', + 'ii': true, + 'du': null, + 'price': 1, + 'zi': 0, + 'impurl': 'http://g2.gumgum.com/ad/view', + 'clsurl': 'http://g2.gumgum.com/ad/close' + }, + 'pag': { + 't': 'ggumtest', + 'pvid': 'aa8bbb65-427f-4689-8cee-e3eed0b89eec', + 'css': 'html { overflow-y: auto }', + 'js': 'console.log("environment", env);' + }, + 'thms': 10000 + } + let result = spec.interpretResponse({ body: inscreenServerResponse }, inscreenBidRequest); + expect(result[0].width).to.equal(inscreenBidRequest.sizes[0][0].toString()); + expect(result[0].height).to.equal(inscreenBidRequest.sizes[0][1].toString()); + }) }) - describe('getUserSyncs', () => { + describe('getUserSyncs', function () { const syncOptions = { 'iframeEnabled': 'true' } diff --git a/test/spec/modules/gxoneBidAdapter_spec.js b/test/spec/modules/gxoneBidAdapter_spec.js index f34f4358490..afeb2c781f5 100644 --- a/test/spec/modules/gxoneBidAdapter_spec.js +++ b/test/spec/modules/gxoneBidAdapter_spec.js @@ -5,13 +5,13 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('GXOne Adapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'gxone', 'params': { @@ -24,11 +24,11 @@ describe('GXOne Adapter', function () { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -38,7 +38,7 @@ describe('GXOne Adapter', function () { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { function parseRequest(url) { const res = {}; url.split('&').forEach((it) => { @@ -83,7 +83,7 @@ describe('GXOne Adapter', function () { } ]; - it('should attach valid params to the tag', () => { + it('should attach valid params to the tag', function () { const request = spec.buildRequests([bidRequests[0]]); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -92,7 +92,7 @@ describe('GXOne Adapter', function () { expect(payload).to.have.property('auids', '5'); }); - it('auids must not be duplicated', () => { + it('auids must not be duplicated', function () { const request = spec.buildRequests(bidRequests); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -101,7 +101,7 @@ describe('GXOne Adapter', function () { expect(payload).to.have.property('auids', '5,6'); }); - it('pt parameter must be "gross" if params.priceType === "gross"', () => { + it('pt parameter must be "gross" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; const request = spec.buildRequests(bidRequests); expect(request.data).to.be.an('string'); @@ -112,7 +112,7 @@ describe('GXOne Adapter', function () { delete bidRequests[1].params.priceType; }); - it('pt parameter must be "net" or "gross"', () => { + it('pt parameter must be "net" or "gross"', function () { bidRequests[1].params.priceType = 'some'; const request = spec.buildRequests(bidRequests); expect(request.data).to.be.an('string'); @@ -124,7 +124,7 @@ describe('GXOne Adapter', function () { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 4, 'h': 250, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 5, 'h': 90, 'w': 728}], 'seat': '1'}, @@ -135,7 +135,7 @@ describe('GXOne Adapter', function () { {'seat': '1'}, ]; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const bidRequests = [ { 'bidder': 'gxone', @@ -169,7 +169,7 @@ describe('GXOne Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('should get correct multi bid response', () => { + it('should get correct multi bid response', function () { const bidRequests = [ { 'bidder': 'gxone', @@ -249,7 +249,7 @@ describe('GXOne Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('handles wrong and nobid responses', () => { + it('handles wrong and nobid responses', function () { const bidRequests = [ { 'bidder': 'gxone', diff --git a/test/spec/modules/hpmdnetworkBidAdapter_spec.js b/test/spec/modules/hpmdnetworkBidAdapter_spec.js new file mode 100644 index 00000000000..37ec44f07c4 --- /dev/null +++ b/test/spec/modules/hpmdnetworkBidAdapter_spec.js @@ -0,0 +1,148 @@ +import { expect } from 'chai'; +import { spec } from 'modules/hpmdnetworkBidAdapter'; + +describe('HPMDNetwork Adapter', function() { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + const validBid = { + bidder: 'hpmdnetwork', + params: { + placementId: '1' + } + }; + + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + + it('should return false for when required params are not passed', function () { + const invalidBid = { + bidder: 'hpmdnetwork', + params: {} + }; + + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [ + { + bidId: 'bid1', + bidder: 'hpmdnetwork', + params: { + placementId: '1' + } + }, + { + bidId: 'bid2', + bidder: 'hpmdnetwork', + params: { + placementId: '2', + } + } + ]; + const bidderRequest = { + refererInfo: { + referer: 'https://example.com?foo=bar' + } + }; + + const bidRequest = spec.buildRequests(bidRequests, bidderRequest); + + it('should build single POST request for multiple bids', function() { + expect(bidRequest.method).to.equal('POST'); + expect(bidRequest.url).to.equal('//banner.hpmdnetwork.ru/bidder/request'); + expect(bidRequest.data).to.be.an('object'); + expect(bidRequest.data.places).to.be.an('array'); + expect(bidRequest.data.places).to.have.lengthOf(2); + }); + + it('should pass bid parameters', function() { + const place1 = bidRequest.data.places[0]; + const place2 = bidRequest.data.places[1]; + + expect(place1.placementId).to.equal('1'); + expect(place2.placementId).to.equal('2'); + expect(place1.id).to.equal('bid1'); + expect(place2.id).to.equal('bid2'); + }); + + it('should pass site parameters', function() { + const url = bidRequest.data.url; + + expect(url).to.be.an('String'); + expect(url).to.equal('https://example.com?foo=bar'); + }); + + it('should pass settings', function() { + const settings = bidRequest.data.settings; + + expect(settings).to.be.an('object'); + expect(settings.currency).to.equal('RUB'); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + body: { + 'bids': + [ + { + 'cpm': 20, + 'currency': 'RUB', + 'displayUrl': '//banner.hpmdnetwork.ru/bidder/display?dbid=0&vbid=168', + 'id': '1', + 'creativeId': '11111', + }, + { + 'cpm': 30, + 'currency': 'RUB', + 'displayUrl': '//banner.hpmdnetwork.ru/bidder/display?dbid=0&vbid=170', + 'id': '2', + 'creativeId': '22222', + 'width': 300, + 'height': 250, + }, + ] + } + }; + + const bids = spec.interpretResponse(serverResponse); + + it('should return empty array for response with no bids', function() { + const emptyBids = spec.interpretResponse({ body: {} }); + + expect(emptyBids).to.have.lengthOf(0); + }); + + it('should parse all bids from response', function() { + expect(bids).to.have.lengthOf(2); + }); + + it('should parse bid without sizes', function() { + expect(bids[0].requestId).to.equal('1'); + expect(bids[0].cpm).to.equal(20); + expect(bids[0].width).to.equal(1); + expect(bids[0].height).to.equal(1); + expect(bids[0].ttl).to.equal(300); + expect(bids[0].currency).to.equal('RUB'); + expect(bids[0]).to.have.property('creativeId'); + expect(bids[0].creativeId).to.equal('11111'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].ad).to.include(''); + }); + + it('should parse bid with sizes', function() { + expect(bids[1].requestId).to.equal('2'); + expect(bids[1].cpm).to.equal(30); + expect(bids[1].width).to.equal(300); + expect(bids[1].height).to.equal(250); + expect(bids[1].ttl).to.equal(300); + expect(bids[1].currency).to.equal('RUB'); + expect(bids[1]).to.have.property('creativeId'); + expect(bids[1].creativeId).to.equal('22222'); + expect(bids[1].netRevenue).to.equal(true); + expect(bids[1].ad).to.include(''); + }); + }); +}); diff --git a/test/spec/modules/huddledmassesBidAdapter_spec.js b/test/spec/modules/huddledmassesBidAdapter_spec.js index f98bc06d0da..7823ae53c12 100644 --- a/test/spec/modules/huddledmassesBidAdapter_spec.js +++ b/test/spec/modules/huddledmassesBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import {spec} from '../../../modules/huddledmassesBidAdapter'; -describe('HuddledmassesAdapter', () => { +describe('HuddledmassesAdapter', function () { let bid = { bidId: '2dd581a2b6281d', bidder: 'huddledmasses', @@ -15,35 +15,35 @@ describe('HuddledmassesAdapter', () => { transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' }; - describe('isBidRequestValid', () => { - it('Should return true when placement_id can be cast to a number, and when at least one of the sizes passed is allowed', () => { + describe('isBidRequestValid', function () { + it('Should return true when placement_id can be cast to a number, and when at least one of the sizes passed is allowed', function () { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false when placement_id is not a number', () => { + it('Should return false when placement_id is not a number', function () { bid.params.placement_id = 'aaa'; expect(spec.isBidRequestValid(bid)).to.be.false; }); - it('Should return false when the sizes are not allowed', () => { + it('Should return false when the sizes are not allowed', function () { bid.sizes = [[1, 1]]; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let serverRequest = spec.buildRequests([bid]); - it('Creates a ServerRequest object with method, URL and data', () => { + it('Creates a ServerRequest object with method, URL and data', function () { expect(serverRequest).to.exist; expect(serverRequest.method).to.exist; expect(serverRequest.url).to.exist; expect(serverRequest.data).to.exist; }); - it('Returns POST method', () => { + it('Returns POST method', function () { expect(serverRequest.method).to.equal('POST'); }); - it('Returns valid URL', () => { + it('Returns valid URL', function () { expect(serverRequest.url).to.equal('//huddledmassessupply.com/?c=o&m=multi'); }); - it('Returns valid data if array of bids is valid', () => { + it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; expect(data).to.be.an('object'); expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); @@ -62,13 +62,13 @@ describe('HuddledmassesAdapter', () => { expect(placement.sizes).to.be.an('array'); } }); - it('Returns empty data if no valid requests are passed', () => { + it('Returns empty data if no valid requests are passed', function () { serverRequest = spec.buildRequests([]); let data = serverRequest.data; expect(data.placements).to.be.an('array').that.is.empty; }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let resObject = { body: [ { requestId: '123', @@ -83,7 +83,7 @@ describe('HuddledmassesAdapter', () => { } ] }; let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', () => { + it('Returns an array of valid server responses if response object is valid', function () { expect(serverResponses).to.be.an('array').that.is.not.empty; for (let i = 0; i < serverResponses.length; i++) { let dataItem = serverResponses[i]; @@ -99,16 +99,16 @@ describe('HuddledmassesAdapter', () => { expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); } - it('Returns an empty array if invalid response is passed', () => { + it('Returns an empty array if invalid response is passed', function () { serverResponses = spec.interpretResponse('invalid_response'); expect(serverResponses).to.be.an('array').that.is.empty; }); }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs', function () { let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', () => { + it('Returns valid URL and type', function () { expect(userSync).to.be.an('array').with.lengthOf(1); expect(userSync[0].type).to.exist; expect(userSync[0].url).to.exist; diff --git a/test/spec/modules/iasBidAdapter_spec.js b/test/spec/modules/iasBidAdapter_spec.js index 4f335ab22ba..8197ee25856 100644 --- a/test/spec/modules/iasBidAdapter_spec.js +++ b/test/spec/modules/iasBidAdapter_spec.js @@ -1,15 +1,15 @@ import { expect } from 'chai'; import { spec } from 'modules/iasBidAdapter'; -describe('iasBidAdapter is an adapter that', () => { - it('has the correct bidder code', () => { +describe('iasBidAdapter is an adapter that', function () { + it('has the correct bidder code', function () { expect(spec.code).to.equal('ias'); }); - describe('has a method `isBidRequestValid` that', () => { - it('exists', () => { + describe('has a method `isBidRequestValid` that', function () { + it('exists', function () { expect(spec.isBidRequestValid).to.be.a('function'); }); - it('returns false if bid params misses `pubId`', () => { + it('returns false if bid params misses `pubId`', function () { expect(spec.isBidRequestValid( { params: { @@ -17,7 +17,7 @@ describe('iasBidAdapter is an adapter that', () => { } })).to.equal(false); }); - it('returns false if bid params misses `adUnitPath`', () => { + it('returns false if bid params misses `adUnitPath`', function () { expect(spec.isBidRequestValid( { params: { @@ -25,7 +25,7 @@ describe('iasBidAdapter is an adapter that', () => { } })).to.equal(false); }); - it('returns true otherwise', () => { + it('returns true otherwise', function () { expect(spec.isBidRequestValid( { params: { @@ -37,13 +37,13 @@ describe('iasBidAdapter is an adapter that', () => { }); }); - describe('has a method `buildRequests` that', () => { - it('exists', () => { + describe('has a method `buildRequests` that', function () { + it('exists', function () { expect(spec.buildRequests).to.be.a('function'); }); - describe('given bid requests, returns a `ServerRequest` instance that', () => { + describe('given bid requests, returns a `ServerRequest` instance that', function () { let bidRequests, IAS_HOST; - beforeEach(() => { + beforeEach(function () { IAS_HOST = '//pixel.adsafeprotected.com/services/pub'; bidRequests = [ { @@ -79,17 +79,20 @@ describe('iasBidAdapter is an adapter that', () => { } ]; }); - it('has property `method` of `GET`', () => { + it('has property `method` of `GET`', function () { expect(spec.buildRequests(bidRequests)).to.deep.include({ method: 'GET' }); }); - it('has property `url` to be the correct IAS endpoint', () => { + it('has property `url` to be the correct IAS endpoint', function () { expect(spec.buildRequests(bidRequests)).to.deep.include({ url: IAS_HOST }); }); - describe('has property `data` that is an encode query string containing information such as', () => { + it('only includes the first `bidRequest` as the bidRequest variable on a multiple slot request', function () { + expect(spec.buildRequests(bidRequests).bidRequest.adUnitCode).to.equal(bidRequests[0].adUnitCode); + }); + describe('has property `data` that is an encode query string containing information such as', function () { let val; const ANID_PARAM = 'anId'; const SLOT_PARAM = 'slot'; @@ -97,35 +100,73 @@ describe('iasBidAdapter is an adapter that', () => { const SLOT_SIZE_PARAM = 'ss'; const SLOT_AD_UNIT_PATH_PARAM = 'p'; - beforeEach(() => val = decodeURI(spec.buildRequests(bidRequests).data)); - it('publisher id', () => { + beforeEach(function () { + val = decodeURI(spec.buildRequests(bidRequests).data); + }); + it('publisher id', function () { expect(val).to.have.string(`${ANID_PARAM}=1234`); }); - it('ad slot`s id, size and ad unit path', () => { + it('ad slot`s id, size and ad unit path', function () { expect(val).to.have.string(`${SLOT_PARAM}={${SLOT_ID_PARAM}:one-div-id,${SLOT_SIZE_PARAM}:[10.20,300.400],${SLOT_AD_UNIT_PATH_PARAM}:/a/b/c}`); expect(val).to.have.string(`${SLOT_PARAM}={${SLOT_ID_PARAM}:two-div-id,${SLOT_SIZE_PARAM}:[50.60],${SLOT_AD_UNIT_PATH_PARAM}:/d/e/f}`); }); - it('window size', () => { + it('window size', function () { expect(val).to.match(/.*wr=[0-9]*\.[0-9]*/); }); - it('screen size', () => { + it('screen size', function () { expect(val).to.match(/.*sr=[0-9]*\.[0-9]*/); }); + it('url value', function () { + expect(val).to.match(/.*url=https?%3A%2F%2F[^\s$.?#].[^\s]*/); + }); }); - it('has property `bidRequest` that is the first passed in bid request', () => { + it('has property `bidRequest` that is the first passed in bid request', function () { expect(spec.buildRequests(bidRequests)).to.deep.include({ bidRequest: bidRequests[0] }); }); }); }); - describe('has a method `interpretResponse` that', () => { - it('exists', () => { + describe('has a method `interpretResponse` that', function () { + it('exists', function () { expect(spec.interpretResponse).to.be.a('function'); }); - describe('returns a list of bid response that', () => { - let bidResponse, slots; - beforeEach(() => { + describe('returns a list of bid response that', function () { + let bidRequests, bidResponse, slots, serverResponse; + beforeEach(function () { + bidRequests = [ + { + adUnitCode: 'one-div-id', + auctionId: 'someAuctionId', + bidId: 'someBidId1', + bidder: 'ias', + bidderRequestId: 'someBidderRequestId', + params: { + pubId: '1234', + adUnitPath: '/a/b/c' + }, + sizes: [ + [10, 20], + [300, 400] + ], + transactionId: 'someTransactionId' + }, + { + adUnitCode: 'two-div-id', + auctionId: 'someAuctionId', + bidId: 'someBidId2', + bidder: 'ias', + bidderRequestId: 'someBidderRequestId', + params: { + pubId: '1234', + adUnitPath: '/d/e/f' + }, + sizes: [ + [50, 60] + ], + transactionId: 'someTransactionId' + } + ]; const request = { bidRequest: { bidId: '102938' @@ -140,7 +181,7 @@ describe('iasBidAdapter is an adapter that', () => { id: '5678', vw: ['80', '90'] }; - const serverResponse = { + serverResponse = { body: { brandSafety: { adt: 'adtVal', @@ -158,33 +199,145 @@ describe('iasBidAdapter is an adapter that', () => { }; bidResponse = spec.interpretResponse(serverResponse, request); }); - it('has IAS keyword `adt` as property', () => { + it('has IAS keyword `adt` as property', function () { expect(bidResponse[0]).to.deep.include({ adt: 'adtVal' }); }); - it('has IAS keyword `alc` as property', () => { + it('has IAS keyword `alc` as property', function () { expect(bidResponse[0]).to.deep.include({ alc: 'alcVal' }); }); - it('has IAS keyword `dlm` as property', () => { + it('has IAS keyword `dlm` as property', function () { expect(bidResponse[0]).to.deep.include({ dlm: 'dlmVal' }); }); - it('has IAS keyword `drg` as property', () => { + it('has IAS keyword `drg` as property', function () { expect(bidResponse[0]).to.deep.include({ drg: 'drgVal' }); }); - it('has IAS keyword `hat` as property', () => { + it('has IAS keyword `hat` as property', function () { expect(bidResponse[0]).to.deep.include({ hat: 'hatVal' }); }); - it('has IAS keyword `off` as property', () => { + it('has IAS keyword `off` as property', function () { expect(bidResponse[0]).to.deep.include({ off: 'offVal' }); }); - it('has IAS keyword `vio` as property', () => { + it('has IAS keyword `vio` as property', function () { expect(bidResponse[0]).to.deep.include({ vio: 'vioVal' }); }); - it('has IAS keyword `fr` as property', () => { + it('has IAS keyword `fr` as property', function () { expect(bidResponse[0]).to.deep.include({ fr: 'false' }); }); - it('has property `slots`', () => { + it('has property `slots`', function () { expect(bidResponse[0]).to.deep.include({ slots: slots }); }); + it('response is the same for multiple slots', function () { + var adapter = spec; + var requests = adapter.buildRequests(bidRequests); + expect(adapter.interpretResponse(serverResponse, requests)).to.length(2); + }); + }); + describe('returns a list of bid response that with custom value', function () { + let bidRequests, bidResponse, slots, custom, serverResponse; + beforeEach(function () { + bidRequests = [ + { + adUnitCode: 'one-div-id', + auctionId: 'someAuctionId', + bidId: 'someBidId1', + bidder: 'ias', + bidderRequestId: 'someBidderRequestId', + params: { + pubId: '1234', + adUnitPath: '/a/b/c' + }, + sizes: [ + [10, 20], + [300, 400] + ], + transactionId: 'someTransactionId' + }, + { + adUnitCode: 'two-div-id', + auctionId: 'someAuctionId', + bidId: 'someBidId2', + bidder: 'ias', + bidderRequestId: 'someBidderRequestId', + params: { + pubId: '1234', + adUnitPath: '/d/e/f' + }, + sizes: [ + [50, 60] + ], + transactionId: 'someTransactionId' + } + ]; + const request = { + bidRequest: { + bidId: '102938' + } + }; + slots = {}; + slots['test-div-id'] = { + id: '1234', + vw: ['60', '70'] + }; + slots['test-div-id-two'] = { + id: '5678', + vw: ['80', '90'] + }; + custom = {}; + custom['ias-kw'] = ['IAS_1_KW', 'IAS_2_KW']; + serverResponse = { + body: { + brandSafety: { + adt: 'adtVal', + alc: 'alcVal', + dlm: 'dlmVal', + drg: 'drgVal', + hat: 'hatVal', + off: 'offVal', + vio: 'vioVal' + }, + fr: 'false', + slots: slots, + custom: custom + }, + headers: {} + }; + bidResponse = spec.interpretResponse(serverResponse, request); + }); + it('has IAS keyword `adt` as property', function () { + expect(bidResponse[0]).to.deep.include({ adt: 'adtVal' }); + }); + it('has IAS keyword `alc` as property', function () { + expect(bidResponse[0]).to.deep.include({ alc: 'alcVal' }); + }); + it('has IAS keyword `dlm` as property', function () { + expect(bidResponse[0]).to.deep.include({ dlm: 'dlmVal' }); + }); + it('has IAS keyword `drg` as property', function () { + expect(bidResponse[0]).to.deep.include({ drg: 'drgVal' }); + }); + it('has IAS keyword `hat` as property', function () { + expect(bidResponse[0]).to.deep.include({ hat: 'hatVal' }); + }); + it('has IAS keyword `off` as property', function () { + expect(bidResponse[0]).to.deep.include({ off: 'offVal' }); + }); + it('has IAS keyword `vio` as property', function () { + expect(bidResponse[0]).to.deep.include({ vio: 'vioVal' }); + }); + it('has IAS keyword `fr` as property', function () { + expect(bidResponse[0]).to.deep.include({ fr: 'false' }); + }); + it('has property `slots`', function () { + expect(bidResponse[0]).to.deep.include({ slots: slots }); + }); + it('has property `custom`', function () { + expect(bidResponse[0]).to.deep.include({ custom: custom }); + }); + it('response is the same for multiple slots', function () { + var adapter = spec; + var requests = adapter.buildRequests(bidRequests); + expect(adapter.interpretResponse(serverResponse, requests)).to.length(3); + }); }); }); }); diff --git a/test/spec/modules/imonomyBidAdapter_spec.js b/test/spec/modules/imonomyBidAdapter_spec.js new file mode 100644 index 00000000000..206e227a3b1 --- /dev/null +++ b/test/spec/modules/imonomyBidAdapter_spec.js @@ -0,0 +1,164 @@ +import { expect } from 'chai'; +import { spec } from 'modules/imonomyBidAdapter'; + +describe('Imonomy Adapter Tests', function () { + const bidsRequest = [ + { + bidder: 'imonomy', + params: { + placementId: '170577', + hbid: '14567718624', + }, + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', + sizes: [ + [300, 250] + ], + bidId: '2faedf1095f815', + bidderRequestId: '18065867f8ae39', + auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' + }, + { + bidder: 'imonomy', + params: { + placementId: '281277', + hbid: '14567718624', + floorPrice: 0.5 + }, + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', + sizes: [ + [728, 90] + ], + bidId: '3c34e2367a3f59', + bidderRequestId: '18065867f8ae39', + auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' + }]; + + const bidsResponse = { + body: { + bids: [ + { + placementid: '170577', + uuid: '2faedf1095f815', + width: 300, + height: 250, + cpm: 0.51, + creative: '', + ttl: 360, + currency: 'USD', + netRevenue: true, + creativeId: 'd30b58c2ba' + } + ] + } + }; + + it('Verifies imonomyAdapter bidder code', function () { + expect(spec.code).to.equal('imonomy'); + }); + + it('Verifies imonomyAdapter bid request validation', function () { + expect(spec.isBidRequestValid(bidsRequest[0])).to.equal(true); + expect(spec.isBidRequestValid(bidsRequest[1])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ params: { hbid: 12345 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { placementid: 12345 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890 } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890, floorPrice: 0.8 } })).to.equal(true); + }); + + it('Verify imonomyAdapter build request', function () { + var startTime = new Date().getTime(); + + const request = spec.buildRequests(bidsRequest); + expect(request.url).to.equal('//b.imonomy.com/openrtb/hb/14567718624'); + expect(request.method).to.equal('POST'); + const requestData = JSON.parse(request.data); + + // bids object + let bids = requestData.bids; + expect(bids).to.have.lengthOf(2); + + // first bid request: no floor price + expect(bids[0].uuid).to.equal('2faedf1095f815'); + expect(bids[0].floorprice).to.be.undefined; + expect(bids[0].placementid).to.equal('170577'); + expect(bids[0].hbid).to.equal('14567718624'); + expect(bids[0].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); + expect(bids[0].sizes).to.have.lengthOf(1); + expect(bids[0].sizes[0][0]).to.equal(300); + expect(bids[0].sizes[0][1]).to.equal(250); + + // second bid request: with floor price + expect(bids[1].uuid).to.equal('3c34e2367a3f59'); + expect(bids[1].floorprice).to.equal(0.5); + expect(bids[1].placementid).to.equal('281277'); + expect(bids[1].hbid).to.equal('14567718624'); + expect(bids[1].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); + expect(bids[1]).to.have.property('sizes') + .that.is.an('array') + .of.length(1) + .that.deep.equals([[728, 90]]); + + // kbConf object + let kbConf = requestData.kbConf; + expect(kbConf.hdbdid).to.equal(bids[0].hbid); + expect(kbConf.hdbdid).to.equal(bids[1].hbid); + expect(kbConf.encode_bid).to.be.undefined; + // kbConf timezone and cb + expect(kbConf.cb).not.to.be.undefined; + expect(kbConf.ts_as).to.be.above(startTime - 1); + expect(kbConf.tz).to.equal(new Date().getTimezoneOffset()); + // kbConf bid ids + expect(kbConf.hb_placement_bidids) + .to.have.property(bids[0].placementid) + .that.equal(bids[0].uuid); + expect(kbConf.hb_placement_bidids) + .to.have.property(bids[1].placementid) + .that.equal(bids[1].uuid); + // kbConf floor price + expect(kbConf.hb_floors).not.to.have.property(bids[0].placementid) + expect(kbConf.hb_floors).to.have.property(bids[1].placementid).that.equal(bids[1].floorprice); + // kbConf placement ids + expect(kbConf.hb_placements).to.have.lengthOf(2); + expect(kbConf.hb_placements[0]).to.equal(bids[0].placementid); + expect(kbConf.hb_placements[1]).to.equal(bids[1].placementid); + }); + + it('Verify imonomyAdapter build response', function () { + const request = spec.buildRequests(bidsRequest); + const bids = spec.interpretResponse(bidsResponse, request); + + // 'server' return single bid + expect(bids).to.have.lengthOf(1); + + // verify bid object + const bid = bids[0]; + const responseBids = bidsResponse.body.bids; + + expect(bid.cpm).to.equal(responseBids[0].cpm); + expect(bid.ad).to.equal(responseBids[0].creative); + expect(bid.requestId).equal(responseBids[0].uuid); + expect(bid.uuid).equal(responseBids[0].uuid); + expect(bid.width).to.equal(responseBids[0].width); + expect(bid.height).to.equal(responseBids[0].height); + expect(bid.ttl).to.equal(responseBids[0].ttl); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.creativeId).to.equal(responseBids[0].creativeId); + }); + + it('Verifies imonomyAdapter sync options', function () { + // user sync disabled + expect(spec.getUserSyncs({})).to.be.undefined; + expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; + // user sync enabled + const options = spec.getUserSyncs({ iframeEnabled: true }); + expect(options).to.not.be.undefined; + expect(options).to.have.lengthOf(1); + expect(options[0].type).to.equal('iframe'); + expect(options[0].url).to.equal('//b.imonomy.com/UserMatching/b/'); + }); +}); diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index cd68418d6c7..8b8f6c4bf4c 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -7,7 +7,7 @@ describe('Improve Digital Adapter Tests', function () { let idClient = new ImproveDigitalAdServerJSClient('hb'); const METHOD = 'GET'; - const URL = '//ad.360yield.com/hb'; + const URL = 'https://ice.360yield.com/hb'; const PARAM_PREFIX = 'jsonp='; const simpleBidRequest = { @@ -19,7 +19,8 @@ describe('Improve Digital Adapter Tests', function () { transactionId: 'f183e871-fbed-45f0-a427-c8a63c4c01eb', bidId: '33e9500b21129f', bidderRequestId: '2772c1e566670b', - auctionId: '192721e36a0239' + auctionId: '192721e36a0239', + sizes: [[300, 250], [160, 600], ['blah', 150], [-1, 300], [300, -5]] }; const simpleSmartTagBidRequest = { @@ -32,22 +33,36 @@ describe('Improve Digital Adapter Tests', function () { } }; - describe('isBidRequestValid', () => { - it('should return false when no bid', () => { + const bidderRequestGdpr = { + gdprConsent: { + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + vendorData: {}, + gdprApplies: true + }, + }; + + const bidderRequestReferrer = { + refererInfo: { + referer: 'https://blah.com/test.html', + }, + }; + + describe('isBidRequestValid', function () { + it('should return false when no bid', function () { expect(spec.isBidRequestValid()).to.equal(false); }); - it('should return false when no bid.params', () => { + it('should return false when no bid.params', function () { let bid = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when both placementId and placementKey + publisherId are missing', () => { + it('should return false when both placementId and placementKey + publisherId are missing', function () { let bid = { 'params': {} }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when only one of placementKey and publisherId is present', () => { + it('should return false when only one of placementKey and publisherId is present', function () { let bid = { params: { publisherId: 1234 @@ -62,19 +77,19 @@ describe('Improve Digital Adapter Tests', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return true when placementId is passed', () => { + it('should return true when placementId is passed', function () { let bid = { 'params': {} }; expect(spec.isBidRequestValid(simpleBidRequest)).to.equal(true); }); - it('should return true when both placementKey and publisherId are passed', () => { + it('should return true when both placementKey and publisherId are passed', function () { let bid = { 'params': {} }; expect(spec.isBidRequestValid(simpleSmartTagBidRequest)).to.equal(true); }); }); - describe('buildRequests', () => { - it('should make a well-formed request objects', () => { + describe('buildRequests', function () { + it('should make a well-formed request objects', function () { const requests = spec.buildRequests([simpleBidRequest]); expect(requests).to.be.an('array'); expect(requests.length).to.equal(1); @@ -84,7 +99,7 @@ describe('Improve Digital Adapter Tests', function () { expect(request.url).to.equal(URL); expect(request.data.substring(0, PARAM_PREFIX.length)).to.equal(PARAM_PREFIX); - const params = JSON.parse(request.data.substring(PARAM_PREFIX.length)); + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); expect(params.bid_request).to.be.an('object'); expect(params.bid_request.id).to.be.a('string'); expect(params.bid_request.version).to.equal(`${spec.version}-${idClient.CONSTANTS.CLIENT_VERSION}`); @@ -98,14 +113,14 @@ describe('Improve Digital Adapter Tests', function () { ]); }); - it('should set placementKey and publisherId for smart tags', () => { + it('should set placementKey and publisherId for smart tags', function () { const requests = spec.buildRequests([simpleSmartTagBidRequest]); - const params = JSON.parse(requests[0].data.substring(PARAM_PREFIX.length)); + const params = JSON.parse(decodeURIComponent(requests[0].data.substring(PARAM_PREFIX.length))); expect(params.bid_request.imp[0].pubid).to.equal(1032); expect(params.bid_request.imp[0].pkey).to.equal('data_team_test_hb_smoke_test'); }); - it('should add keyValues', () => { + it('should add keyValues', function () { let bidRequest = Object.assign({}, simpleBidRequest); const keyValues = { testKey: [ @@ -114,11 +129,11 @@ describe('Improve Digital Adapter Tests', function () { }; bidRequest.params.keyValues = keyValues; const request = spec.buildRequests([bidRequest])[0]; - const params = JSON.parse(request.data.substring(PARAM_PREFIX.length)); + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); expect(params.bid_request.imp[0].kvw).to.deep.equal(keyValues); }); - it('should add size', () => { + it('should add single size filter', function () { let bidRequest = Object.assign({}, simpleBidRequest); const size = { w: 800, @@ -126,20 +141,59 @@ describe('Improve Digital Adapter Tests', function () { }; bidRequest.params.size = size; const request = spec.buildRequests([bidRequest])[0]; - const params = JSON.parse(request.data.substring(PARAM_PREFIX.length)); + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); expect(params.bid_request.imp[0].banner).to.deep.equal(size); + // When single size filter is set, format shouldn't be populated. This + // is to maintain backward compatibily + expect(params.bid_request.imp[0].banner.format).to.not.exist; }); - it('should add currency', () => { + it('should add currency', function () { const bidRequest = Object.assign({}, simpleBidRequest); const getConfigStub = sinon.stub(config, 'getConfig').returns('JPY'); const request = spec.buildRequests([bidRequest])[0]; - const params = JSON.parse(request.data.substring(PARAM_PREFIX.length)); + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); expect(params.bid_request.imp[0].currency).to.equal('JPY'); getConfigStub.restore(); }); - it('should return 2 requests', () => { + it('should add bid floor', function () { + const bidRequest = Object.assign({}, simpleBidRequest); + let request = spec.buildRequests([bidRequest])[0]; + let params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + // Floor price currency shouldn't be populated without a floor price + expect(params.bid_request.imp[0].bidfloorcur).to.not.exist; + + // Default floor price currency + bidRequest.params.bidFloor = 0.05; + request = spec.buildRequests([bidRequest])[0]; + params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.imp[0].bidfloor).to.equal(0.05); + expect(params.bid_request.imp[0].bidfloorcur).to.equal('USD'); + + // Floor price currency + bidRequest.params.bidFloorCur = 'eUR'; + request = spec.buildRequests([bidRequest])[0]; + params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.imp[0].bidfloor).to.equal(0.05); + expect(params.bid_request.imp[0].bidfloorcur).to.equal('EUR'); + }); + + it('should add GDPR consent string', function () { + const bidRequest = Object.assign({}, simpleBidRequest); + const request = spec.buildRequests([bidRequest], bidderRequestGdpr)[0]; + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.gdpr).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + }); + + it('should add referrer', function () { + const bidRequest = Object.assign({}, simpleBidRequest); + const request = spec.buildRequests([bidRequest], bidderRequestReferrer)[0]; + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.referrer).to.equal('https://blah.com/test.html'); + }); + + it('should return 2 requests', function () { const requests = spec.buildRequests([ simpleBidRequest, simpleSmartTagBidRequest @@ -147,79 +201,275 @@ describe('Improve Digital Adapter Tests', function () { expect(requests).to.be.an('array'); expect(requests.length).to.equal(2); }); - }); - describe('interpretResponse', () => { - let registerSyncStub; - beforeEach(() => { - registerSyncStub = sinon.stub(userSync, 'registerSync'); + it('should return one request in a single request mode', function () { + const getConfigStub = sinon.stub(config, 'getConfig'); + getConfigStub.withArgs('improvedigital.singleRequest').returns(true); + const requests = spec.buildRequests([ + simpleBidRequest, + simpleSmartTagBidRequest + ]); + expect(requests).to.be.an('array'); + expect(requests.length).to.equal(1); + getConfigStub.restore(); }); - afterEach(() => { - registerSyncStub.restore(); + it('should set Prebid sizes in bid request', function () { + const getConfigStub = sinon.stub(config, 'getConfig'); + getConfigStub.withArgs('improvedigital.usePrebidSizes').returns(true); + const request = spec.buildRequests([simpleBidRequest])[0]; + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.imp[0].banner).to.deep.equal({ + format: [ + { w: 300, h: 250 }, + { w: 160, h: 600 } + ] + }); + getConfigStub.restore(); }); - const serverResponse = { - 'body': { - 'id': '687a06c541d8d1', - 'site_id': 191642, - 'bid': [ - { - 'isNet': false, - 'id': '33e9500b21129f', - 'advid': '5279', - 'price': 1.45888594164456, - 'nurl': 'http://ad.360yield.com/imp_pixel?ic=wVmhKI07hCVyGC1sNdFp.6buOSiGYOw8jPyZLlcMY2RCwD4ek3Fy6.xUI7U002skGBs3objMBoNU-Frpvmb9js3NKIG0YZJgWaNdcpXY9gOXE9hY4-wxybCjVSNzhOQB-zic73hzcnJnKeoGgcfvt8fMy18-yD0aVdYWt4zbqdoITOkKNCPBEgbPFu1rcje-o7a64yZ7H3dKvtnIixXQYc1Ep86xGSBGXY6xW2KfUOMT6vnkemxO72divMkMdhR8cAuqIubbx-ZID8-xf5c9k7p6DseeBW0I8ionrlTHx.rGosgxhiFaMqtr7HiA7PBzKvPdeEYN0hQ8RYo8JzYL82hA91A3V2m9Ij6y0DfIJnnrKN8YORffhxmJ6DzwEl1zjrVFbD01bqB3Vdww8w8PQJSkKQkd313tr-atU8LS26fnBmOngEkVHwAr2WCKxuUvxHmuVBTA-Lgz7wKwMoOJCA3hFxMavVb0ZFB7CK0BUTVU6z0De92Q.FJKNCHLMbjX3vcAQ90=', - 'h': 290, - 'pid': 1053688, - 'sync': [ - 'http://link1', - 'http://link2' + + it('should not add single size filter when using Prebid sizes', function () { + const getConfigStub = sinon.stub(config, 'getConfig'); + getConfigStub.withArgs('improvedigital.usePrebidSizes').returns(true); + const bidRequest = Object.assign({}, simpleBidRequest); + const size = { + w: 800, + h: 600 + }; + bidRequest.params.size = size; + const request = spec.buildRequests([bidRequest])[0]; + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.imp[0].banner).to.deep.equal({ + format: [ + { w: 300, h: 250 }, + { w: 160, h: 600 } + ] + }); + getConfigStub.restore(); + }); + }); + + const serverResponse = { + 'body': { + 'id': '687a06c541d8d1', + 'site_id': 191642, + 'bid': [ + { + 'isNet': false, + 'id': '33e9500b21129f', + 'advid': '5279', + 'price': 1.45888594164456, + 'nurl': 'http://ice.360yield.com/imp_pixel?ic=wVmhKI07hCVyGC1sNdFp.6buOSiGYOw8jPyZLlcMY2RCwD4ek3Fy6.xUI7U002skGBs3objMBoNU-Frpvmb9js3NKIG0YZJgWaNdcpXY9gOXE9hY4-wxybCjVSNzhOQB-zic73hzcnJnKeoGgcfvt8fMy18-yD0aVdYWt4zbqdoITOkKNCPBEgbPFu1rcje-o7a64yZ7H3dKvtnIixXQYc1Ep86xGSBGXY6xW2KfUOMT6vnkemxO72divMkMdhR8cAuqIubbx-ZID8-xf5c9k7p6DseeBW0I8ionrlTHx.rGosgxhiFaMqtr7HiA7PBzKvPdeEYN0hQ8RYo8JzYL82hA91A3V2m9Ij6y0DfIJnnrKN8YORffhxmJ6DzwEl1zjrVFbD01bqB3Vdww8w8PQJSkKQkd313tr-atU8LS26fnBmOngEkVHwAr2WCKxuUvxHmuVBTA-Lgz7wKwMoOJCA3hFxMavVb0ZFB7CK0BUTVU6z0De92Q.FJKNCHLMbjX3vcAQ90=', + 'h': 290, + 'pid': 1053688, + 'sync': [ + 'http://link1', + 'http://link2' + ], + 'crid': '422031', + 'w': 600, + 'cid': '99006', + 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' + } + ], + 'debug': '' + } + }; + + const serverResponseTwoBids = { + 'body': { + 'id': '687a06c541d8d1', + 'site_id': 191642, + 'bid': [ + serverResponse.body.bid[0], + { + 'isNet': true, + 'id': '1234', + 'advid': '5280', + 'price': 1.23, + 'nurl': 'http://link/imp_pixel?ic=wVmhKI07hCVyGC1sNdFp.6buOSiGYOw8jPyZLlcMY2RCwD4ek3Fy6.xUI7U002skGBs3objMBoNU-Frpvmb9js3NKIG0YZJgWaNdcpXY9gOXE9hY4-wxybCjVSNzhOQB-zic73hzcnJnKeoGgcfvt8fMy18-yD0aVdYWt4zbqdoITOkKNCPBEgbPFu1rcje-o7a64yZ7H3dKvtnIixXQYc1Ep86xGSBGXY6xW2KfUOMT6vnkemxO72divMkMdhR8cAuqIubbx-ZID8-xf5c9k7p6DseeBW0I8ionrlTHx.rGosgxhiFaMqtr7HiA7PBzKvPdeEYN0hQ8RYo8JzYL82hA91A3V2m9Ij6y0DfIJnnrKN8YORffhxmJ6DzwEl1zjrVFbD01bqB3Vdww8w8PQJSkKQkd313tr-atU8LS26fnBmOngEkVHwAr2WCKxuUvxHmuVBTA-Lgz7wKwMoOJCA3hFxMavVb0ZFB7CK0BUTVU6z0De92Q.FJKNCHLMbjX3vcAQ90=', + 'h': 400, + 'pid': 1053688, + 'sync': [ + 'http://link3' + ], + 'crid': '422033', + 'w': 700, + 'cid': '99006', + 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' + } + ], + 'debug': '' + } + }; + + const serverResponseNative = { + body: { + id: '687a06c541d8d1', + site_id: 191642, + bid: [ + { + isNet: false, + id: '33e9500b21129f', + advid: '5279', + price: 1.45888594164456, + nurl: 'http://ice.360yield.com/imp_pixel?ic=wVm', + h: 290, + pid: 1053688, + sync: [ + 'http://link1', + 'http://link2' + ], + crid: '422031', + w: 600, + cid: '99006', + native: { + assets: [ + { + title: { + text: 'Native title' + } + }, + { + data: { + type: 1, + value: 'Improve Digital' + } + }, + { + data: { + type: 2, + value: 'Native body' + } + }, + { + data: { + type: 3, + value: '4' // rating + } + }, + { + data: { + type: 4, + value: '10105' // likes + } + }, + { + data: { + type: 5, + value: '150000' // downloads + } + }, + { + data: { + type: 6, + value: '3.99' // price + } + }, + { + data: { + type: 7, + value: '4.49' // salePrice + } + }, + { + data: { + type: 8, + value: '(123) 456-7890' // phone + } + }, + { + data: { + type: 9, + value: '123 Main Street, Anywhere USA' // address + } + }, + { + data: { + type: 10, + value: 'body2' + } + }, + { + data: { + type: 11, + value: 'https://myurl.com' // displayUrl + } + }, + { + data: { + type: 12, + value: 'Do it' // cta + } + }, + { + img: { + type: 1, + url: 'Should get ignored', + h: 300, + w: 400 + } + }, + { + img: { + type: 2, + url: 'http://blah.com/icon.jpg', + h: 30, + w: 40 + } + + }, + { + img: { + type: 3, + url: 'http://blah.com/image.jpg', + h: 200, + w: 800 + } + } ], - 'crid': '422031', - 'w': 600, - 'cid': '99006', - 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' - } - ], - 'debug': '' - } - }; - - const serverResponseTwoBids = { - 'body': { - 'id': '687a06c541d8d1', - 'site_id': 191642, - 'bid': [ - serverResponse.body.bid[0], - { - 'isNet': true, - 'id': '1234', - 'advid': '5280', - 'price': 1.23, - 'nurl': 'http://link/imp_pixel?ic=wVmhKI07hCVyGC1sNdFp.6buOSiGYOw8jPyZLlcMY2RCwD4ek3Fy6.xUI7U002skGBs3objMBoNU-Frpvmb9js3NKIG0YZJgWaNdcpXY9gOXE9hY4-wxybCjVSNzhOQB-zic73hzcnJnKeoGgcfvt8fMy18-yD0aVdYWt4zbqdoITOkKNCPBEgbPFu1rcje-o7a64yZ7H3dKvtnIixXQYc1Ep86xGSBGXY6xW2KfUOMT6vnkemxO72divMkMdhR8cAuqIubbx-ZID8-xf5c9k7p6DseeBW0I8ionrlTHx.rGosgxhiFaMqtr7HiA7PBzKvPdeEYN0hQ8RYo8JzYL82hA91A3V2m9Ij6y0DfIJnnrKN8YORffhxmJ6DzwEl1zjrVFbD01bqB3Vdww8w8PQJSkKQkd313tr-atU8LS26fnBmOngEkVHwAr2WCKxuUvxHmuVBTA-Lgz7wKwMoOJCA3hFxMavVb0ZFB7CK0BUTVU6z0De92Q.FJKNCHLMbjX3vcAQ90=', - 'h': 400, - 'pid': 1053688, - 'sync': [ - 'http://link3' + link: { + url: 'http://advertiser.com', + clicktrackers: [ + 'http://click.tracker.com/click?impid=123' + ] + }, + imptrackers: [ + 'http://imptrack1.com', + 'http://imptrack2.com' ], - 'crid': '422033', - 'w': 700, - 'cid': '99006', - 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' + jstracker: '', + privacy: 'https://www.myprivacyurl.com' } - ], - 'debug': '' - } - }; + } + ], + debug: '' + } + }; + + const nativeEventtrackers = [ + { + event: 1, + method: 1, + url: 'http://www.mytracker.com/imptracker' + }, + { + event: 1, + method: 2, + url: 'http://www.mytracker.com/tracker.js' + } + ]; + describe('interpretResponse', function () { let expectedBid = [ { - 'ad': '', + 'ad': '', 'adId': '33e9500b21129f', 'creativeId': '422031', 'cpm': 1.45888594164456, 'currency': 'USD', 'height': 290, + 'mediaType': 'banner', 'netRevenue': false, 'requestId': '33e9500b21129f', 'ttl': 300, @@ -230,12 +480,13 @@ describe('Improve Digital Adapter Tests', function () { let expectedTwoBids = [ expectedBid[0], { - 'ad': '', + 'ad': '', 'adId': '1234', 'creativeId': '422033', 'cpm': 1.23, 'currency': 'USD', 'height': 400, + 'mediaType': 'banner', 'netRevenue': true, 'requestId': '1234', 'ttl': 300, @@ -243,49 +494,113 @@ describe('Improve Digital Adapter Tests', function () { } ]; - it('should return a well-formed bid', () => { + let expectedBidNative = [ + { + mediaType: 'native', + adId: '33e9500b21129f', + creativeId: '422031', + cpm: 1.45888594164456, + currency: 'USD', + height: 290, + netRevenue: false, + requestId: '33e9500b21129f', + ttl: 300, + width: 600, + native: { + title: 'Native title', + body: 'Native body', + body2: 'body2', + cta: 'Do it', + sponsoredBy: 'Improve Digital', + rating: '4', + likes: '10105', + downloads: '150000', + price: '3.99', + salePrice: '4.49', + phone: '(123) 456-7890', + address: '123 Main Street, Anywhere USA', + displayUrl: 'https://myurl.com', + icon: { + url: 'http://blah.com/icon.jpg', + height: 30, + width: 40 + }, + image: { + url: 'http://blah.com/image.jpg', + height: 200, + width: 800 + }, + clickUrl: 'http://advertiser.com', + clickTrackers: ['http://click.tracker.com/click?impid=123'], + impressionTrackers: [ + 'http://ice.360yield.com/imp_pixel?ic=wVm', + 'http://imptrack1.com', + 'http://imptrack2.com' + ], + javascriptTrackers: '', + privacyLink: 'https://www.myprivacyurl.com' + } + } + ]; + + it('should return a well-formed bid', function () { const bids = spec.interpretResponse(serverResponse); expect(bids).to.deep.equal(expectedBid); }); - it('should return two bids', () => { + it('should return two bids', function () { const bids = spec.interpretResponse(serverResponseTwoBids); expect(bids).to.deep.equal(expectedTwoBids); }); - it('should register user syncs', () => { - const bids = spec.interpretResponse(serverResponse); - expect(registerSyncStub.withArgs('image', 'improvedigital', 'http://link1').calledOnce).to.equal(true); - expect(registerSyncStub.withArgs('image', 'improvedigital', 'http://link2').calledOnce).to.equal(true); - }); - - it('should set dealId correctly', () => { + it('should set dealId correctly', function () { let response = JSON.parse(JSON.stringify(serverResponse)); let bids; - response.body.bid[0].lid = 'xyz'; + delete response.body.bid[0].lid; + response.body.bid[0].buying_type = 'deal_id'; bids = spec.interpretResponse(response); expect(bids[0].dealId).to.not.exist; response.body.bid[0].lid = 268515; + delete response.body.bid[0].buying_type; bids = spec.interpretResponse(response); - expect(bids[0].dealId).to.equal(268515); + expect(bids[0].dealId).to.not.exist; - response.body.bid[0].lid = { - 1: 268515 - }; + response.body.bid[0].lid = 268515; + response.body.bid[0].buying_type = 'classic'; + bids = spec.interpretResponse(response); + expect(bids[0].dealId).to.not.exist; + + response.body.bid[0].lid = 268515; + response.body.bid[0].buying_type = 'deal_id'; bids = spec.interpretResponse(response); expect(bids[0].dealId).to.equal(268515); + + response.body.bid[0].lid = [ 268515, 12456, 34567 ]; + response.body.bid[0].buying_type = 'deal_id'; + bids = spec.interpretResponse(response); + expect(bids[0].dealId).to.not.exist; + + response.body.bid[0].lid = [ 268515, 12456, 34567 ]; + response.body.bid[0].buying_type = [ 'deal_id', 'classic' ]; + bids = spec.interpretResponse(response); + expect(bids[0].dealId).to.not.exist; + + response.body.bid[0].lid = [ 268515, 12456, 34567 ]; + response.body.bid[0].buying_type = [ 'classic', 'deal_id', 'deal_id' ]; + bids = spec.interpretResponse(response); + expect(bids[0].dealId).to.equal(12456); }); - it('should set currency', () => { + it('should set currency', function () { let response = JSON.parse(JSON.stringify(serverResponse)); response.body.bid[0].currency = 'eur'; const bids = spec.interpretResponse(response); expect(bids[0].currency).to.equal('EUR'); }); - it('should return empty array for bad response or no price', () => { + it('should return empty array for bad response or no price', function () { let response = JSON.parse(JSON.stringify(serverResponse)); let bids; @@ -306,7 +621,7 @@ describe('Improve Digital Adapter Tests', function () { bids = spec.interpretResponse(response); expect(bids).to.deep.equal([]); - // Adm missing or bad + // adm and native missing response = JSON.parse(JSON.stringify(serverResponse)); delete response.body.bid[0].adm; bids = spec.interpretResponse(response); @@ -314,19 +629,53 @@ describe('Improve Digital Adapter Tests', function () { response.body.bid[0].adm = null; bids = spec.interpretResponse(response); expect(bids).to.deep.equal([]); - response.body.bid[0].adm = 1234; - bids = spec.interpretResponse(response); - expect(bids).to.deep.equal([]); - response.body.bid[0].adm = {}; - bids = spec.interpretResponse(response); - expect(bids).to.deep.equal([]); }); - it('should set netRevenue', () => { + it('should set netRevenue', function () { let response = JSON.parse(JSON.stringify(serverResponse)); response.body.bid[0].isNet = true; const bids = spec.interpretResponse(response); expect(bids[0].netRevenue).to.equal(true); }); + + // Native ads + it('should return a well-formed native ad bid', function () { + let bids = spec.interpretResponse(serverResponseNative); + expect(bids[0].ortbNative).to.deep.equal(serverResponseNative.body.bid[0].native); + delete bids[0].ortbNative; + expect(bids).to.deep.equal(expectedBidNative); + + // eventtrackers + const response = JSON.parse(JSON.stringify(serverResponseNative)); + const expectedBids = JSON.parse(JSON.stringify(expectedBidNative)); + response.body.bid[0].native.eventtrackers = nativeEventtrackers; + expectedBids[0].native.impressionTrackers = [ + 'http://ice.360yield.com/imp_pixel?ic=wVm', + 'http://www.mytracker.com/imptracker' + ]; + expectedBids[0].native.javascriptTrackers = ''; + bids = spec.interpretResponse(response); + delete bids[0].ortbNative; + expect(bids).to.deep.equal(expectedBids); + }); + }); + + describe('getUserSyncs', function () { + const serverResponses = [ serverResponseTwoBids ]; + + it('should return no syncs when pixel syncing is disabled', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: false }, serverResponses); + expect(syncs).to.deep.equal([]); + }); + + it('should return user syncs', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true }, serverResponses); + const expected = [ + { type: 'image', url: 'http://link1' }, + { type: 'image', url: 'http://link2' }, + { type: 'image', url: 'http://link3' } + ]; + expect(syncs).to.deep.equal(expected); + }); }); }); diff --git a/test/spec/modules/innityBidAdapter_spec.js b/test/spec/modules/innityBidAdapter_spec.js index 87042ca6a6d..0132f093ca1 100644 --- a/test/spec/modules/innityBidAdapter_spec.js +++ b/test/spec/modules/innityBidAdapter_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { spec } from 'modules/innityBidAdapter'; -describe('innityAdapterTest', () => { - describe('bidRequestValidity', () => { - it('bidRequest with pub ID and zone ID param', () => { +describe('innityAdapterTest', function () { + describe('bidRequestValidity', function () { + it('bidRequest with pub ID and zone ID param', function () { expect(spec.isBidRequestValid({ bidder: 'innity', params: { @@ -13,7 +13,7 @@ describe('innityAdapterTest', () => { })).to.equal(true); }); - it('bidRequest with no required params', () => { + it('bidRequest with no required params', function () { expect(spec.isBidRequestValid({ bidder: 'innity', params: { @@ -22,7 +22,7 @@ describe('innityAdapterTest', () => { }); }); - describe('bidRequest', () => { + describe('bidRequest', function () { const bidRequests = [{ 'bidder': 'innity', 'params': { @@ -37,14 +37,14 @@ describe('innityAdapterTest', () => { 'auctionId': '18fd8b8b0bd757' }]; - it('bidRequest HTTP method', () => { + it('bidRequest HTTP method', function () { const requests = spec.buildRequests(bidRequests); requests.forEach(function(requestItem) { expect(requestItem.method).to.equal('GET'); }); }); - it('bidRequest data', () => { + it('bidRequest data', function () { const requests = spec.buildRequests(bidRequests); expect(requests[0].data.pub).to.equal(267); expect(requests[0].data.zone).to.equal(62546); @@ -54,7 +54,7 @@ describe('innityAdapterTest', () => { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const bidRequest = { 'method': 'GET', 'url': 'https://as.innity.com/synd/?', @@ -85,7 +85,7 @@ describe('innityAdapterTest', () => { headers: {} }; - it('result is correct', () => { + it('result is correct', function () { const result = spec.interpretResponse(bidResponse, bidRequest); expect(result[0].requestId).to.equal('51ef8751f9aead'); expect(result[0].cpm).to.equal(1); diff --git a/test/spec/modules/inskinBidAdapter_spec.js b/test/spec/modules/inskinBidAdapter_spec.js index f22e6242d53..896fe36d443 100644 --- a/test/spec/modules/inskinBidAdapter_spec.js +++ b/test/spec/modules/inskinBidAdapter_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/inskinBidAdapter'; - -var bidFactory = require('src/bidfactory.js'); +import { createBid } from 'src/bidfactory'; const ENDPOINT = 'https://mfad.inskinad.com/api/v2'; @@ -83,6 +82,9 @@ const RESPONSE = { 'type': 'html', 'body': '', 'data': { + 'customData': { + 'pubCPM': 1 + }, 'height': 90, 'width': 728, 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif', @@ -99,11 +101,11 @@ const RESPONSE = { } }; -describe('InSkin BidAdapter', () => { +describe('InSkin BidAdapter', function () { let bidRequests; let adapter = spec; - beforeEach(() => { + beforeEach(function () { bidRequests = [ { bidder: 'inskin', @@ -121,8 +123,8 @@ describe('InSkin BidAdapter', () => { ]; }); - describe('bid request validation', () => { - it('should accept valid bid requests', () => { + describe('bid request validation', function () { + it('should accept valid bid requests', function () { let bid = { bidder: 'inskin', params: { @@ -133,7 +135,7 @@ describe('InSkin BidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should accept valid bid requests with extra fields', () => { + it('should accept valid bid requests with extra fields', function () { let bid = { bidder: 'inskin', params: { @@ -145,7 +147,7 @@ describe('InSkin BidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should reject bid requests without siteId', () => { + it('should reject bid requests without siteId', function () { let bid = { bidder: 'inskin', params: { @@ -155,7 +157,7 @@ describe('InSkin BidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should reject bid requests without networkId', () => { + it('should reject bid requests without networkId', function () { let bid = { bidder: 'inskin', params: { @@ -166,45 +168,65 @@ describe('InSkin BidAdapter', () => { }); }); - describe('buildRequests validation', () => { - it('creates request data', () => { + describe('buildRequests validation', function () { + it('creates request data', function () { let request = spec.buildRequests(bidRequests); expect(request).to.exist.and.to.be.a('object'); }); - it('request to inskin should contain a url', () => { + it('request to inskin should contain a url', function () { let request = spec.buildRequests(bidRequests); expect(request.url).to.have.string('inskinad.com'); }); - it('requires valid bids to make request', () => { + it('requires valid bids to make request', function () { let request = spec.buildRequests([]); expect(request.bidRequest).to.be.empty; }); - it('sends bid request to ENDPOINT via POST', () => { + it('sends bid request to ENDPOINT via POST', function () { let request = spec.buildRequests(bidRequests); expect(request.method).to.have.string('POST'); }); + + it('should add gdpr consent information to the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'inskin', + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.consent.gdprConsentString).to.exist; + expect(payload.consent.gdprConsentRequired).to.exist; + expect(payload.consent.gdprConsentString).to.exist.and.to.equal(consentString); + expect(payload.consent.gdprConsentRequired).to.exist.and.to.be.true; + }); }); - describe('interpretResponse validation', () => { - it('response should have valid bidderCode', () => { + describe('interpretResponse validation', function () { + it('response should have valid bidderCode', function () { let bidRequest = spec.buildRequests(REQUEST.bidRequest); - let bid = bidFactory.createBid(1, bidRequest.bidRequest[0]); + let bid = createBid(1, bidRequest.bidRequest[0]); expect(bid.bidderCode).to.equal('inskin'); }); - it('response should include objects for all bids', () => { + it('response should include objects for all bids', function () { let bids = spec.interpretResponse(RESPONSE, REQUEST); expect(bids.length).to.equal(2); }); - it('registers bids', () => { + it('registers bids', function () { let bids = spec.interpretResponse(RESPONSE, REQUEST); bids.forEach(b => { expect(b).to.have.property('cpm'); @@ -222,34 +244,41 @@ describe('InSkin BidAdapter', () => { }); }); - it('handles nobid responses', () => { + it('cpm is correctly set', function () { + let bids = spec.interpretResponse(RESPONSE, REQUEST); + + expect(bids[0].cpm).to.equal(0.5); + expect(bids[1].cpm).to.equal(1); + }); + + it('handles nobid responses', function () { let EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {'decisions': null}}) let bids = spec.interpretResponse(EMPTY_RESP, REQUEST); expect(bids).to.be.empty; }); - it('handles no server response', () => { + it('handles no server response', function () { let bids = spec.interpretResponse(null, REQUEST); expect(bids).to.be.empty; }); }); - describe('getUserSyncs', () => { - it('handles empty sync options', () => { + describe('getUserSyncs', function () { + it('handles empty sync options', function () { let opts = spec.getUserSyncs({}); expect(opts).to.be.empty; }); - it('should return two sync urls if pixel syncs are enabled', () => { + it('should return two sync urls if pixel syncs are enabled', function () { let syncOptions = {'pixelEnabled': true}; let opts = spec.getUserSyncs(syncOptions); expect(opts.length).to.equal(2); }); - it('should return three sync urls if pixel and iframe syncs are enabled', () => { + it('should return three sync urls if pixel and iframe syncs are enabled', function () { let syncOptions = {'iframeEnabled': true, 'pixelEnabled': true}; let opts = spec.getUserSyncs(syncOptions); diff --git a/test/spec/modules/interactiveOffersBidAdapter_spec.js b/test/spec/modules/interactiveOffersBidAdapter_spec.js new file mode 100644 index 00000000000..8921a302738 --- /dev/null +++ b/test/spec/modules/interactiveOffersBidAdapter_spec.js @@ -0,0 +1,177 @@ +import {expect} from 'chai'; +import {spec} from 'modules/interactiveOffersBidAdapter'; + +describe('interactiveOffers adapter', function () { + describe('implementation', function () { + describe('for requests', function () { + it('should accept valid bid', function () { + let validBid = { + bidder: 'interactiveOffers', + params: { + pubId: '42' + } + }, + isValid = spec.isBidRequestValid(validBid); + + expect(isValid).to.equal(true); + }); + + it('should reject invalid bid', function () { + let invalidBid = { + bidder: 'interactiveOffers' + }, + isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + }); + describe('for requests', function () { + it('should accept valid bid with optional params', function () { + let validBid = { + bidder: 'interactiveOffers', + params: { + pubId: '42', + loc: 'http://test.com/prebid', + tmax: 1500 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + + let buildRequest = spec.buildRequests([validBid])[0]; + let requestUrlCustomParams = buildRequest.data; + expect(requestUrlCustomParams).have.property('loc', 'http://test.com/prebid'); + expect(requestUrlCustomParams).have.property('tmax', 1500); + }); + + it('should accept valid bid without optional params', function () { + let validBid = { + bidder: 'interactiveOffers', + params: { + pubId: '42' + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + + let buildRequest = spec.buildRequests([validBid])[0]; + let requestUrlCustomParams = buildRequest.data; + expect(requestUrlCustomParams).have.property('loc'); + expect(requestUrlCustomParams).have.property('tmax'); + }); + + it('should reject invalid bid without pubId', function () { + let invalidBid = { + bidder: 'interactiveOffers', + params: {} + }, + isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + }); + describe('bid responses', function () { + it('should return complete bid response', function () { + let serverResponse = { + body: { + 'success': 'true', + 'message': 'Request Valid', + 'payloadData': { + bidId: '3842b02f7ec0fd', + cpm: 0.5, + width: 300, + height: 600, + ad: '
    ...
    ', + } + } + }; + + let bidRequests = [ + { + bidder: 'interactiveOffers', + params: { + pubId: '42' + } + } + ]; + let bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(0.5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(600); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].ad).to.have.length.above(1); + }); + + it('should return empty bid response', function () { + let bidRequests = [ + { + bidder: 'interactiveOffers', + params: { + pubId: '42' + } + } + ]; + let serverResponse = { + body: { + 'success': 'true', + 'message': 'Request Valid', + 'payloadData': { + bidId: '3842b02f7ec0fd', + cpm: 0 + } + } + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response with error', function () { + let bidRequests = [ + { + bidder: 'interactiveOffers', + params: { + pubId: '42' + } + } + ]; + let serverResponse = {body: {'success': 'false', 'message': 'Request Error'}}, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response without payload', function () { + let bidRequests = [ + { + bidder: 'interactiveOffers', + params: { + pubId: '42' + } + } + ]; + let serverResponse = {body: {'success': 'true', 'message': 'Empty Payload', 'payloadData': []}}, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response on empty body', function () { + let bidRequests = [ + { + bidder: 'interactiveOffers', + params: { + pubId: '42' + } + } + ]; + let serverResponse, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + }); + }); +}); diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index 646448bd5a7..6391f168599 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from 'modules/invibesBidAdapter'; +import { spec, resetInvibes, stubDomainOptions } from 'modules/invibesBidAdapter'; describe('invibesBidAdapter:', function () { const BIDDER_CODE = 'invibes'; @@ -40,8 +40,23 @@ describe('invibesBidAdapter:', function () { } ]; + let StubbedPersistence = function(initialValue) { + var value = initialValue; + return { + load: function () { + let str = value || ''; + try { + return JSON.parse(str); + } catch (e) { } + }, + save: function (obj) { + value = JSON.stringify(obj); + } + } + }; + beforeEach(function () { - top.window.invibes = null; + resetInvibes(); document.cookie = ''; this.cStub1 = sinon.stub(console, 'info'); }); @@ -83,22 +98,34 @@ describe('invibesBidAdapter:', function () { expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); + + it('returns false when bid response was previously received', function() { + const validBid = { + bidder: BIDDER_CODE, + params: { + placementId: PLACEMENT_ID + } + } + + top.window.invibes.bidResponse = { prop: 'prop' }; + expect(spec.isBidRequestValid(validBid)).to.be.false; + }); }); }); - describe('buildRequests', () => { - it('sends bid request to ENDPOINT via GET', () => { + describe('buildRequests', function () { + it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET'); }); - it('sends cookies with the bid request', () => { + it('sends cookies with the bid request', function () { const request = spec.buildRequests(bidRequests); expect(request.options.withCredentials).to.equal(true); }); - it('has location, html id, placement and width/height', () => { + it('has location, html id, placement and width/height', function () { const request = spec.buildRequests(bidRequests, { auctionStart: Date.now() }); const parsedData = request.data; expect(parsedData.location).to.exist; @@ -108,42 +135,73 @@ describe('invibesBidAdapter:', function () { expect(parsedData.height).to.exist; }); - it('sends all Placement Ids', () => { + it('sends all Placement Ids', function () { const request = spec.buildRequests(bidRequests); expect(JSON.parse(request.data.bidParamsJson).placementIds).to.contain(bidRequests[0].params.placementId); expect(JSON.parse(request.data.bidParamsJson).placementIds).to.contain(bidRequests[1].params.placementId); }); - it('uses cookies', () => { + it('uses cookies', function () { global.document.cookie = 'ivNoCookie=1'; let request = spec.buildRequests(bidRequests); expect(request.data.lId).to.be.undefined; }); - it('does not overwrite the domain id', () => { - top.window.invibes = window.invibes || {}; - top.window.invibes.dom = {}; + it('doesnt send the domain id if not graduated', function () { + global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1522929537626,"hc":1}'; let request = spec.buildRequests(bidRequests); + expect(request.data.lId).to.not.exist; }); - it('doesnt send the domain id if not graduated', () => { - global.document.cookie = 'ivbsdid={"id":"p4vauj.4ekt9w","hc":3,"temp":1}'; - let request = spec.buildRequests(bidRequests); + it('try to graduate but not enough count - doesnt send the domain id', function () { + global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1521818537626,"hc":5}'; + let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } }; + let request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.lId).to.not.exist; }); - it('graduate and send the domain id', () => { - global.document.cookie = 'ivbsdid={"id":"p4rrk7.ax2i2s","hc":4,"temp":1}'; - let request = spec.buildRequests(bidRequests); + it('try to graduate but not old enough - doesnt send the domain id', function () { + let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } }; + global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":5}'; + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.lId).to.not.exist; + }); + + it('graduate and send the domain id', function () { + let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } }; + stubDomainOptions(new StubbedPersistence('{"id":"dvdjkams6nkq","cr":1521818537626,"hc":7}')); + let request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.lId).to.exist; }); - it('send the domain id if already graduated', () => { - global.document.cookie = 'ivbsdid={"id":"p4rrk7.ax2i2s"}'; - let request = spec.buildRequests(bidRequests); + it('send the domain id if already graduated', function () { + let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } }; + stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi"}')); + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.lId).to.exist; + }); + + it('send the domain id after replacing it with new format', function () { + let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } }; + stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}')); + let request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.lId).to.exist; }); - }) + + it('dont send the domain id if consent declined', function () { + let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: false } } } }; + stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}')); + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.lId).to.not.exist; + }); + + it('dont send the domain id if no consent', function () { + let bidderRequest = { }; + stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}')); + let request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.lId).to.not.exist; + }); + }); describe('interpretResponse', function () { let response = { @@ -177,55 +235,55 @@ describe('invibesBidAdapter:', function () { }]; context('when the response is not valid', function () { - it('handles response with no bids requested', () => { + it('handles response with no bids requested', function () { let emptyResult = spec.interpretResponse({ body: response }); expect(emptyResult).to.be.empty; }); - it('handles empty response', () => { + it('handles empty response', function () { let emptyResult = spec.interpretResponse(null, { bidRequests }); expect(emptyResult).to.be.empty; }); - it('handles response with bidding is not configured', () => { + it('handles response with bidding is not configured', function () { let emptyResult = spec.interpretResponse({ body: { Ads: [{ BidPrice: 1 }] } }, { bidRequests }); expect(emptyResult).to.be.empty; }); - it('handles response with no ads are received', () => { + it('handles response with no ads are received', function () { let emptyResult = spec.interpretResponse({ body: { BidModel: { PlacementId: '12345' }, AdReason: 'No ads' } }, { bidRequests }); expect(emptyResult).to.be.empty; }); - it('handles response with no ads are received - no ad reason', () => { + it('handles response with no ads are received - no ad reason', function () { let emptyResult = spec.interpretResponse({ body: { BidModel: { PlacementId: '12345' } } }, { bidRequests }); expect(emptyResult).to.be.empty; }); - it('handles response when no placement Id matches', () => { + it('handles response when no placement Id matches', function () { let emptyResult = spec.interpretResponse({ body: { BidModel: { PlacementId: '123456' }, Ads: [{ BidPrice: 1 }] } }, { bidRequests }); expect(emptyResult).to.be.empty; }); - it('handles response when placement Id is not present', () => { + it('handles response when placement Id is not present', function () { let emptyResult = spec.interpretResponse({ BidModel: { }, Ads: [{ BidPrice: 1 }] }, { bidRequests }); expect(emptyResult).to.be.empty; }); }); context('when the response is valid', function () { - it('responds with a valid bid', () => { + it('responds with a valid bid', function () { let result = spec.interpretResponse({ body: response }, { bidRequests }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('responds with a valid bid and uses logger', () => { + it('responds with a valid bid and uses logger', function () { localStorage.InvibesDEBUG = true; let result = spec.interpretResponse({ body: response }, { bidRequests }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('does not make multiple bids', () => { + it('does not make multiple bids', function () { localStorage.InvibesDEBUG = false; let result = spec.interpretResponse({ body: response }, { bidRequests }); let secondResult = spec.interpretResponse({ body: response }, { bidRequests }); @@ -235,13 +293,24 @@ describe('invibesBidAdapter:', function () { }); describe('getUserSyncs', function () { - it('returns an iframe if enabled', () => { + it('returns an iframe if enabled', function () { let response = spec.getUserSyncs({iframeEnabled: true}); expect(response.type).to.equal('iframe'); - expect(response.url).to.equal(SYNC_ENDPOINT); + expect(response.url).to.include(SYNC_ENDPOINT); + }); + + it('returns an iframe with params if enabled', function () { + top.window.invibes.optIn = 1; + global.document.cookie = 'ivvbks=17639.0,1,2'; + let response = spec.getUserSyncs({ iframeEnabled: true }); + expect(response.type).to.equal('iframe'); + expect(response.url).to.include(SYNC_ENDPOINT); + expect(response.url).to.include('optIn'); + expect(response.url).to.include('ivvbks'); + expect(response.url).to.include('ivbsdid'); }); - it('returns undefined if iframe not enabled ', () => { + it('returns undefined if iframe not enabled ', function () { let response = spec.getUserSyncs({ iframeEnabled: false }); expect(response).to.equal(undefined); }); diff --git a/test/spec/modules/iqmBidAdapter_spec.js b/test/spec/modules/iqmBidAdapter_spec.js index 8958a4dfc45..5535c52af9b 100644 --- a/test/spec/modules/iqmBidAdapter_spec.js +++ b/test/spec/modules/iqmBidAdapter_spec.js @@ -2,7 +2,7 @@ import {expect} from 'chai'; import {spec} from 'modules/iqmBidAdapter' import * as utils from 'src/utils'; -describe('iqmBidAdapter', () => { +describe('iqmBidAdapter', function () { const ENDPOINT_URL = 'https://pbd.bids.iqm.com'; const bidRequests = [{ bidder: 'iqm', @@ -80,15 +80,15 @@ describe('iqmBidAdapter', () => { headers: {} }; - describe('Request verification', () => { - it('basic property verification', () => { + describe('Request verification', function () { + it('basic property verification', function () { expect(spec.code).to.equal('iqm'); expect(spec.aliases).to.be.an('array'); // expect(spec.aliases).to.be.ofSize(1); expect(spec.aliases).to.have.lengthOf(1); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'iqm', 'params': { @@ -103,17 +103,17 @@ describe('iqmBidAdapter', () => { 'auctionId': '1d1a030790a475' }; - it('should return false for empty object', () => { + it('should return false for empty object', function () { expect(spec.isBidRequestValid({})).to.equal(false); }); - it('should return false for request without param', () => { + it('should return false for request without param', function () { let bid = Object.assign({}, bid); delete bid.params; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false for invalid params', () => { + it('should return false for invalid params', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -122,13 +122,13 @@ describe('iqmBidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return true for proper request', () => { + it('should return true for proper request', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); }); - describe('buildRequests', () => { - it('sends every bid request to ENDPOINT_URL via POST method', () => { + describe('buildRequests', function () { + it('sends every bid request to ENDPOINT_URL via POST method', function () { const requests = spec.buildRequests(bidRequests); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(ENDPOINT_URL); @@ -136,7 +136,7 @@ describe('iqmBidAdapter', () => { // expect(requests[1].url).to.equal(ENDPOINT_URL); }); - it('should send request data with every request', () => { + it('should send request data with every request', function () { const requests = spec.buildRequests(bidRequests); const data = requests[0].data; expect(data.id).to.equal(bidRequests[0].requestId); @@ -175,31 +175,31 @@ describe('iqmBidAdapter', () => { }); }); - describe('interpretResponse', () => { - it('should handle no bid response', () => { + describe('interpretResponse', function () { + it('should handle no bid response', function () { const response = spec.interpretResponse({ body: null }, { bidRequests }); expect(response.length).to.equal(0); }); - it('should have at least one Seat Object', () => { + it('should have at least one Seat Object', function () { const request = spec.buildRequests(bidRequests); const response = spec.interpretResponse(bidResponseEmptySeat, request); expect(response.length).to.equal(0); }); - it('should have at least one Bid Object', () => { + it('should have at least one Bid Object', function () { const request = spec.buildRequests(bidRequests); const response = spec.interpretResponse(bidResponseEmptyBid, request); expect(response.length).to.equal(0); }); - it('should have impId in Bid Object', () => { + it('should have impId in Bid Object', function () { const request = spec.buildRequests(bidRequests); const response = spec.interpretResponse(bidResponseNoImpId, request); expect(response.length).to.equal(0); }); - it('should handle valid response', () => { + it('should handle valid response', function () { const request = spec.buildRequests(bidRequests); const response = spec.interpretResponse(bidResponses, request); expect(response).to.be.an('array').to.have.lengthOf(1); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 0e00a64aab4..38e64e8d338 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -4,8 +4,9 @@ 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'; +describe('IndexexchangeAdapter', function () { + const IX_INSECURE_ENDPOINT = 'http://as.casalemedia.com/cygnus'; + const IX_SECURE_ENDPOINT = 'https://as-sec.casalemedia.com/cygnus'; const BIDDER_VERSION = 7.2; const DEFAULT_BANNER_VALID_BID = [ @@ -28,6 +29,17 @@ describe('IndexexchangeAdapter', () => { auctionId: '1aa2bb3cc4dd' } ]; + const DEFAULT_BANNER_OPTION = { + gdprConsent: { + gdprApplies: true, + consentString: '3huaa11=qu3198ae', + vendorData: {} + }, + refererInfo: { + referer: 'http://www.prebid.org', + canonicalUrl: 'http://www.prebid.org/the/link/to/the/page' + } + }; const DEFAULT_BANNER_BID_RESPONSE = { cur: 'USD', id: '11a22b33c44d', @@ -57,45 +69,58 @@ describe('IndexexchangeAdapter', () => { } ] }; + const DEFAULT_IDENTITY_RESPONSE = { + IdentityIp: { + responsePending: false, + data: { + source: 'identityinc.com', + uids: [ + { + id: 'identityid' + } + ] + } + } + }; - describe('inherited functions', () => { - it('should exists and is a function', () => { + describe('inherited functions', function () { + it('should exists and is a function', 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', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found for a banner ad', function () { expect(spec.isBidRequestValid(DEFAULT_BANNER_VALID_BID[0])).to.equal(true); }); - it('should return true when optional params found for a banner ad', () => { + it('should return true when optional params found for a banner ad', function () { 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', () => { + it('should return true when siteID is number', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.siteId = 123; - expect(spec.isBidRequestValid(bid)).to.equal(false); + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when siteID is missing', () => { + it('should return false when siteID is missing', function () { 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', () => { + it('should return false when size is missing', function () { 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', () => { + it('should return false when size array is wrong length', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.size = [ 300, @@ -105,32 +130,91 @@ describe('IndexexchangeAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when size array is array of strings', () => { + it('should return false when size array is array of strings', function () { 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', () => { + it('should return false when mediaTypes is not banner', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.mediaTypes = { + video: { + sizes: [[300, 250]] + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaTypes.banner does not have sizes', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.mediaTypes = { + banner: { + size: [[300, 250]] + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaType is not banner', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + delete bid.params.mediaTypes; + bid.mediaType = 'banne'; + bid.sizes = [[300, 250]]; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaType is video', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + delete bid.params.mediaTypes; + bid.mediaType = 'video'; + bid.sizes = [[300, 250]]; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaType is native', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + delete bid.params.mediaTypes; + bid.mediaType = 'native'; + bid.sizes = [[300, 250]]; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when mediaType is missing and has sizes', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + delete bid.mediaTypes; + bid.sizes = [[300, 250]]; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when mediaType is banner', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + delete bid.mediaTypes; + bid.mediaType = 'banner'; + bid.sizes = [[300, 250]]; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when there is only bidFloor', function () { 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', () => { + it('should return false when there is only bidFloorCur', function () { 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', () => { + it('should return false when bidFloor is string', function () { 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', () => { + it('should return false when bidFloorCur is number', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; bid.params.bidFloorCur = 70; @@ -138,18 +222,189 @@ describe('IndexexchangeAdapter', () => { }); }); - describe('buildRequestsBanner', () => { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + describe('buildRequestsIdentity', function () { + let request; + let query; + let testCopy; + + beforeEach(function() { + window.headertag = {}; + window.headertag.getIdentityInfo = function() { + return testCopy; + }; + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + query = request.data; + }); + afterEach(function() { + delete window.headertag; + }); + describe('buildRequestSingleRTI', function() { + before(function() { + testCopy = JSON.parse(JSON.stringify(DEFAULT_IDENTITY_RESPONSE)); + }); + it('payload should have correct format and value (single identity partner)', function () { + const payload = JSON.parse(query.r); + + expect(payload.user).to.exist; + expect(payload.user.eids).to.exist; + expect(payload.user.eids).to.be.an('array'); + expect(payload.user.eids).to.have.lengthOf(1); + }); + + it('identity data in impression should have correct format and value (single identity partner)', function () { + const impression = JSON.parse(query.r).user.eids; + expect(impression[0].source).to.equal(testCopy.IdentityIp.data.source); + expect(impression[0].uids[0].id).to.equal(testCopy.IdentityIp.data.uids[0].id); + }); + }); + + describe('buildRequestMultipleIds', function() { + before(function() { + testCopy = JSON.parse(JSON.stringify(DEFAULT_IDENTITY_RESPONSE)); + testCopy.IdentityIp.data.uids.push({ + id: '1234567' + }, + { + id: '2019-04-01TF2:34:41' + }); + }); + it('payload should have correct format and value (single identity w/ multi ids)', function () { + const payload = JSON.parse(query.r); + + expect(payload.user).to.exist; + expect(payload.user.eids).to.exist; + expect(payload.user.eids).to.be.an('array'); + expect(payload.user.eids).to.have.lengthOf(1); + }); + + it('identity data in impression should have correct format and value (single identity w/ multi ids)', function () { + const impression = JSON.parse(query.r).user.eids; + + expect(impression[0].source).to.equal(testCopy.IdentityIp.data.source); + expect(impression[0].uids).to.have.lengthOf(3); + }); + }); + + describe('buildRequestMultipleRTI', function() { + before(function() { + testCopy = JSON.parse(JSON.stringify(DEFAULT_IDENTITY_RESPONSE)); + testCopy.JackIp = { + responsePending: false, + data: { + source: 'jackinc.com', + uids: [ + { + id: 'jackid' + } + ] + } + } + testCopy.GenericIp = { + responsePending: false, + data: { + source: 'genericip.com', + uids: [ + { + id: 'genericipenvelope' + } + ] + } + } + }); + it('payload should have correct format and value (multiple identity partners)', function () { + const payload = JSON.parse(query.r); + + expect(payload.user).to.exist; + expect(payload.user.eids).to.exist; + expect(payload.user.eids).to.be.an('array'); + expect(payload.user.eids).to.have.lengthOf(3); + }); + + it('identity data in impression should have correct format and value (multiple identity partners)', function () { + const impression = JSON.parse(query.r).user.eids; + + expect(impression[0].source).to.equal(testCopy.IdentityIp.data.source); + expect(impression[0].uids).to.have.lengthOf(1); + + expect(impression[1].source).to.equal(testCopy.JackIp.data.source); + expect(impression[1].uids).to.have.lengthOf(1); + + expect(impression[2].source).to.equal(testCopy.GenericIp.data.source); + expect(impression[2].uids).to.have.lengthOf(1); + }); + }); + + describe('buildRequestNoData', function() { + beforeEach(function() { + testCopy = JSON.parse(JSON.stringify(DEFAULT_IDENTITY_RESPONSE)); + }); + + it('payload should not have any user eids with an undefined identity data response', function () { + window.headertag.getIdentityInfo = function() { + return undefined; + }; + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + query = request.data; + const payload = JSON.parse(query.r); + + expect(payload.user).to.exist; + expect(payload.user.eids).to.not.exist; + }); + + it('payload should have user eids if least one partner data is available', function () { + testCopy.GenericIp = { + responsePending: true, + data: {} + } + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + query = request.data; + const payload = JSON.parse(query.r); + + expect(payload.user).to.exist; + expect(payload.user.eids).to.exist; + }); + + it('payload should not have any user eids if identity data is pending for all partners', function () { + testCopy.IdentityIp.responsePending = true; + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + query = request.data; + const payload = JSON.parse(query.r); + + expect(payload.user).to.exist; + expect(payload.user.eids).to.not.exist; + }); + + it('payload should not have any user eids if identity data is pending or not available for all partners', function () { + testCopy.IdentityIp.responsePending = false; + testCopy.IdentityIp.data = {}; + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + query = request.data; + const payload = JSON.parse(query.r); + + expect(payload.user).to.exist; + expect(payload.user.eids).to.not.exist; + }); + }); + }); + + describe('buildRequestsBanner', function () { + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); const requestUrl = request.url; const requestMethod = request.method; const query = request.data; - it('request should be made to IX endpoint with GET method', () => { + const bidWithoutMediaType = utils.deepClone(DEFAULT_BANNER_VALID_BID); + delete bidWithoutMediaType[0].mediaTypes; + bidWithoutMediaType[0].sizes = [[300, 250], [300, 600]]; + const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType, DEFAULT_BANNER_OPTION); + const queryWithoutMediaType = requestWithoutMediaType.data; + + it('request should be made to IX endpoint with GET method', function () { expect(requestMethod).to.equal('GET'); - expect(requestUrl).to.equal(IX_ENDPOINT); + expect(requestUrl).to.equal(IX_INSECURE_ENDPOINT); }); - it('query object (version, siteID and request) should be correct', () => { + it('query object (version, siteID and request) should be correct', function () { expect(query.v).to.equal(BIDDER_VERSION); expect(query.s).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId); expect(query.r).to.exist; @@ -157,15 +412,13 @@ describe('IndexexchangeAdapter', () => { expect(query.sd).to.equal(1); }); - it('payload should have correct format and value', () => { + it('payload should have correct format and value', function () { 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.site.page).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(payload.site.ref).to.equal(document.referrer); expect(payload.ext).to.exist; expect(payload.ext.source).to.equal('prebid'); expect(payload.imp).to.exist; @@ -173,7 +426,7 @@ describe('IndexexchangeAdapter', () => { expect(payload.imp).to.have.lengthOf(1); }); - it('impression should have correct format and value', () => { + it('impression should have correct format and value', function () { 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()}`; @@ -188,7 +441,7 @@ describe('IndexexchangeAdapter', () => { expect(impression.ext.sid).to.equal(sidValue); }); - it('impression should have bidFloor and bidFloorCur if configured', () => { + it('impression should have bidFloor and bidFloorCur if configured', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; bid.params.bidFloorCur = 'USD'; @@ -199,7 +452,69 @@ describe('IndexexchangeAdapter', () => { 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', () => { + it('payload without mediaType should have correct format and value', function () { + const payload = JSON.parse(queryWithoutMediaType.r); + + expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); + expect(payload.site).to.exist; + expect(payload.site.page).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(payload.site.ref).to.equal(document.referrer); + 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 without mediaType should have correct format and value', function () { + const impression = JSON.parse(queryWithoutMediaType.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 sid if id is configured as number', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.params.id = 50; + const requestBidFloor = spec.buildRequests([bid]); + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + + 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('50'); + }); + + it('impression should have sid if id is configured as string', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.params.id = 'abc'; + const requestBidFloor = spec.buildRequests([bid]); + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + 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('abc'); + }); + + it('should add first party data to page url in bid request if it exists in config', function () { config.setConfig({ ix: { firstPartyData: { @@ -211,47 +526,47 @@ describe('IndexexchangeAdapter', () => { } }); - const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); 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`; + const expectedPageUrl = DEFAULT_BANNER_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; expect(pageUrl).to.equal(expectedPageUrl); }); - it('should not set first party data if it is not an object', () => { + it('should not set first party data if it is not an object', function () { config.setConfig({ ix: { firstPartyData: 500 } }); - const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); const pageUrl = JSON.parse(requestFirstPartyDataNumber.data.r).site.page; - expect(pageUrl).to.equal(utils.getTopWindowUrl()); + expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); }); - it('should not set first party or timeout if it is not present', () => { + it('should not set first party or timeout if it is not present', function () { config.setConfig({ ix: {} }); - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - expect(pageUrl).to.equal(utils.getTopWindowUrl()); + expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); expect(requestWithoutConfig.data.t).to.be.undefined; }); - it('should not set first party or timeout if it is setConfig is not called', () => { - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + it('should not set first party or timeout if it is setConfig is not called', function () { + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - expect(pageUrl).to.equal(utils.getTopWindowUrl()); + expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); expect(requestWithoutConfig.data.t).to.be.undefined; }); - it('should set timeout if publisher set it through setConfig', () => { + it('should set timeout if publisher set it through setConfig', function () { config.setConfig({ ix: { timeout: 500 @@ -262,7 +577,7 @@ describe('IndexexchangeAdapter', () => { expect(requestWithTimeout.data.t).to.equal(500); }); - it('should set timeout if timeout is a string', () => { + it('should set timeout if timeout is a string', function () { config.setConfig({ ix: { timeout: '500' @@ -274,8 +589,8 @@ describe('IndexexchangeAdapter', () => { }); }); - describe('interpretResponseBanner', () => { - it('should get correct bid response', () => { + describe('interpretResponseBanner', function () { + it('should get correct bid response', function () { const expectedParse = [ { requestId: '1a2b3c4d', @@ -285,16 +600,21 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '
    ', currency: 'USD', - ttl: 60, + ttl: 35, netRevenue: true, - dealId: undefined + dealId: undefined, + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA' + } } ]; 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', () => { + it('should set creativeId to default value if not provided', function () { const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); delete bidResponse.seatbid[0].bid[0].crid; const expectedParse = [ @@ -306,16 +626,21 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '', currency: 'USD', - ttl: 60, + ttl: 35, netRevenue: true, - dealId: undefined + dealId: undefined, + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA' + } } ]; const result = spec.interpretResponse({ body: bidResponse }); expect(result[0]).to.deep.equal(expectedParse[0]); }); - it('should set Japanese price correctly', () => { + it('should set Japanese price correctly', function () { const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); bidResponse.cur = 'JPY'; const expectedParse = [ @@ -327,16 +652,21 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '', currency: 'JPY', - ttl: 60, + ttl: 35, netRevenue: true, - dealId: undefined + dealId: undefined, + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA' + } } ]; const result = spec.interpretResponse({ body: bidResponse }); expect(result[0]).to.deep.equal(expectedParse[0]); }); - it('should set dealId correctly', () => { + it('should set dealId correctly', function () { const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); bidResponse.seatbid[0].bid[0].ext.dealid = 'deal'; const expectedParse = [ @@ -348,31 +678,29 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '', currency: 'USD', - ttl: 60, + ttl: 35, netRevenue: true, - dealId: 'deal' + dealId: 'deal', + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA' + } } ]; const result = spec.interpretResponse({ body: bidResponse }); expect(result[0]).to.deep.equal(expectedParse[0]); }); - it('bidrequest should have consent info if gdprApplies and consentString exist', () => { - const options = { - gdprConsent: { - gdprApplies: true, - consentString: '3huaa11=qu3198ae', - vendorData: {} - } - }; - const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + it('bidrequest should have consent info if gdprApplies and consentString exist', function () { + const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); const requestWithConsent = JSON.parse(validBidWithConsent.data.r); expect(requestWithConsent.regs.ext.gdpr).to.equal(1); expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); }); - it('bidrequest should not have consent field if consentString is undefined', () => { + it('bidrequest should not have consent field if consentString is undefined', function () { const options = { gdprConsent: { gdprApplies: true, @@ -386,7 +714,7 @@ describe('IndexexchangeAdapter', () => { expect(requestWithConsent.user).to.be.undefined; }); - it('bidrequest should not have gdpr field if gdprApplies is undefined', () => { + it('bidrequest should not have gdpr field if gdprApplies is undefined', function () { const options = { gdprConsent: { consentString: '3huaa11=qu3198ae', @@ -400,7 +728,7 @@ describe('IndexexchangeAdapter', () => { expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); }); - it('bidrequest should not have consent info if options.gdprConsent is undefined', () => { + it('bidrequest should not have consent info if options.gdprConsent is undefined', function () { const options = {}; const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); const requestWithConsent = JSON.parse(validBidWithConsent.data.r); @@ -408,5 +736,38 @@ describe('IndexexchangeAdapter', () => { expect(requestWithConsent.regs).to.be.undefined; expect(requestWithConsent.user).to.be.undefined; }); + + it('bidrequest should not have page if options is undefined', function () { + const options = {}; + const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + + expect(requestWithoutreferInfo.site.page).to.be.undefined; + expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + }); + + it('bidrequest should not have page if options.refererInfo is an empty object', function () { + const options = { + refererInfo: {} + }; + const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + + expect(requestWithoutreferInfo.site.page).to.be.undefined; + expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + }); + + it('bidrequest should sent to secure endpoint if page url is secure', function () { + const options = { + refererInfo: { + referer: 'https://www.prebid.org' + } + }; + const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + + expect(requestWithoutreferInfo.site.page).to.equal(options.refererInfo.referer); + expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + }); }); }); diff --git a/test/spec/modules/jcmBidAdapter_spec.js b/test/spec/modules/jcmBidAdapter_spec.js index 27784def4f9..0b467e1ecfb 100644 --- a/test/spec/modules/jcmBidAdapter_spec.js +++ b/test/spec/modules/jcmBidAdapter_spec.js @@ -4,16 +4,16 @@ import { newBidder } from 'src/adapters/bidderFactory'; const ENDPOINT = '//media.adfrontiers.com/'; -describe('jcmAdapter', () => { +describe('jcmAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'jcm', 'params': { @@ -26,18 +26,18 @@ describe('jcmAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': 'jcm', @@ -55,11 +55,11 @@ describe('jcmAdapter', () => { const request = spec.buildRequests(bidRequests); - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { expect(request.method).to.equal('GET'); }); - it('sends correct bid parameters', () => { + it('sends correct bid parameters', function () { const payloadArr = request.data.split('&'); expect(request.method).to.equal('GET'); expect(payloadArr.length).to.equal(4); @@ -75,8 +75,8 @@ describe('jcmAdapter', () => { }); }); - describe('interpretResponse', () => { - it('should get correct bid response', () => { + describe('interpretResponse', function () { + it('should get correct bid response', function () { let serverResponse = {'bids': [{'width': 300, 'height': 250, 'creativeId': '29681110', 'ad': '', 'cpm': 0.5, 'callbackId': '30b31c1838de1e'}]}; let expectedResponse = [ @@ -109,15 +109,15 @@ describe('jcmAdapter', () => { expect(Object.keys(result[0]).ad).to.equal(Object.keys(expectedResponse[0]).ad); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let serverResponse = {'bids': []}; let result = spec.interpretResponse({ body: serverResponse }); expect(result.length).to.equal(0); }); }); - describe('getUserSyncs', () => { - it('Verifies sync iframe option', () => { + describe('getUserSyncs', function () { + it('Verifies sync iframe option', function () { expect(spec.getUserSyncs({})).to.be.undefined; expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; const options = spec.getUserSyncs({ iframeEnabled: true }); @@ -127,7 +127,7 @@ describe('jcmAdapter', () => { expect(options[0].url).to.equal('//media.adfrontiers.com/hb/jcm_usersync.html'); }); - it('Verifies sync image option', () => { + it('Verifies sync image option', function () { expect(spec.getUserSyncs({ image: false })).to.be.undefined; const options = spec.getUserSyncs({ image: true }); expect(options).to.not.be.undefined; diff --git a/test/spec/modules/justpremiumBidAdapter_spec.js b/test/spec/modules/justpremiumBidAdapter_spec.js index 226a5788cef..c3cd015b6e3 100644 --- a/test/spec/modules/justpremiumBidAdapter_spec.js +++ b/test/spec/modules/justpremiumBidAdapter_spec.js @@ -1,16 +1,42 @@ import { expect } from 'chai' -import { spec } from 'modules/justpremiumBidAdapter' +import { spec, pixel } from 'modules/justpremiumBidAdapter' + +describe('justpremium adapter', function () { + let sandbox; + let pixelStub; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + pixelStub = sandbox.stub(pixel, 'fire'); + }); + + afterEach(function() { + sandbox.restore(); + }); -describe('justpremium adapter', () => { let adUnits = [ { + adUnitCode: 'div-gpt-ad-1471513102552-1', bidder: 'justpremium', + crumbs: { + pubcid: '0000000' + }, + userId: { + tdid: '1111111', + id5id: '2222222', + digitrustid: { + data: { + id: '3333333' + } + } + }, params: { zone: 28313, allow: ['lb', 'wp'] } }, { + adUnitCode: 'div-gpt-ad-1471513102552-2', bidder: 'justpremium', params: { zone: 32831, @@ -19,12 +45,18 @@ describe('justpremium adapter', () => { }, ] - describe('isBidRequestValid', () => { - it('Verifies bidder code', () => { + let bidderRequest = { + refererInfo: { + referer: 'http://justpremium.com' + } + } + + describe('isBidRequestValid', function () { + it('Verifies bidder code', function () { expect(spec.code).to.equal('justpremium') }) - it('Verify build request', () => { + it('Verify build request', function () { expect(spec.isBidRequestValid({bidder: 'justpremium', params: {}})).to.equal(false) expect(spec.isBidRequestValid({})).to.equal(false) expect(spec.isBidRequestValid(adUnits[0])).to.equal(true) @@ -32,17 +64,16 @@ describe('justpremium adapter', () => { }) }) - describe('buildRequests', () => { - it('Verify build request and parameters', () => { - const request = spec.buildRequests(adUnits) + describe('buildRequests', function () { + it('Verify build request and parameters', function () { + const request = spec.buildRequests(adUnits, bidderRequest) expect(request.method).to.equal('POST') expect(request.url).to.match(/pre.ads.justpremium.com\/v\/2.0\/t\/xhr/) const jpxRequest = JSON.parse(request.data) expect(jpxRequest).to.not.equal(null) expect(jpxRequest.zone).to.not.equal('undefined') - expect(jpxRequest.hostname).to.equal(top.document.location.hostname) - expect(jpxRequest.protocol).to.equal(top.document.location.protocol.replace(':', '')) + expect(bidderRequest.refererInfo.referer).to.equal('http://justpremium.com') expect(jpxRequest.sw).to.equal(window.top.screen.width) expect(jpxRequest.sh).to.equal(window.top.screen.height) expect(jpxRequest.ww).to.equal(window.top.innerWidth) @@ -50,12 +81,18 @@ describe('justpremium adapter', () => { expect(jpxRequest.c).to.not.equal('undefined') expect(jpxRequest.id).to.equal(adUnits[0].params.zone) expect(jpxRequest.sizes).to.not.equal('undefined') + expect(jpxRequest.version.prebid).to.equal('$prebid.version$') + expect(jpxRequest.version.jp_adapter).to.equal('1.4') + expect(jpxRequest.pubcid).to.equal('0000000') + expect(jpxRequest.uids.tdid).to.equal('1111111') + expect(jpxRequest.uids.id5id).to.equal('2222222') + expect(jpxRequest.uids.digitrustid.data.id).to.equal('3333333') }) }) - describe('interpretResponse', () => { - const request = spec.buildRequests(adUnits) - it('Verify server response', () => { + describe('interpretResponse', function () { + const request = spec.buildRequests(adUnits, bidderRequest) + it('Verify server response', function () { let response = { 'bid': { '28313': [{ @@ -83,7 +120,8 @@ describe('justpremium adapter', () => { cpm: 0.52, netRevenue: true, currency: 'USD', - ttl: 60000 + ttl: 60000, + format: 'lb' } ] @@ -99,9 +137,10 @@ describe('justpremium adapter', () => { expect(result[0].ttl).to.equal(60000) expect(result[0].creativeId).to.equal(3213123) expect(result[0].netRevenue).to.equal(true) + expect(result[0].format).to.equal('lb') }) - it('Verify wrong server response', () => { + it('Verify wrong server response', function () { let response = { 'bid': { '28313': [] @@ -116,12 +155,42 @@ describe('justpremium adapter', () => { }) }) - describe('getUserSyncs', () => { - it('Verifies sync options', () => { + describe('getUserSyncs', function () { + it('Verifies sync options', function () { const options = spec.getUserSyncs({iframeEnabled: true}) expect(options).to.not.be.undefined expect(options[0].type).to.equal('iframe') - expect(options[0].src).to.match(/\/\/us-u.openx.net\/w\/1.0/) + expect(options[0].url).to.match(/\/\/pre.ads.justpremium.com\/v\/1.0\/t\/sync/) + }) + }) + + describe('onTimeout', function () { + it('onTimeout', function(done) { + spec.onTimeout([{ + 'bidId': '25cd3ec3fd6ed7', + 'bidder': 'justpremium', + 'adUnitCode': 'div-gpt-ad-1471513102552-1', + 'auctionId': '6fbd0562-f613-4151-a6df-6cb446fc717b', + 'params': [{ + 'adType': 'iab', + 'zone': 21521 + }], + 'timeout': 1 + }, { + 'bidId': '3b51df1f254e32', + 'bidder': 'justpremium', + 'adUnitCode': 'div-gpt-ad-1471513102552-3', + 'auctionId': '6fbd0562-f613-4151-a6df-6cb446fc717b', + 'params': [{ + 'adType': 'iab', + 'zone': 21521 + }], + 'timeout': 1 + }]); + + expect(pixelStub.calledOnce).to.equal(true); + + done() }) }) }) diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 0e896b4fc04..fd0e22b91b9 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -1,17 +1,18 @@ import {expect, assert} from 'chai'; import {spec} from 'modules/kargoBidAdapter'; -import {registerBidder} from 'src/adapters/bidderFactory'; import {config} from 'src/config'; describe('kargo adapter tests', function () { - var sandbox; + var sandbox, clock, frozenNow = new Date(); - beforeEach(() => { + beforeEach(function () { sandbox = sinon.sandbox.create(); + clock = sinon.useFakeTimers(frozenNow.getTime()); }); - afterEach(() => { + afterEach(function () { sandbox.restore(); + clock.restore(); }); describe('bid request validity', function() { @@ -33,12 +34,20 @@ describe('kargo adapter tests', function () { }); describe('build request', function() { - var bids, cookies = [], localStorageItems = []; + var bids, undefinedCurrency, noAdServerCurrency, cookies = [], localStorageItems = [], sessionIds = [], requestCount = 0; - beforeEach(() => { + beforeEach(function () { + undefinedCurrency = false; + noAdServerCurrency = false; sandbox.stub(config, 'getConfig').callsFake(function(key) { if (key === 'currency') { - return 'USD'; + if (undefinedCurrency) { + return undefined; + } + if (noAdServerCurrency) { + return {}; + } + return {adServerCurrency: 'USD'}; } throw new Error(`Config stub incomplete! Missing key "${key}"`) }); @@ -48,18 +57,30 @@ describe('kargo adapter tests', function () { params: { placementId: 'foo' }, - placementCode: 1 + bidId: 1, + userId: { + tdid: 'fake-tdid' + }, + sizes: [[320, 50], [300, 250], [300, 600]] + }, + { + params: { + placementId: 'bar' + }, + bidId: 2, + sizes: [[320, 50], [300, 250], [300, 600]] }, { params: { placementId: 'bar' }, - placementCode: 2 + bidId: 3, + sizes: [[320, 50], [300, 250], [300, 600]] } ]; }); - afterEach(() => { + afterEach(function () { for (let key in cookies) { let cookie = cookies[key]; removeCookie(cookie); @@ -101,6 +122,16 @@ describe('kargo adapter tests', function () { return sandbox.stub(localStorage, 'getItem').throws(); } + function simulateNoCurrencyObject() { + undefinedCurrency = true; + noAdServerCurrency = false; + } + + function simulateNoAdServerCurrency() { + undefinedCurrency = false; + noAdServerCurrency = true; + } + function initializeKruxUser() { setLocalStorageItem('kxkar_user', 'rsgr9pnij'); } @@ -109,20 +140,19 @@ describe('kargo adapter tests', function () { setLocalStorageItem('kxkar_segs', 'qv9v984dy,rpx2gy365,qrd5u4axv,rnub9nmtd,reha00jnu'); } - function initializeKrgUid() { - setCookie('krg_uid', '%7B%22v%22%3A%7B%22userId%22%3A%225f108831-302d-11e7-bf6b-4595acd3bf6c%22%2C%22clientId%22%3A%222410d8f2-c111-4811-88a5-7b5e190e475f%22%2C%22optOut%22%3Afalse%7D%7D'); - } - function getKrgCrb() { - return '%7B%22v%22%3A%22eyJzeW5jSWRzIjp7IjIiOiI4MmZhMjU1NS01OTY5LTQ2MTQtYjRjZS00ZGNmMTA4MGU5ZjkiLCIxNiI6IlZveElrOEFvSnowQUFFZENleUFBQUFDMiY1MDIiLCIyMyI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjI0IjoiVm94SWs4QW9KejBBQUVkQ2V5QUFBQUMyJjUwMiIsIjI1IjoiNWVlMjQxMzgtNWUwMy00YjlkLWE5NTMtMzhlODMzZjI4NDlmIiwiMl84MCI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjJfOTMiOiI1ZWUyNDEzOC01ZTAzLTRiOWQtYTk1My0zOGU4MzNmMjg0OWYifSwiZXhwaXJlVGltZSI6MTQ5NzQ0OTM4MjY2OCwibGFzdFN5bmNlZEF0IjoxNDk3MzYyOTc5MDEyfQ%3D%3D%22%7D'; + return 'eyJzeW5jSWRzIjp7IjIiOiI4MmZhMjU1NS01OTY5LTQ2MTQtYjRjZS00ZGNmMTA4MGU5ZjkiLCIxNiI6IlZveElrOEFvSnowQUFFZENleUFBQUFDMiY1MDIiLCIyMyI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjI0IjoiVm94SWs4QW9KejBBQUVkQ2V5QUFBQUMyJjUwMiIsIjI1IjoiNWVlMjQxMzgtNWUwMy00YjlkLWE5NTMtMzhlODMzZjI4NDlmIiwiMl84MCI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjJfOTMiOiI1ZWUyNDEzOC01ZTAzLTRiOWQtYTk1My0zOGU4MzNmMjg0OWYifSwidXNlcklkIjoiNWYxMDg4MzEtMzAyZC0xMWU3LWJmNmItNDU5NWFjZDNiZjZjIiwiY2xpZW50SWQiOiIyNDEwZDhmMi1jMTExLTQ4MTEtODhhNS03YjVlMTkwZTQ3NWYiLCJvcHRPdXQiOmZhbHNlLCJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9'; } - function initializeKrgCrb() { - setCookie('krg_crb', getKrgCrb()); + function getKrgCrbOldStyle() { + return '%7B%22v%22%3A%22eyJzeW5jSWRzIjp7IjIiOiI4MmZhMjU1NS01OTY5LTQ2MTQtYjRjZS00ZGNmMTA4MGU5ZjkiLCIxNiI6IlZveElrOEFvSnowQUFFZENleUFBQUFDMiY1MDIiLCIyMyI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjI0IjoiVm94SWs4QW9KejBBQUVkQ2V5QUFBQUMyJjUwMiIsIjI1IjoiNWVlMjQxMzgtNWUwMy00YjlkLWE5NTMtMzhlODMzZjI4NDlmIiwiMl84MCI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjJfOTMiOiI1ZWUyNDEzOC01ZTAzLTRiOWQtYTk1My0zOGU4MzNmMjg0OWYifSwidXNlcklkIjoiNWYxMDg4MzEtMzAyZC0xMWU3LWJmNmItNDU5NWFjZDNiZjZjIiwiY2xpZW50SWQiOiIyNDEwZDhmMi1jMTExLTQ4MTEtODhhNS03YjVlMTkwZTQ3NWYiLCJvcHRPdXQiOmZhbHNlLCJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9%22%7D'; } - function initializeInvalidKrgUid() { - setCookie('krg_uid', 'invalid-krg-uid'); + function initializeKrgCrb(cookieOnly) { + if (!cookieOnly) { + setLocalStorageItem('krg_crb', getKrgCrb()); + } + setCookie('krg_crb', getKrgCrbOldStyle()); } function getInvalidKrgCrbType1() { @@ -130,53 +160,90 @@ describe('kargo adapter tests', function () { } function initializeInvalidKrgCrbType1() { + setLocalStorageItem('krg_crb', getInvalidKrgCrbType1()); + } + + function initializeInvalidKrgCrbType1Cookie() { setCookie('krg_crb', getInvalidKrgCrbType1()); } function getInvalidKrgCrbType2() { + return 'Ly8v'; + } + + function getInvalidKrgCrbType2OldStyle() { return '%7B%22v%22%3A%22%26%26%26%26%26%26%22%7D'; } function initializeInvalidKrgCrbType2() { - setCookie('krg_crb', getInvalidKrgCrbType2()); + setLocalStorageItem('krg_crb', getInvalidKrgCrbType2()); } - function getInvalidKrgCrbType3() { + function initializeInvalidKrgCrbType2Cookie() { + setCookie('krg_crb', getInvalidKrgCrbType2OldStyle()); + } + + function getInvalidKrgCrbType3OldStyle() { return '%7B%22v%22%3A%22Ly8v%22%7D'; } - function initializeInvalidKrgCrbType3() { - setCookie('krg_crb', getInvalidKrgCrbType3()); + function initializeInvalidKrgCrbType3Cookie() { + setCookie('krg_crb', getInvalidKrgCrbType3OldStyle()); + } + + function getInvalidKrgCrbType4OldStyle() { + return '%7B%22v%22%3A%22bnVsbA%3D%3D%22%7D'; } - function initializeEmptyKrgUid() { - setCookie('krg_uid', '%7B%7D'); + function initializeInvalidKrgCrbType4Cookie() { + setCookie('krg_crb', getInvalidKrgCrbType4OldStyle()); } function getEmptyKrgCrb() { + return 'eyJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9'; + } + + function getEmptyKrgCrbOldStyle() { return '%7B%22v%22%3A%22eyJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9%22%7D'; } function initializeEmptyKrgCrb() { - setCookie('krg_crb', getEmptyKrgCrb()); + setLocalStorageItem('krg_crb', getEmptyKrgCrb()); + } + + function initializeEmptyKrgCrbCookie() { + setCookie('krg_crb', getEmptyKrgCrbOldStyle()); } - function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB) { + function getSessionId() { + return spec._getSessionId(); + } + + function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB, expectedRawCRBCookie) { var base = { timeout: 200, + requestCount: requestCount++, currency: 'USD', cpmGranularity: 1, + timestamp: frozenNow.getTime(), cpmRange: { floor: 0, ceil: 20 }, - adSlotIds: [ - 'foo', - 'bar' - ], + bidIDs: { + 1: 'foo', + 2: 'bar', + 3: 'bar' + }, + bidSizes: { + 1: [[320, 50], [300, 250], [300, 600]], + 2: [[320, 50], [300, 250], [300, 600]], + 3: [[320, 50], [300, 250], [300, 600]] + }, userIDs: { kargoID: '5f108831-302d-11e7-bf6b-4595acd3bf6c', clientID: '2410d8f2-c111-4811-88a5-7b5e190e475f', + tdID: 'fake-tdid', crbIDs: { 2: '82fa2555-5969-4614-b4ce-4dcf1080e9f9', 16: 'VoxIk8AoJz0AAEdCeyAAAAC2&502', @@ -199,23 +266,41 @@ describe('kargo adapter tests', function () { ] }, pageURL: window.location.href, - rawCRB: expectedRawCRB + prebidRawBidRequests: [ + { + bidId: 1, + params: { + placementId: 'foo' + }, + userId: { + tdid: 'fake-tdid' + }, + sizes: [[320, 50], [300, 250], [300, 600]] + }, + { + bidId: 2, + params: { + placementId: 'bar' + }, + sizes: [[320, 50], [300, 250], [300, 600]] + }, + { + bidId: 3, + params: { + placementId: 'bar' + }, + sizes: [[320, 50], [300, 250], [300, 600]] + } + ], + rawCRB: expectedRawCRBCookie, + rawCRBLocalStorage: expectedRawCRB }; if (excludeUserIds === true) { base.userIDs = { crbIDs: {} }; - } else if (excludeUserIds) { - if (excludeUserIds.uid) { - delete base.userIDs.kargoID; - delete base.userIDs.clientID; - delete base.userIDs.optOut; - } - - if (excludeUserIds.crb) { - base.userIDs.crbIDs = {}; - } + delete base.prebidRawBidRequests[0].userId.tdid; } if (excludeKrux) { @@ -228,88 +313,142 @@ describe('kargo adapter tests', function () { return base; } - function testBuildRequests(expected) { - var request = spec.buildRequests(bids, {timeout: 200, foo: 'bar'}); + function testBuildRequests(excludeTdid, expected) { + var clonedBids = JSON.parse(JSON.stringify(bids)); + if (excludeTdid) { + delete clonedBids[0].userId.tdid; + } + var request = spec.buildRequests(clonedBids, {timeout: 200, foo: 'bar'}); + expected.sessionId = getSessionId(); + sessionIds.push(expected.sessionId); var krakenParams = JSON.parse(decodeURIComponent(request.data.slice(5))); expect(request.data.slice(0, 5)).to.equal('json='); - expect(request.url).to.equal('https://krk.kargo.com/api/v1/bid'); + expect(request.url).to.equal('https://krk.kargo.com/api/v2/bid'); expect(request.method).to.equal('GET'); expect(request.currency).to.equal('USD'); expect(request.timeout).to.equal(200); expect(request.foo).to.equal('bar'); expect(krakenParams).to.deep.equal(expected); + // Make sure session ID stays the same across requests simulating multiple auctions on one page load + for (let i in sessionIds) { + if (i == 0) { + continue; + } + let sessionId = sessionIds[i]; + expect(sessionIds[0]).to.equal(sessionId); + } } - it('works when all params and cookies are correctly set', function() { + it('works when all params and localstorage and cookies are correctly set', function() { initializeKruxUser(); initializeKruxSegments(); - initializeKrgUid(); initializeKrgCrb(); - testBuildRequests(getExpectedKrakenParams(undefined, undefined, getKrgCrb())); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle())); + }); + + it('works when all params and cookies are correctly set but no localstorage', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgCrb(true); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, null, getKrgCrbOldStyle())); }); it('gracefully handles nothing being set', function() { - testBuildRequests(getExpectedKrakenParams(true, true, null)); + testBuildRequests(true, getExpectedKrakenParams(true, true, null, null)); }); it('gracefully handles browsers without localStorage', function() { simulateNoLocalStorage(); - initializeKrgUid(); - initializeKrgCrb(); - testBuildRequests(getExpectedKrakenParams(false, true, getKrgCrb())); + testBuildRequests(true, getExpectedKrakenParams(true, true, null, null)); }); - it('handles empty yet valid Kargo CRBs and UIDs', function() { + it('handles empty yet valid Kargo CRB', function() { initializeKruxUser(); initializeKruxSegments(); - initializeEmptyKrgUid(); initializeEmptyKrgCrb(); - testBuildRequests(getExpectedKrakenParams(true, undefined, getEmptyKrgCrb())); + initializeEmptyKrgCrbCookie(); + testBuildRequests(true, getExpectedKrakenParams(true, undefined, getEmptyKrgCrb(), getEmptyKrgCrbOldStyle())); }); - it('handles broken Kargo UIDs', function() { + it('handles broken Kargo CRBs where base64 encoding is invalid', function() { initializeKruxUser(); initializeKruxSegments(); - initializeInvalidKrgUid(); - initializeKrgCrb(); - testBuildRequests(getExpectedKrakenParams({uid: true}, undefined, getKrgCrb())); + initializeInvalidKrgCrbType1(); + testBuildRequests(true, getExpectedKrakenParams(true, undefined, getInvalidKrgCrbType1(), null)); }); - it('handles broken Kargo CRBs where top level JSON is invalid', function() { + it('handles broken Kargo CRBs where top level JSON is invalid on cookie', function() { initializeKruxUser(); initializeKruxSegments(); - initializeKrgUid(); - initializeInvalidKrgCrbType1(); - testBuildRequests(getExpectedKrakenParams({crb: true}, undefined, getInvalidKrgCrbType1())); + initializeInvalidKrgCrbType1Cookie(); + testBuildRequests(true, getExpectedKrakenParams(true, undefined, null, getInvalidKrgCrbType1())); }); - it('handles broken Kargo CRBs where inner base 64 is invalid', function() { + it('handles broken Kargo CRBs where decoded JSON is invalid', function() { initializeKruxUser(); initializeKruxSegments(); - initializeKrgUid(); initializeInvalidKrgCrbType2(); - testBuildRequests(getExpectedKrakenParams({crb: true}, undefined, getInvalidKrgCrbType2())); + testBuildRequests(true, getExpectedKrakenParams(true, undefined, getInvalidKrgCrbType2(), null)); }); - it('handles broken Kargo CRBs where inner JSON is invalid', function() { + it('handles broken Kargo CRBs where inner base 64 is invalid on cookie', function() { initializeKruxUser(); initializeKruxSegments(); - initializeKrgUid(); - initializeInvalidKrgCrbType3(); - testBuildRequests(getExpectedKrakenParams({crb: true}, undefined, getInvalidKrgCrbType3())); + initializeInvalidKrgCrbType2Cookie(); + testBuildRequests(true, getExpectedKrakenParams(true, undefined, null, getInvalidKrgCrbType2OldStyle())); + }); + + it('handles broken Kargo CRBs where inner JSON is invalid on cookie', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeInvalidKrgCrbType3Cookie(); + testBuildRequests(true, getExpectedKrakenParams(true, undefined, null, getInvalidKrgCrbType3OldStyle())); + }); + + it('handles broken Kargo CRBs where inner JSON is falsey', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeInvalidKrgCrbType4Cookie(); + testBuildRequests(true, getExpectedKrakenParams(true, undefined, null, getInvalidKrgCrbType4OldStyle())); + }); + + it('handles a non-existant currency object on the config', function() { + simulateNoCurrencyObject(); + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgCrb(); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle())); + }); + + it('handles no ad server currency being set on the currency object in the config', function() { + simulateNoAdServerCurrency(); + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgCrb(); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle())); }); }); describe('response handler', function() { it('handles bid responses', function() { var resp = spec.interpretResponse({body: { - foo: { + 1: { + id: 'foo', cpm: 3, adm: '
    ', width: 320, height: 50 }, - bar: { + 2: { + id: 'bar', + cpm: 2.5, + adm: '
    ', + width: 300, + height: 250, + targetingCustom: 'dmpmptest1234' + }, + 3: { + id: 'bar', cpm: 2.5, adm: '
    ', width: 300, @@ -318,39 +457,150 @@ describe('kargo adapter tests', function () { }}, { currency: 'USD', bids: [{ - bidId: 'fake bid id 1', + bidId: 1, params: { placementId: 'foo' } }, { - bidId: 'fake bid id 2', + bidId: 2, + params: { + placementId: 'bar' + } + }, { + bidId: 3, params: { placementId: 'bar' } }] }); var expectation = [{ - requestId: 'fake bid id 1', + requestId: '1', cpm: 3, width: 320, height: 50, ad: '
    ', ttl: 300, creativeId: 'foo', + dealId: undefined, netRevenue: true, currency: 'USD' }, { - requestId: 'fake bid id 2', + requestId: '2', cpm: 2.5, width: 300, height: 250, ad: '
    ', ttl: 300, creativeId: 'bar', + dealId: 'dmpmptest1234', + netRevenue: true, + currency: 'USD' + }, { + requestId: '3', + cpm: 2.5, + width: 300, + height: 250, + ad: '
    ', + ttl: 300, + creativeId: 'bar', + dealId: undefined, netRevenue: true, currency: 'USD' }]; expect(resp).to.deep.equal(expectation); }); }); + + describe('user sync handler', function() { + const clientId = '74c81cbb-7d07-46d9-be9b-68ccb291c949'; + var shouldSimulateOutdatedBrowser, crb, isActuallyOutdatedBrowser; + + beforeEach(() => { + crb = {}; + shouldSimulateOutdatedBrowser = false; + isActuallyOutdatedBrowser = false; + + // IE11 fails these tests in the Prebid test suite. Since this + // browser won't support any of this stuff we expect all user + // syncing to fail gracefully. Kargo is mobile only, so this + // doesn't really matter. + if (!window.crypto) { + isActuallyOutdatedBrowser = true; + } else { + sandbox.stub(crypto, 'getRandomValues').callsFake(function(buf) { + if (shouldSimulateOutdatedBrowser) { + throw new Error('Could not generate random values'); + } + var bytes = [50, 5, 232, 133, 141, 55, 49, 57, 244, 126, 248, 44, 255, 38, 128, 0]; + for (var i = 0; i < bytes.length; i++) { + buf[i] = bytes[i]; + } + return buf; + }); + } + + sandbox.stub(spec, '_getCrb').callsFake(function() { + return crb; + }); + }); + + function getUserSyncsWhenAllowed() { + return spec.getUserSyncs({iframeEnabled: true}); + } + + function getUserSyncsWhenForbidden() { + return spec.getUserSyncs({}); + } + + function turnOnClientId() { + crb.clientId = clientId; + } + + function simulateOutdatedBrowser() { + shouldSimulateOutdatedBrowser = true; + } + + function getSyncUrl(index) { + return { + type: 'iframe', + url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}` + }; + } + + function getSyncUrls() { + var syncs = []; + for (var i = 0; i < 5; i++) { + syncs[i] = getSyncUrl(i); + } + return syncs; + } + + function safelyRun(runExpectation) { + if (isActuallyOutdatedBrowser) { + expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty; + } else { + runExpectation(); + } + } + + it('handles user syncs when there is a client id', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed()).to.deep.equal(getSyncUrls())); + }); + + it('no user syncs when there is no client id', function() { + safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); + }); + + it('no user syncs when there is outdated browser', function() { + turnOnClientId(); + simulateOutdatedBrowser(); + safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); + }); + + it('no user syncs when no iframe syncing allowed', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenForbidden()).to.be.an('array').that.is.empty); + }); + }); }); diff --git a/test/spec/modules/komoonaBidAdapter_spec.js b/test/spec/modules/komoonaBidAdapter_spec.js index f7038505db3..b6d17c7b20c 100644 --- a/test/spec/modules/komoonaBidAdapter_spec.js +++ b/test/spec/modules/komoonaBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/komoonaBidAdapter'; -describe('Komoona.com Adapter Tests', () => { +describe('Komoona.com Adapter Tests', function () { const bidsRequest = [ { bidder: 'komoona', @@ -54,11 +54,11 @@ describe('Komoona.com Adapter Tests', () => { } }; - it('Verifies komoonaAdapter bidder code', () => { + it('Verifies komoonaAdapter bidder code', function () { expect(spec.code).to.equal('komoona'); }); - it('Verifies komoonaAdapter bid request validation', () => { + it('Verifies komoonaAdapter bid request validation', function () { expect(spec.isBidRequestValid(bidsRequest[0])).to.equal(true); expect(spec.isBidRequestValid(bidsRequest[1])).to.equal(true); expect(spec.isBidRequestValid({})).to.equal(false); @@ -69,7 +69,7 @@ describe('Komoona.com Adapter Tests', () => { expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890, floorPrice: 0.8 } })).to.equal(true); }); - it('Verify komoonaAdapter build request', () => { + it('Verify komoonaAdapter build request', function () { var startTime = new Date().getTime(); const request = spec.buildRequests(bidsRequest); @@ -127,7 +127,7 @@ describe('Komoona.com Adapter Tests', () => { expect(kbConf.hb_placements[1]).to.equal(bids[1].placementid); }); - it('Verify komoonaAdapter build response', () => { + it('Verify komoonaAdapter build response', function () { const request = spec.buildRequests(bidsRequest); const bids = spec.interpretResponse(bidsResponse, request); @@ -150,7 +150,7 @@ describe('Komoona.com Adapter Tests', () => { expect(bid.creativeId).to.equal(responseBids[0].creativeId); }); - it('Verifies komoonaAdapter sync options', () => { + it('Verifies komoonaAdapter sync options', function () { // user sync disabled expect(spec.getUserSyncs({})).to.be.undefined; expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; diff --git a/test/spec/modules/kummaBidAdapter_spec.js b/test/spec/modules/kummaBidAdapter_spec.js index d90063820b7..7d33bd085b5 100644 --- a/test/spec/modules/kummaBidAdapter_spec.js +++ b/test/spec/modules/kummaBidAdapter_spec.js @@ -1,29 +1,83 @@ import {expect} from 'chai'; import {spec} from 'modules/kummaBidAdapter'; -import {getTopWindowLocation, getTopWindowReferrer} from 'src/utils'; +import {getTopWindowLocation} from 'src/utils'; +import {newBidder} from 'src/adapters/bidderFactory'; -describe('Kumma Adapter Tests', () => { +describe('Kumma Adapter Tests', function () { const slotConfigs = [{ placementCode: '/DfpAccount1/slot1', sizes: [[300, 250]], bidId: 'bid12345', + mediaType: 'banner', params: { - pubId: '55879', + pubId: '29521', siteId: '26047', placementId: '123', - bidFloor: '0.001' + bidFloor: '0.001', + ifa: 'IFA', + latitude: '40.712775', + longitude: '-74.005973' } }, { placementCode: '/DfpAccount2/slot2', - sizes: [[250, 250]], + sizes: [[728, 90]], bidId: 'bid23456', + mediaType: 'banner', params: { - pubId: '55879', + pubId: '29521', siteId: '26047', - placementId: '456' + placementId: '1234', + bidFloor: '0.000001', } }]; - it('Verify build request', () => { + const nativeSlotConfig = [{ + placementCode: '/DfpAccount1/slot3', + bidId: 'bid12345', + mediaType: 'native', + nativeParams: { + title: { required: true, len: 200 }, + body: {}, + image: { wmin: 100 }, + sponsoredBy: { }, + icon: { } + }, + params: { + pubId: '29521', + placementId: '123', + siteId: '26047' + } + }]; + const videoSlotConfig = [{ + placementCode: '/DfpAccount1/slot4', + sizes: [[640, 480]], + bidId: 'bid12345678', + mediaType: 'video', + video: { + skippable: true + }, + params: { + pubId: '29521', + placementId: '1234567', + siteId: '26047', + } + }]; + const appSlotConfig = [{ + placementCode: '/DfpAccount1/slot5', + bidId: 'bid12345', + params: { + pubId: '29521', + placementId: '1234', + app: { + id: '1111', + name: 'app name', + bundle: 'com.kumma.apps', + storeUrl: 'http://kumma.com/apps', + domain: 'kumma.com' + } + } + }]; + + it('Verify build request', function () { const request = spec.buildRequests(slotConfigs); expect(request.url).to.equal('//hb.kumma.com/'); expect(request.method).to.equal('POST'); @@ -31,13 +85,16 @@ describe('Kumma Adapter Tests', () => { // site object expect(ortbRequest.site).to.not.equal(null); expect(ortbRequest.site.publisher).to.not.equal(null); - expect(ortbRequest.site.publisher.id).to.equal('55879'); - expect(ortbRequest.site.ref).to.equal(getTopWindowReferrer()); + expect(ortbRequest.site.publisher.id).to.equal('29521'); + expect(ortbRequest.site.ref).to.equal(window.top.document.referrer); expect(ortbRequest.site.page).to.equal(getTopWindowLocation().href); expect(ortbRequest.imp).to.have.lengthOf(2); // device object expect(ortbRequest.device).to.not.equal(null); expect(ortbRequest.device.ua).to.equal(navigator.userAgent); + expect(ortbRequest.device.ifa).to.equal('IFA'); + expect(ortbRequest.device.geo.lat).to.equal('40.712775'); + expect(ortbRequest.device.geo.lon).to.equal('-74.005973'); // slot 1 expect(ortbRequest.imp[0].tagid).to.equal('123'); expect(ortbRequest.imp[0].banner).to.not.equal(null); @@ -45,14 +102,14 @@ describe('Kumma Adapter Tests', () => { expect(ortbRequest.imp[0].banner.h).to.equal(250); expect(ortbRequest.imp[0].bidfloor).to.equal('0.001'); // slot 2 - expect(ortbRequest.imp[1].tagid).to.equal('456'); + expect(ortbRequest.imp[1].tagid).to.equal('1234'); expect(ortbRequest.imp[1].banner).to.not.equal(null); - expect(ortbRequest.imp[1].banner.w).to.equal(250); - expect(ortbRequest.imp[1].banner.h).to.equal(250); + expect(ortbRequest.imp[1].banner.w).to.equal(728); + expect(ortbRequest.imp[1].banner.h).to.equal(90); expect(ortbRequest.imp[1].bidfloor).to.equal('0.000001'); }); - it('Verify parse response', () => { + it('Verify parse response', function () { const request = spec.buildRequests(slotConfigs); const ortbRequest = JSON.parse(request.data); const ortbResponse = { @@ -60,8 +117,7 @@ describe('Kumma Adapter Tests', () => { bid: [{ impid: ortbRequest.imp[0].id, price: 1.25, - adm: 'This is an Ad', - adid: '471810', + adm: 'This is an Ad' }] }], cur: 'USD' @@ -74,24 +130,206 @@ describe('Kumma Adapter Tests', () => { expect(bid.ad).to.equal('This is an Ad'); expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('471810'); + expect(bid.adId).to.equal('bid12345'); + expect(bid.creativeId).to.equal('bid12345'); + expect(bid.netRevenue).to.equal(true); expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(360); }); - it('Verify full passback', () => { + it('Verify full passback', function () { const request = spec.buildRequests(slotConfigs); const bids = spec.interpretResponse({ body: null }, request) expect(bids).to.have.lengthOf(0); }); - it('Verifies bidder code', () => { + it('Verify Native request', function () { + const request = spec.buildRequests(nativeSlotConfig); + expect(request.url).to.equal('//hb.kumma.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + // native impression + expect(ortbRequest.imp[0].tagid).to.equal('123'); + const nativePart = ortbRequest.imp[0]['native']; + expect(nativePart).to.not.equal(null); + expect(nativePart.ver).to.equal('1.1'); + expect(nativePart.request).to.not.equal(null); + // native request assets + const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); + expect(nativeRequest).to.not.equal(null); + expect(nativeRequest.assets).to.have.lengthOf(5); + expect(nativeRequest.assets[0].id).to.equal(1); + expect(nativeRequest.assets[1].id).to.equal(2); + expect(nativeRequest.assets[2].id).to.equal(3); + expect(nativeRequest.assets[3].id).to.equal(4); + expect(nativeRequest.assets[4].id).to.equal(5); + expect(nativeRequest.assets[0].required).to.equal(1); + expect(nativeRequest.assets[0].title).to.not.equal(null); + expect(nativeRequest.assets[0].title.len).to.equal(200); + expect(nativeRequest.assets[1].title).to.be.undefined; + expect(nativeRequest.assets[1].data).to.not.equal(null); + expect(nativeRequest.assets[1].data.type).to.equal(2); + expect(nativeRequest.assets[1].data.len).to.equal(200); + expect(nativeRequest.assets[2].required).to.equal(0); + expect(nativeRequest.assets[3].img).to.not.equal(null); + expect(nativeRequest.assets[3].img.wmin).to.equal(50); + expect(nativeRequest.assets[3].img.hmin).to.equal(50); + expect(nativeRequest.assets[3].img.type).to.equal(1); + expect(nativeRequest.assets[4].img).to.not.equal(null); + expect(nativeRequest.assets[4].img.wmin).to.equal(100); + expect(nativeRequest.assets[4].img.hmin).to.equal(150); + expect(nativeRequest.assets[4].img.type).to.equal(3); + }); + + it('Verify Native response', function () { + const request = spec.buildRequests(nativeSlotConfig); + expect(request.url).to.equal('//hb.kumma.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + const nativeResponse = { + 'native': { + assets: [ + { id: 1, title: { text: 'Ad Title' } }, + { id: 2, data: { value: 'Test description' } }, + { id: 3, data: { value: 'Brand' } }, + { id: 4, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_icon.png', w: 100, h: 100 } }, + { id: 5, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_image.png', w: 300, h: 300 } } + ], + link: { url: 'http://brand.com/' } + } + }; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + nurl: 'http://rtb.adx1.com/log', + adm: JSON.stringify(nativeResponse) + }] + }], + cur: 'USD', + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + // verify bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.adId).to.equal('bid12345'); + expect(bid.ad).to.be.undefined; + expect(bid.mediaType).to.equal('native'); + const nativeBid = bid['native']; + expect(nativeBid).to.not.equal(null); + expect(nativeBid.title).to.equal('Ad Title'); + expect(nativeBid.sponsoredBy).to.equal('Brand'); + expect(nativeBid.icon.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_icon.png'); + expect(nativeBid.image.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_image.png'); + expect(nativeBid.image.width).to.equal(300); + expect(nativeBid.image.height).to.equal(300); + expect(nativeBid.icon.width).to.equal(100); + expect(nativeBid.icon.height).to.equal(100); + expect(nativeBid.clickUrl).to.equal(encodeURIComponent('http://brand.com/')); + expect(nativeBid.impressionTrackers).to.have.lengthOf(1); + expect(nativeBid.impressionTrackers[0]).to.equal('http://rtb.adx1.com/log'); + }); + + it('Verify Video request', function () { + const request = spec.buildRequests(videoSlotConfig); + expect(request.url).to.equal('//hb.kumma.com/'); + expect(request.method).to.equal('POST'); + const videoRequest = JSON.parse(request.data); + // site object + expect(videoRequest.site).to.not.equal(null); + expect(videoRequest.site.publisher.id).to.equal('29521'); + expect(videoRequest.site.ref).to.equal(window.top.document.referrer); + expect(videoRequest.site.page).to.equal(getTopWindowLocation().href); + // device object + expect(videoRequest.device).to.not.equal(null); + expect(videoRequest.device.ua).to.equal(navigator.userAgent); + // slot 1 + expect(videoRequest.imp[0].tagid).to.equal('1234567'); + expect(videoRequest.imp[0].video).to.not.equal(null); + expect(videoRequest.imp[0].video.w).to.equal(640); + expect(videoRequest.imp[0].video.h).to.equal(480); + expect(videoRequest.imp[0].banner).to.equal(null); + expect(videoRequest.imp[0].native).to.equal(null); + }); + + it('Verify parse video response', function () { + const request = spec.buildRequests(videoSlotConfig); + const videoRequest = JSON.parse(request.data); + const videoResponse = { + seatbid: [{ + bid: [{ + impid: videoRequest.imp[0].id, + price: 1.90, + adm: 'http://vid.example.com/9876', + crid: '510511_754567308' + }] + }], + cur: 'USD' + }; + const bids = spec.interpretResponse({ body: videoResponse }, request); + expect(bids).to.have.lengthOf(1); + // verify first bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.90); + expect(bid.vastUrl).to.equal('http://vid.example.com/9876'); + expect(bid.crid).to.equal('510511_754567308'); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(480); + expect(bid.adId).to.equal('bid12345678'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(360); + }); + + it('Verifies bidder code', function () { expect(spec.code).to.equal('kumma'); }); - it('Verifies if bid request valid', () => { + it('Verifies supported media types', function () { + expect(spec.supportedMediaTypes).to.have.lengthOf(3); + expect(spec.supportedMediaTypes[0]).to.equal('banner'); + expect(spec.supportedMediaTypes[1]).to.equal('native'); + expect(spec.supportedMediaTypes[2]).to.equal('video'); + }); + + it('Verifies if bid request valid', function () { expect(spec.isBidRequestValid(slotConfigs[0])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid(slotConfigs[1])).to.equal(true); + expect(spec.isBidRequestValid(nativeSlotConfig[0])).to.equal(true); + expect(spec.isBidRequestValid(videoSlotConfig[0])).to.equal(true); + }); + + it('Verify app requests', function () { + const request = spec.buildRequests(appSlotConfig); + const ortbRequest = JSON.parse(request.data); + expect(ortbRequest.site).to.equal(null); + expect(ortbRequest.app).to.not.be.null; + expect(ortbRequest.app.publisher).to.not.equal(null); + expect(ortbRequest.app.publisher.id).to.equal('29521'); + expect(ortbRequest.app.id).to.equal('1111'); + expect(ortbRequest.app.name).to.equal('app name'); + expect(ortbRequest.app.bundle).to.equal('com.kumma.apps'); + expect(ortbRequest.app.storeurl).to.equal('http://kumma.com/apps'); + expect(ortbRequest.app.domain).to.equal('kumma.com'); + }); + + it('Verify GDPR', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'serialized_gpdr_data' + } + }; + const request = spec.buildRequests(slotConfigs, bidderRequest); + expect(request.url).to.equal('//hb.kumma.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + expect(ortbRequest.user).to.not.equal(null); + expect(ortbRequest.user.ext).to.not.equal(null); + expect(ortbRequest.user.ext.consent).to.equal('serialized_gpdr_data'); + expect(ortbRequest.regs).to.not.equal(null); + expect(ortbRequest.regs.ext).to.not.equal(null); + expect(ortbRequest.regs.ext.gdpr).to.equal(1); }); }); diff --git a/test/spec/modules/lemmaBidAdapter_spec.js b/test/spec/modules/lemmaBidAdapter_spec.js new file mode 100644 index 00000000000..624e763ebe1 --- /dev/null +++ b/test/spec/modules/lemmaBidAdapter_spec.js @@ -0,0 +1,335 @@ +import { expect } from 'chai'; +import { spec } from 'modules/lemmaBidAdapter'; +import * as utils from 'src/utils'; +const constants = require('src/constants.json'); + +describe('lemmaBidAdapter', function() { + var bidRequests; + var videoBidRequests; + var bidResponses; + beforeEach(function() { + bidRequests = [{ + bidder: 'lemma', + mediaType: 'banner', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600] + ], + } + }, + params: { + pubId: 1001, + adunitId: 1, + currency: 'AUD', + geo: { + lat: '12.3', + lon: '23.7', + } + }, + sizes: [ + [300, 250], + [300, 600] + ] + }]; + videoBidRequests = [{ + code: 'video1', + mediaType: 'video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bidder: 'lemma', + params: { + pubId: 1001, + adunitId: 1, + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30 + } + } + }]; + bidResponses = { + 'body': { + 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', + 'seatbid': [{ + 'bid': [{ + 'id': '74858439-49D7-4169-BA5D-44A046315B2F', + 'impid': '22bddb28db77d', + 'price': 1.3, + 'adm': '

    lemma"Connecting Advertisers and Publishers directly"

    ', + 'adomain': ['amazon.com'], + 'iurl': 'https://thetradedesk-t-general.s3.amazonaws.com/AdvertiserLogos/vgl908z.png', + 'cid': '22918', + 'crid': 'v55jutrh', + 'h': 250, + 'w': 300, + 'ext': {} + }] + }] + } + }; + }); + describe('implementation', function() { + describe('Bid validations', function() { + it('valid bid case', function() { + var validBid = { + bidder: 'lemma', + params: { + pubId: 1001, + adunitId: 1 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + it('invalid bid case', function() { + var isValid = spec.isBidRequestValid(); + expect(isValid).to.equal(false); + }); + it('invalid bid case: pubId not passed', function() { + var validBid = { + bidder: 'lemma', + params: { + adunitId: 1 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + it('invalid bid case: pubId is not number', function() { + var validBid = { + bidder: 'lemma', + params: { + pubId: '301', + adunitId: 1 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + it('invalid bid case: adunitId is not passed', function() { + var validBid = { + bidder: 'lemma', + params: { + pubId: 1001 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + it('invalid bid case: video bid request mimes is not passed', function() { + var validBid = { + bidder: 'lemma', + params: { + pubId: 1001, + adunitId: 1, + video: { + skippable: true, + minduration: 5, + maxduration: 30 + } + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + validBid.params.video.mimes = []; + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + }); + describe('Request formation', function() { + it('buildRequests function should not modify original bidRequests object', function() { + var originalBidRequests = utils.deepClone(bidRequests); + var request = spec.buildRequests(bidRequests); + expect(bidRequests).to.deep.equal(originalBidRequests); + }); + it('Endpoint checking', function() { + var request = spec.buildRequests(bidRequests); + expect(request.url).to.equal('//ads.lemmatechnologies.com/lemma/servad?pid=1001&aid=1'); + expect(request.method).to.equal('POST'); + }); + it('Request params check', function() { + var request = spec.buildRequests(bidRequests); + var data = JSON.parse(request.data); + expect(data.site.domain).to.be.a('string'); // domain should be set + expect(data.site.publisher.id).to.equal(bidRequests[0].params.pubId.toString()); // publisher Id + expect(data.imp[0].tagid).to.equal('1'); // tagid + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + }); + it('Request params check without mediaTypes object', function() { + var bidRequests = [{ + bidder: 'lemma', + params: { + pubId: 1001, + adunitId: 1, + currency: 'AUD' + }, + sizes: [ + [300, 250], + [300, 600] + ] + }]; + var request = spec.buildRequests(bidRequests); + var data = JSON.parse(request.data); + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + expect(data.imp[0].banner.format).exist.and.to.be.an('array'); + expect(data.imp[0].banner.format[0]).exist.and.to.be.an('object'); + expect(data.imp[0].banner.format[0].w).to.equal(300); // width + expect(data.imp[0].banner.format[0].h).to.equal(600); // height + }); + it('Request params check: without tagId', function() { + delete bidRequests[0].params.adunitId; + var request = spec.buildRequests(bidRequests); + var data = JSON.parse(request.data); + expect(data.site.domain).to.be.a('string'); // domain should be set + expect(data.site.publisher.id).to.equal(bidRequests[0].params.pubId.toString()); // publisher Id + expect(data.imp[0].tagid).to.equal(undefined); // tagid + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + }); + it('Request params multi size format object check', function() { + var bidRequests = [{ + bidder: 'lemma', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600] + ], + } + }, + params: { + pubId: 1001, + adunitId: 1, + currency: 'AUD' + }, + sizes: [ + [300, 250], + [300, 600] + ] + }]; + /* case 1 - size passed in adslot */ + var request = spec.buildRequests(bidRequests); + var data = JSON.parse(request.data); + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + /* case 2 - size passed in adslot as well as in sizes array */ + bidRequests[0].sizes = [ + [300, 600], + [300, 250] + ]; + bidRequests[0].mediaTypes = { + banner: { + sizes: [ + [300, 600], + [300, 250] + ] + } + }; + request = spec.buildRequests(bidRequests); + data = JSON.parse(request.data); + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(600); // height + /* case 3 - size passed in sizes but not in adslot */ + bidRequests[0].params.adunitId = 1; + bidRequests[0].sizes = [ + [300, 250], + [300, 600] + ]; + bidRequests[0].mediaTypes = { + banner: { + sizes: [ + [300, 250], + [300, 600] + ] + } + }; + request = spec.buildRequests(bidRequests); + data = JSON.parse(request.data); + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + expect(data.imp[0].banner.format).exist.and.to.be.an('array'); + expect(data.imp[0].banner.format[0]).exist.and.to.be.an('object'); + expect(data.imp[0].banner.format[0].w).to.equal(300); // width + expect(data.imp[0].banner.format[0].h).to.equal(250); // height + }); + it('Request params currency check', function() { + var bidRequest = [{ + bidder: 'lemma', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600] + ], + } + }, + params: { + pubId: 1001, + adunitId: 1, + currency: 'AUD' + }, + sizes: [ + [300, 250], + [300, 600] + ] + }]; + /* case 1 - + currency specified in adunits + output: imp[0] use currency specified in bidRequests[0].params.currency + */ + var request = spec.buildRequests(bidRequest); + var data = JSON.parse(request.data); + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + /* case 2 - + currency specified in adunit + output: imp[0] use default currency - USD + */ + delete bidRequest[0].params.currency; + request = spec.buildRequests(bidRequest); + data = JSON.parse(request.data); + expect(data.imp[0].bidfloorcur).to.equal('USD'); + }); + it('Request params check for video ad', function() { + var request = spec.buildRequests(videoBidRequests); + var data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist; + expect(data.imp[0].tagid).to.equal('1'); + expect(data.imp[0]['video']['mimes']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['mimes'][0]).to.equal(videoBidRequests[0].params.video['mimes'][0]); + expect(data.imp[0]['video']['mimes'][1]).to.equal(videoBidRequests[0].params.video['mimes'][1]); + expect(data.imp[0]['video']['minduration']).to.equal(videoBidRequests[0].params.video['minduration']); + expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); + expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); + }); + describe('Response checking', function() { + it('should check for valid response values', function() { + var request = spec.buildRequests(bidRequests); + var data = JSON.parse(request.data); + var response = spec.interpretResponse(bidResponses, request); + expect(response).to.be.an('array').with.length.above(0); + expect(response[0].requestId).to.equal(bidResponses.body.seatbid[0].bid[0].impid); + expect(response[0].cpm).to.equal((bidResponses.body.seatbid[0].bid[0].price).toFixed(2)); + expect(response[0].width).to.equal(bidResponses.body.seatbid[0].bid[0].w); + expect(response[0].height).to.equal(bidResponses.body.seatbid[0].bid[0].h); + if (bidResponses.body.seatbid[0].bid[0].crid) { + expect(response[0].creativeId).to.equal(bidResponses.body.seatbid[0].bid[0].crid); + } else { + expect(response[0].creativeId).to.equal(bidResponses.body.seatbid[0].bid[0].id); + } + expect(response[0].dealId).to.equal(bidResponses.body.seatbid[0].bid[0].dealid); + expect(response[0].currency).to.equal('USD'); + expect(response[0].netRevenue).to.equal(false); + expect(response[0].ttl).to.equal(300); + }); + }); + }); + }); +}); diff --git a/test/spec/modules/lifestreetBidAdapter_spec.js b/test/spec/modules/lifestreetBidAdapter_spec.js index 2c48a0f1892..7f8c5f6c44d 100644 --- a/test/spec/modules/lifestreetBidAdapter_spec.js +++ b/test/spec/modules/lifestreetBidAdapter_spec.js @@ -56,45 +56,45 @@ let getDefaultBidResponse = (isBanner, noBid = 0) => { }; }; -describe('LifestreetAdapter', () => { +describe('LifestreetAdapter', function () { const LIFESTREET_URL = '//ads.lfstmedia.com/gate/'; const ADAPTER_VERSION = 'prebidJS-2.0'; - describe('buildRequests()', () => { - it('method exists and is a function', () => { + describe('buildRequests()', function () { + it('method exists and is a function', function () { expect(spec.buildRequests).to.exist.and.to.be.a('function'); }); - it('should not return request when no bids are present', () => { + it('should not return request when no bids are present', function () { let [request] = spec.buildRequests([]); - expect(request).to.be.empty; + expect(request).to.be.undefined; }); let bidRequest = getDefaultBidRequest(); let [request] = spec.buildRequests(bidRequest.bids); - it('should return request for Lifestreet endpoint', () => { + it('should return request for Lifestreet endpoint', function () { expect(request.url).to.contain(LIFESTREET_URL); }); - it('should use json adapter', () => { + it('should use json adapter', function () { expect(request.url).to.contain('/prebid/'); }); - it('should contain slot', () => { + it('should contain slot', function () { expect(request.url).to.contain('slot166704'); }); - it('should contain adkey', () => { + it('should contain adkey', function () { expect(request.url).to.contain('adkey=78c'); }); - it('should contain ad_size', () => { + it('should contain ad_size', function () { expect(request.url).to.contain('ad_size=160x600'); }); - it('should contain location and rferrer paramters', () => { + it('should contain location and rferrer paramters', function () { expect(request.url).to.contain('__location='); expect(request.url).to.contain('__referrer='); }); - it('should contain info parameters', () => { + it('should contain info parameters', function () { expect(request.url).to.contain('__wn='); expect(request.url).to.contain('__sf='); expect(request.url).to.contain('__fif='); @@ -103,24 +103,24 @@ describe('LifestreetAdapter', () => { expect(request.url).to.contain('__pp='); }); - it('should contain HB enabled', () => { + it('should contain HB enabled', function () { expect(request.url).to.contain('__hb=1'); }); - it('should include gzip', () => { + it('should include gzip', function () { expect(request.url).to.contain('__gz=1'); }); - it('should not contain __gdpr parameter', () => { + it('should not contain __gdpr parameter', function () { expect(request.url).to.not.contain('__gdpr'); }); - it('should not contain __concent parameter', () => { + it('should not contain __concent parameter', function () { expect(request.url).to.not.contain('__consent'); }); - it('should contain the right version of adapter', () => { + it('should contain the right version of adapter', function () { expect(request.url).to.contain('__hbver=' + ADAPTER_VERSION); }); - it('should contain __gdpr and __consent parameters', () => { + it('should contain __gdpr and __consent parameters', function () { const options = { gdprConsent: { gdprApplies: true, @@ -132,7 +132,7 @@ describe('LifestreetAdapter', () => { expect(request.url).to.contain('__gdpr=1'); expect(request.url).to.contain('__consent=test'); }); - it('should contain __gdpr parameters', () => { + it('should contain __gdpr parameters', function () { const options = { gdprConsent: { gdprApplies: true, @@ -143,7 +143,7 @@ describe('LifestreetAdapter', () => { expect(request.url).to.contain('__gdpr=1'); expect(request.url).to.not.contain('__consent'); }); - it('should contain __consent parameters', () => { + it('should contain __consent parameters', function () { const options = { gdprConsent: { consentString: 'test', @@ -155,8 +155,8 @@ describe('LifestreetAdapter', () => { expect(request.url).to.contain('__consent=test'); }); }); - describe('interpretResponse()', () => { - it('should return formatted bid response with required properties', () => { + describe('interpretResponse()', function () { + it('should return formatted bid response with required properties', function () { let bidRequest = getDefaultBidRequest().bids[0]; let bidResponse = { body: getDefaultBidResponse(true) }; let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); @@ -174,7 +174,7 @@ describe('LifestreetAdapter', () => { mediaType: 'banner' }]); }); - it('should return formatted VAST bid response with required properties', () => { + it('should return formatted VAST bid response with required properties', function () { let bidRequest = getDefaultBidRequest().bids[0]; let bidResponse = { body: getDefaultBidResponse(false) }; let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); @@ -192,7 +192,7 @@ describe('LifestreetAdapter', () => { mediaType: 'video' }]); }); - it('should return formatted VAST bid response with vastUrl', () => { + it('should return formatted VAST bid response with vastUrl', function () { let bidRequest = getDefaultBidRequest().bids[0]; let bidResponse = { body: getDefaultBidResponse(false) }; bidResponse.body.vastUrl = 'http://lifestreet.com'; // set vastUrl @@ -212,7 +212,7 @@ describe('LifestreetAdapter', () => { }]); }); - it('should return no-bid', () => { + it('should return no-bid', function () { let bidRequest = getDefaultBidRequest().bids[0]; let bidResponse = { body: getDefaultBidResponse(true, true) }; let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); diff --git a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..611ff95a036 --- /dev/null +++ b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js @@ -0,0 +1,324 @@ +import livewrappedAnalyticsAdapter, { BID_WON_TIMEOUT } from 'modules/livewrappedAnalyticsAdapter'; +import CONSTANTS from 'src/constants.json'; +import { config } from 'src/config'; + +let events = require('src/events'); +let utils = require('src/utils'); +let adapterManager = require('src/adapterManager').default; + +const { + EVENTS: { + AUCTION_INIT, + AUCTION_END, + BID_REQUESTED, + BID_RESPONSE, + BIDDER_DONE, + BID_WON, + BID_TIMEOUT, + SET_TARGETING + }, + STATUS: { + GOOD + } +} = CONSTANTS; + +const BID1 = { + width: 980, + height: 240, + cpm: 1.1, + timeToRespond: 200, + bidId: '2ecff0db240757', + requestId: '2ecff0db240757', + adId: '2ecff0db240757', + auctionId: '25c6d7f5-699a-4bfc-87c9-996f915341fa', + getStatusCode() { + return CONSTANTS.STATUS.GOOD; + } +}; + +const BID2 = Object.assign({}, BID1, { + width: 300, + height: 250, + cpm: 2.2, + timeToRespond: 300, + bidId: '3ecff0db240757', + requestId: '3ecff0db240757', + adId: '3ecff0db240757', +}); + +const BID3 = { + bidId: '4ecff0db240757', + requestId: '4ecff0db240757', + adId: '4ecff0db240757', + auctionId: '25c6d7f5-699a-4bfc-87c9-996f915341fa', + getStatusCode() { + return CONSTANTS.STATUS.NO_BID; + } +}; + +const MOCK = { + AUCTION_INIT: { + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + }, + BID_REQUESTED: { + 'bidder': 'livewrapped', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'livewrapped', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ecff0db240757', + }, + { + 'bidder': 'livewrapped', + 'adUnitCode': 'box_d_1', + 'bidId': '3ecff0db240757', + }, + { + 'bidder': 'livewrapped', + 'adUnitCode': 'box_d_2', + 'bidId': '4ecff0db240757', + } + ], + 'start': 1519149562216 + }, + BID_RESPONSE: [ + BID1, + BID2 + ], + AUCTION_END: { + }, + BID_WON: [ + Object.assign({}, BID1, { + 'status': 'rendered', + 'requestId': '2ecff0db240757' + }), + Object.assign({}, BID2, { + 'status': 'rendered', + 'requestId': '3ecff0db240757' + }) + ], + BIDDER_DONE: { + 'bidderCode': 'livewrapped', + 'bids': [ + BID1, + BID2, + BID3 + ] + }, + BID_TIMEOUT: [ + { + 'bidId': '2ecff0db240757', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + } + ] +}; + +const ANALYTICS_MESSAGE = { + publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7', + bidAdUnits: [ + { + adUnit: 'panorama_d_1', + timeStamp: 1519149562216 + }, + { + adUnit: 'box_d_1', + timeStamp: 1519149562216 + } + ], + requests: [ + { + adUnit: 'panorama_d_1', + bidder: 'livewrapped', + timeStamp: 1519149562216 + }, + { + adUnit: 'box_d_1', + bidder: 'livewrapped', + timeStamp: 1519149562216 + }, + { + adUnit: 'box_d_2', + bidder: 'livewrapped', + timeStamp: 1519149562216 + } + ], + responses: [ + { + timeStamp: 1519149562216, + adUnit: 'panorama_d_1', + bidder: 'livewrapped', + width: 980, + height: 240, + cpm: 1.1, + ttr: 200, + IsBid: true + }, + { + timeStamp: 1519149562216, + adUnit: 'box_d_1', + bidder: 'livewrapped', + width: 300, + height: 250, + cpm: 2.2, + ttr: 300, + IsBid: true + }, + { + timeStamp: 1519149562216, + adUnit: 'box_d_2', + bidder: 'livewrapped', + ttr: 200, + IsBid: false + } + ], + timeouts: [], + wins: [ + { + timeStamp: 1519149562216, + adUnit: 'panorama_d_1', + bidder: 'livewrapped', + width: 980, + height: 240, + cpm: 1.1 + }, + { + timeStamp: 1519149562216, + adUnit: 'box_d_1', + bidder: 'livewrapped', + width: 300, + height: 250, + cpm: 2.2 + } + ] +}; + +function performStandardAuction() { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); +} + +describe('Livewrapped analytics adapter', function () { + let sandbox; + let xhr; + let requests; + let clock; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + + xhr = sandbox.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + + sandbox.stub(events, 'getEvents').returns([]); + sandbox.stub(utils, 'timestamp').returns(1519149562416); + + clock = sandbox.useFakeTimers(1519767013781); + }); + + afterEach(function () { + sandbox.restore(); + config.resetConfig(); + }); + + describe('when handling events', function () { + adapterManager.registerAnalyticsAdapter({ + code: 'livewrapped', + adapter: livewrappedAnalyticsAdapter + }); + + beforeEach(function () { + adapterManager.enableAnalytics({ + provider: 'livewrapped', + options: { + publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7' + } + }); + }); + + afterEach(function () { + livewrappedAnalyticsAdapter.disableAnalytics(); + }); + + it('should build a batched message from prebid events', function () { + sandbox.stub(utils, 'getWindowTop').returns({}); + performStandardAuction(); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(requests.length).to.equal(1); + let request = requests[0]; + + expect(request.url).to.equal('//lwadm.com/analytics/10'); + + let message = JSON.parse(request.requestBody); + + expect(message).to.deep.equal(ANALYTICS_MESSAGE); + }); + + it('should send batched message without BID_WON if necessary and further BID_WON events individually', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + + clock.tick(BID_WON_TIMEOUT + 1000); + + events.emit(BID_WON, MOCK.BID_WON[1]); + + expect(requests.length).to.equal(2); + + let message = JSON.parse(requests[0].requestBody); + expect(message.wins.length).to.equal(1); + expect(message.requests).to.deep.equal(ANALYTICS_MESSAGE.requests); + expect(message.wins[0]).to.deep.equal(ANALYTICS_MESSAGE.wins[0]); + + message = JSON.parse(requests[1].requestBody); + expect(message.wins.length).to.equal(1); + expect(message.wins[0]).to.deep.equal(ANALYTICS_MESSAGE.wins[1]); + }); + + it('should properly mark bids as timed out', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(requests.length).to.equal(1); + + let message = JSON.parse(requests[0].requestBody); + expect(message.timeouts.length).to.equal(1); + expect(message.timeouts[0].bidder).to.equal('livewrapped'); + expect(message.timeouts[0].adUnit).to.equal('panorama_d_1'); + }); + + it('should detect adblocker recovered request', function () { + sandbox.stub(utils, 'getWindowTop').returns({ I12C: { Morph: 1 } }); + performStandardAuction(); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(requests.length).to.equal(1); + let request = requests[0]; + + let message = JSON.parse(request.requestBody); + + expect(message.rcv).to.equal(true); + }); + }); +}); diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js new file mode 100644 index 00000000000..855eb2ee3f9 --- /dev/null +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -0,0 +1,752 @@ +import {expect} from 'chai'; +import {spec} from 'modules/livewrappedBidAdapter'; +import {config} from 'src/config'; +import * as utils from 'src/utils'; + +describe('Livewrapped adapter tests', function () { + let sandbox, + bidderRequest; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + + bidderRequest = { + bidderCode: 'livewrapped', + auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + bidderRequestId: '178e34bad3658f', + bids: [ + { + bidder: 'livewrapped', + params: { + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']} + }, + adUnitCode: 'panorama_d_1', + sizes: [[980, 240], [980, 120]], + bidId: '2ffb201a808da7', + bidderRequestId: '178e34bad3658f', + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + } + ], + start: 1472239426002, + auctionStart: 1472239426000, + timeout: 5000 + }; + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('isBidRequestValid', function() { + it('should accept a request with id only as valid', function() { + let bid = {params: {adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37'}}; + + let result = spec.isBidRequestValid(bid); + + expect(result).to.be.true; + }); + + it('should accept a request with adUnitName and PublisherId as valid', function() { + let bid = {params: {adUnitName: 'panorama_d_1', publisherId: '26947112-2289-405D-88C1-A7340C57E63E'}}; + + let result = spec.isBidRequestValid(bid); + + expect(result).to.be.true; + }); + + it('should accept a request with adUnitCode and PublisherId as valid', function() { + let bid = {adUnitCode: 'panorama_d_1', params: {publisherId: '26947112-2289-405D-88C1-A7340C57E63E'}}; + + let result = spec.isBidRequestValid(bid); + + expect(result).to.be.true; + }); + + it('should accept a request with placementCode and PublisherId as valid', function() { + let bid = {placementCode: 'panorama_d_1', params: {publisherId: '26947112-2289-405D-88C1-A7340C57E63E'}}; + + let result = spec.isBidRequestValid(bid); + + expect(result).to.be.true; + }); + + it('should not accept a request with adUnitName, adUnitCode, placementCode but no PublisherId as valid', function() { + let bid = {placementCode: 'panorama_d_1', adUnitCode: 'panorama_d_1', params: {adUnitName: 'panorama_d_1'}}; + + let result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + }); + + describe('buildRequests', function() { + it('should make a well-formed single request object', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let result = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: true, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed multiple request object', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let multiplebidRequest = clone(bidderRequest); + multiplebidRequest.bids.push(clone(bidderRequest.bids[0])); + multiplebidRequest.bids[1].adUnitCode = 'box_d_1'; + multiplebidRequest.bids[1].sizes = [[300, 250]]; + multiplebidRequest.bids[1].bidId = '3ffb201a808da7'; + delete multiplebidRequest.bids[1].params.adUnitId; + + let result = spec.buildRequests(multiplebidRequest.bids, multiplebidRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: true, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }, { + callerAdUnitId: 'box_d_1', + bidId: '3ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 300, height: 250}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with AdUnitName', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + testbidRequest.bids[0].params.adUnitName = 'caller id 1'; + delete testbidRequest.bids[0].params.adUnitId; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: true, + adRequests: [{ + callerAdUnitId: 'caller id 1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with less parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'http://www.domain.com', + version: '1.1', + cookieSupport: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with less parameters, no publisherId', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.publisherId; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + url: 'http://www.domain.com', + version: '1.1', + cookieSupport: true, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with app parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + testbidRequest.bids[0].params.deviceId = 'deviceid'; + testbidRequest.bids[0].params.ifa = 'ifa'; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'http://www.domain.com', + version: '1.1', + deviceId: 'deviceid', + ifa: 'ifa', + cookieSupport: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with debug parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + testbidRequest.bids[0].params.tid = 'tracking id'; + testbidRequest.bids[0].params.test = true; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'http://www.domain.com', + version: '1.1', + tid: 'tracking id', + test: true, + cookieSupport: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with optional parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + testbidRequest.bids[0].params.options = {keyvalues: [{key: 'key', value: 'value'}]}; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'http://www.domain.com', + version: '1.1', + cookieSupport: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}], + options: {keyvalues: [{key: 'key', value: 'value'}]} + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with ad blocker revovered parameter', function() { + sandbox.stub(utils, 'getWindowTop').returns({ I12C: { Morph: 1 } }); + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'http://www.domain.com', + version: '1.1', + cookieSupport: true, + rcv: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should pass gdpr true parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testRequest = clone(bidderRequest); + testRequest.gdprConsent = { + gdprApplies: true, + consentString: 'test' + }; + let result = spec.buildRequests(testRequest.bids, testRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: true, + gdprApplies: true, + gdprConsent: 'test', + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should pass gdpr false parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testRequest = clone(bidderRequest); + testRequest.gdprConsent = { + gdprApplies: false + }; + let result = spec.buildRequests(testRequest.bids, testRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: true, + gdprApplies: false, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should pass no cookie support', function() { + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => false); + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + let result = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: false, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should pass no cookie support Safari', function() { + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => true); + let result = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: false, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should use params.url, then config pageUrl, then getTopWindowUrl', function() { + let testRequest = clone(bidderRequest); + sandbox.stub(utils, 'getTopWindowUrl').callsFake(() => 'http://www.topurl.com'); + + let result = spec.buildRequests(testRequest.bids, testRequest); + let data = JSON.parse(result.data); + + expect(data.url).to.equal('http://www.domain.com'); + + delete testRequest.bids[0].params.url; + + result = spec.buildRequests(testRequest.bids, testRequest); + data = JSON.parse(result.data); + + expect(data.url).to.equal('http://www.topurl.com'); + + let origGetConfig = config.getConfig; + sandbox.stub(config, 'getConfig').callsFake(function (key) { + if (key === 'pageUrl') { + return 'http://www.configurl.com'; + } + return origGetConfig.apply(config, arguments); + }); + + result = spec.buildRequests(testRequest.bids, testRequest); + data = JSON.parse(result.data); + + expect(data.url).to.equal('http://www.configurl.com'); + }); + + it('should make use of pubcid if available', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + testbidRequest.bids[0].crumbs = {pubcid: 'pubcid 123'}; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'pubcid 123', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: true, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make userId take precedence over pubcid', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + testbidRequest.bids[0].crumbs = {pubcid: 'pubcid 123'}; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('//lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'http://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.1', + cookieSupport: true, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + }); + + describe('interpretResponse', function () { + it('should handle single success response', function() { + let lwResponse = { + ads: [ + { + id: '28e5ddf4-3c01-11e8-86a7-0a44794250d4', + callerId: 'site_outsider_0', + tag: 'ad', + width: 300, + height: 250, + cpmBid: 2.565917, + bidId: '32e50fad901ae89', + auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + ttl: 120 + } + ], + currency: 'USD' + }; + + let expectedResponse = [{ + requestId: '32e50fad901ae89', + bidderCode: 'livewrapped', + cpm: 2.565917, + width: 300, + height: 250, + ad: 'ad', + ttl: 120, + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + netRevenue: true, + currency: 'USD' + }]; + + let bids = spec.interpretResponse({body: lwResponse}); + + expect(bids).to.deep.equal(expectedResponse); + }) + + it('should handle multiple success response', function() { + let lwResponse = { + ads: [ + { + id: '28e5ddf4-3c01-11e8-86a7-0a44794250d4', + callerId: 'site_outsider_0', + tag: 'ad1', + width: 300, + height: 250, + cpmBid: 2.565917, + bidId: '32e50fad901ae89', + auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + ttl: 120 + }, + { + id: '38e5ddf4-3c01-11e8-86a7-0a44794250d4', + callerId: 'site_outsider_1', + tag: 'ad2', + width: 980, + height: 240, + cpmBid: 3.565917, + bidId: '42e50fad901ae89', + auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', + creativeId: '62cbd598-2715-4c43-a06f-229fc170f945:427077', + ttl: 120 + } + ], + currency: 'USD' + }; + + let expectedResponse = [{ + requestId: '32e50fad901ae89', + bidderCode: 'livewrapped', + cpm: 2.565917, + width: 300, + height: 250, + ad: 'ad1', + ttl: 120, + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + netRevenue: true, + currency: 'USD' + }, { + requestId: '42e50fad901ae89', + bidderCode: 'livewrapped', + cpm: 3.565917, + width: 980, + height: 240, + ad: 'ad2', + ttl: 120, + creativeId: '62cbd598-2715-4c43-a06f-229fc170f945:427077', + netRevenue: true, + currency: 'USD' + }]; + + let bids = spec.interpretResponse({body: lwResponse}); + + expect(bids).to.deep.equal(expectedResponse); + }) + }); + + describe('user sync', function () { + let serverResponses; + + beforeEach(function () { + serverResponses = [{ + body: { + pixels: [ + {type: 'Redirect', url: 'http://pixelsync'}, + {type: 'Iframe', url: 'http://iframesync'} + ] + } + }]; + }); + + it('should return empty if no server responses', function() { + let syncs = spec.getUserSyncs({ + pixelEnabled: true, + iframeEnabled: true + }, []); + + let expectedResponse = []; + + expect(syncs).to.deep.equal(expectedResponse) + }); + + it('should return empty if no user sync', function() { + let syncs = spec.getUserSyncs({ + pixelEnabled: true, + iframeEnabled: true + }, [{body: {}}]); + + let expectedResponse = []; + + expect(syncs).to.deep.equal(expectedResponse) + }); + + it('should returns pixel and iframe user sync', function() { + let syncs = spec.getUserSyncs({ + pixelEnabled: true, + iframeEnabled: true + }, serverResponses); + + let expectedResponse = [{type: 'image', url: 'http://pixelsync'}, {type: 'iframe', url: 'http://iframesync'}]; + + expect(syncs).to.deep.equal(expectedResponse) + }); + + it('should returns pixel only if iframe not supported user sync', function() { + let syncs = spec.getUserSyncs({ + pixelEnabled: true, + iframeEnabled: false + }, serverResponses); + + let expectedResponse = [{type: 'image', url: 'http://pixelsync'}]; + + expect(syncs).to.deep.equal(expectedResponse) + }); + + it('should returns iframe only if pixel not supported user sync', function() { + let syncs = spec.getUserSyncs({ + pixelEnabled: false, + iframeEnabled: true + }, serverResponses); + + let expectedResponse = [{type: 'iframe', url: 'http://iframesync'}]; + + expect(syncs).to.deep.equal(expectedResponse) + }); + }); +}); + +function clone(obj) { + return JSON.parse(JSON.stringify(obj)); +} diff --git a/test/spec/modules/liveyieldAnalyticsAdapter_spec.js b/test/spec/modules/liveyieldAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..36ade5ce67b --- /dev/null +++ b/test/spec/modules/liveyieldAnalyticsAdapter_spec.js @@ -0,0 +1,704 @@ +import CONSTANTS from 'src/constants.json'; +import liveyield from 'modules/liveyieldAnalyticsAdapter'; +import { expect } from 'chai'; +const events = require('src/events'); + +const { + EVENTS: { BID_REQUESTED, BID_TIMEOUT, BID_RESPONSE, BID_WON } +} = CONSTANTS; + +describe('liveyield analytics adapter', function() { + const rtaCalls = []; + + window.rta = function() { + rtaCalls.push({ callArgs: arguments }); + }; + + beforeEach(function() { + sinon.stub(events, 'getEvents').returns([]); + }); + afterEach(function() { + events.getEvents.restore(); + }); + + describe('initialization', function() { + afterEach(function() { + rtaCalls.length = 0; + }); + it('it should require provider', function() { + liveyield.enableAnalytics({}); + expect(rtaCalls).to.be.empty; + }); + it('should require config.options', function() { + liveyield.enableAnalytics({ provider: 'liveyield' }); + expect(rtaCalls).to.be.empty; + }); + it('should require options.customerId', function() { + liveyield.enableAnalytics({ provider: 'liveyield', options: {} }); + expect(rtaCalls).to.be.empty; + }); + it('should require options.customerName', function() { + liveyield.enableAnalytics({ + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa' + } + }); + expect(rtaCalls).to.be.empty; + }); + it('should require options.customerSite', function() { + liveyield.enableAnalytics({ + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean' + } + }); + expect(rtaCalls).to.be.empty; + }); + it('should require options.sessionTimezoneOffset', function() { + liveyield.enableAnalytics({ + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com' + } + }); + expect(rtaCalls).to.be.empty; + }); + it("should throw error, when 'rta' function is not defined ", function() { + const keepMe = window.rta; + + delete window.rta; + + liveyield.enableAnalytics({ + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12 + } + }); + expect(rtaCalls).to.be.empty; + + window.rta = keepMe; + }); + it('should initialize when all required parameters are passed', function() { + liveyield.enableAnalytics({ + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12 + } + }); + expect(rtaCalls[0].callArgs['0']).to.match(/create/); + expect(rtaCalls[0].callArgs['1']).to.match( + /d6a6f8da-190f-47d6-ae11-f1a4469083fa/ + ); + expect(rtaCalls[0].callArgs['2']).to.match(/pubocean/); + expect(rtaCalls[0].callArgs['4']).to.match(/12/); + liveyield.disableAnalytics(); + }); + it('should allow to redefine rta function name', function() { + const keepMe = window.rta; + window.abc = keepMe; + delete window.rta; + liveyield.enableAnalytics({ + provider: 'liveyield', + options: { + rtaFunctionName: 'abc', + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'test', + customerSite: 'scribol.com', + sessionTimezoneOffset: 25 + } + }); + + liveyield.disableAnalytics(); + expect(rtaCalls[0].callArgs['0']).to.match(/create/); + expect(rtaCalls[0].callArgs['1']).to.match( + /d6a6f8da-190f-47d6-ae11-f1a4469083fa/ + ); + expect(rtaCalls[0].callArgs['2']).to.match(/test/); + expect(rtaCalls[0].callArgs['4']).to.match(/25/); + + window.rta = keepMe; + liveyield.disableAnalytics(); + }); + it('should handle custom parameters', function() { + liveyield.enableAnalytics({ + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'test2', + customerSite: 'scribol.com', + sessionTimezoneOffset: 38, + contentTitle: 'testTitle', + contentAuthor: 'testAuthor', + contentCategory: 'testCategory' + } + }); + + liveyield.disableAnalytics(); + expect(rtaCalls[0].callArgs['0']).to.match(/create/); + expect(rtaCalls[0].callArgs['2']).to.match(/test2/); + expect(rtaCalls[0].callArgs['4']).to.match(/38/); + expect(rtaCalls[0].callArgs['5'].contentTitle).to.match(/testTitle/); + expect(rtaCalls[0].callArgs['5'].contentAuthor).to.match(/testAuthor/); + expect(rtaCalls[0].callArgs['5'].contentCategory).to.match( + /testCategory/ + ); + liveyield.disableAnalytics(); + }); + }); + + describe('handling events', function() { + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12 + } + }; + beforeEach(function() { + rtaCalls.length = 0; + liveyield.enableAnalytics(options); + }); + afterEach(function() { + liveyield.disableAnalytics(); + }); + it('should handle BID_REQUESTED event', function() { + const bidRequest = { + bidderCode: 'appnexus', + bids: [ + { + params: { + placementId: '10433394' + }, + adUnitCode: 'div-gpt-ad-1438287399331-0', + transactionId: '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', + sizes: '300x250,300x600', + bidId: '2eddfdc0c791dc', + auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7' + } + ] + }; + + events.emit(BID_REQUESTED, bidRequest); + + expect(rtaCalls[1].callArgs['0']).to.equal('bidRequested'); + expect(rtaCalls[1].callArgs['1']).to.equal('div-gpt-ad-1438287399331-0'); + expect(rtaCalls[1].callArgs['2']).to.equal('appnexus'); + }); + it('should handle BID_REQUESTED event with invalid args', function() { + const bidRequest = { + bids: [ + { + params: { + placementId: '10433394' + }, + transactionId: '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', + sizes: '300x250,300x600', + bidId: '2eddfdc0c791dc', + auctionId: 'a5b849e5-87d7-4205-8300-d063084fcf' + }, + { + params: { + placementId: '31034023' + }, + transactionId: '2f481ff1-8d20-4c28-8e36-e384e9e3eec6', + sizes: '300x250,300x600', + bidId: '3dkg0404fmd0', + auctionId: 'a5b849e5-87d7-4205-8300-d063084fcf' + } + ] + }; + events.emit(BID_REQUESTED, bidRequest); + expect(rtaCalls[1].callArgs['0']).to.equal('bidRequested'); + expect(rtaCalls[1].callArgs['1']).to.equal(undefined); + expect(rtaCalls[1].callArgs['2']).to.equal(undefined); + expect(rtaCalls[1].callArgs['0']).to.equal('bidRequested'); + }); + it('should handle BID_RESPONSE event', function() { + const bidResponse = { + height: 250, + statusMessage: 'Bid available', + adId: '2eddfdc0c791dc', + mediaType: 'banner', + source: 'client', + requestId: '2eddfdc0c791dc', + cpm: 0.5, + creativeId: 29681110, + currency: 'USD', + netRevenue: true, + ttl: 300, + auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7', + responseTimestamp: 1522265866110, + requestTimestamp: 1522265863600, + bidder: 'appnexus', + adUnitCode: 'div-gpt-ad-1438287399331-0', + timeToRespond: 2510, + size: '300x250' + }; + + events.emit(BID_RESPONSE, bidResponse); + expect(rtaCalls[1].callArgs['0']).to.equal('addBid'); + expect(rtaCalls[1].callArgs['1']).to.equal('div-gpt-ad-1438287399331-0'); + expect(rtaCalls[1].callArgs['2']).to.equal('appnexus'); + expect(rtaCalls[1].callArgs['3']).to.equal(500); + expect(rtaCalls[1].callArgs['4']).to.equal(false); + expect(rtaCalls[1].callArgs['5']).to.equal(false); + }); + it('should handle BID_RESPONSE event with undefined bidder and cpm', function() { + const bidResponse = { + height: 250, + statusMessage: 'Bid available', + adId: '2eddfdc0c791dc', + mediaType: 'banner', + source: 'client', + requestId: '2eddfdc0c791dc', + creativeId: 29681110, + currency: 'USD', + netRevenue: true, + ttl: 300, + auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7', + responseTimestamp: 1522265866110, + requestTimestamp: 1522265863600, + adUnitCode: 'div-gpt-ad-1438287399331-0', + timeToRespond: 2510, + size: '300x250' + }; + events.emit(BID_RESPONSE, bidResponse); + expect(rtaCalls[1].callArgs['0']).to.equal('addBid'); + expect(rtaCalls[1].callArgs['2']).to.equal('unknown'); + expect(rtaCalls[1].callArgs['3']).to.equal(0); + expect(rtaCalls[1].callArgs['4']).to.equal(true); + }); + it('should handle BID_RESPONSE event with undefined status message and adUnitCode', function() { + const bidResponse = { + height: 250, + adId: '2eddfdc0c791dc', + mediaType: 'banner', + source: 'client', + requestId: '2eddfdc0c791dc', + cpm: 0.5, + creativeId: 29681110, + currency: 'USD', + netRevenue: true, + ttl: 300, + auctionId: 'a5b849e5-87d7-4205-8300-d063084fcfb7', + responseTimestamp: 1522265866110, + requestTimestamp: 1522265863600, + bidder: 'appnexus', + timeToRespond: 2510, + size: '300x250' + }; + events.emit(BID_RESPONSE, bidResponse); + expect(rtaCalls[1].callArgs['0']).to.equal('addBid'); + expect(rtaCalls[1].callArgs['1']).to.equal(undefined); + expect(rtaCalls[1].callArgs['3']).to.equal(0); + expect(rtaCalls[1].callArgs['5']).to.equal(true); + }); + it('should handle BID_TIMEOUT', function() { + const bidTimeout = [ + { + bidId: '2baa51527bd015', + bidder: 'bidderOne', + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + }, + { + bidId: '6fe3b4c2c23092', + bidder: 'bidderTwo', + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + } + ]; + events.emit(BID_TIMEOUT, bidTimeout); + expect(rtaCalls[1].callArgs['0']).to.equal('biddersTimeout'); + expect(rtaCalls[1].callArgs['1'].length).to.equal(2); + }); + it('should handle BID_WON event', function() { + const bidWon = { + adId: '4587fec4900b81', + mediaType: 'banner', + requestId: '4587fec4900b81', + cpm: 1.962, + creativeId: 2126, + currency: 'EUR', + netRevenue: true, + ttl: 302, + auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', + statusMessage: 'Bid available', + responseTimestamp: 1530628534437, + requestTimestamp: 1530628534219, + bidderCode: 'testbidder4', + adUnitCode: 'div-gpt-ad-1438287399331-0', + timeToRespond: 218, + size: '300x250', + status: 'rendered' + }; + events.emit(BID_WON, bidWon); + expect(rtaCalls[1].callArgs['0']).to.equal('resolveSlot'); + expect(rtaCalls[1].callArgs['1']).to.equal('div-gpt-ad-1438287399331-0'); + expect(rtaCalls[1].callArgs['2'].prebidWon).to.equal(true); + expect(rtaCalls[1].callArgs['2'].prebidPartner).to.equal('testbidder4'); + expect(rtaCalls[1].callArgs['2'].prebidValue).to.equal(1962); + }); + it('should throw error, invoking BID_WON event without adUnitCode', function() { + const bidWon = { + adId: '4587fec4900b81', + mediaType: 'banner', + requestId: '4587fec4900b81', + cpm: 1.962, + creativeId: 2126, + currency: 'EUR', + netRevenue: true, + ttl: 302, + auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', + statusMessage: 'Bid available', + responseTimestamp: 1530628534437, + requestTimestamp: 1530628534219, + timeToRespond: 218, + bidderCode: 'testbidder4', + size: '300x250', + status: 'rendered' + }; + events.emit(BID_WON, bidWon); + expect(rtaCalls[1]).to.be.undefined; + }); + it('should throw error, invoking BID_WON event without bidderCode', function() { + const bidWon = { + adId: '4587fec4900b81', + mediaType: 'banner', + requestId: '4587fec4900b81', + cpm: 1.962, + creativeId: 2126, + currency: 'EUR', + netRevenue: true, + ttl: 302, + auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', + statusMessage: 'Bid available', + responseTimestamp: 1530628534437, + requestTimestamp: 1530628534219, + adUnitCode: 'div-gpt-ad-1438287399331-0', + timeToRespond: 218, + size: '300x250', + status: 'rendered' + }; + events.emit(BID_WON, bidWon); + expect(rtaCalls[1]).to.be.undefined; + }); + }); + + describe('googletag use case', function() { + beforeEach(function() { + rtaCalls.length = 0; + }); + afterEach(function() { + liveyield.disableAnalytics(); + }); + it('should ignore BID_WON events when gpt is provided', function() { + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12, + googlePublisherTag: true, + wireGooglePublisherTag: function() { + return null; + } + } + }; + liveyield.enableAnalytics(options); + + const bidWon = { + adId: 'ignore_me', + mediaType: 'banner', + requestId: '4587fec4900b81', + cpm: 1.962, + creativeId: 2126, + currency: 'EUR', + netRevenue: true, + ttl: 302, + auctionId: '914bedad-b145-4e46-ba58-51365faea6cb', + statusMessage: 'Bid available', + responseTimestamp: 1530628534437, + requestTimestamp: 1530628534219, + bidderCode: 'hello', + adUnitCode: 'div-gpt-ad-1438287399331-0', + timeToRespond: 218, + size: '300x250', + status: 'rendered' + }; + events.emit(BID_WON, bidWon); + + expect(rtaCalls.length).to.equal(1); + }); + it('should subscribe to slotRenderEnded', function() { + var googletag; + var callback; + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12, + googlePublisherTag: 'testGPT', + wireGooglePublisherTag: function(gpt, cb) { + googletag = gpt; + callback = cb; + } + } + }; + liveyield.enableAnalytics(options); + expect(googletag).to.equal('testGPT'); + expect(typeof callback).to.equal('function'); + }); + it('should handle BID_WON event for prebid', function() { + var call; + const slot = { + getResponseInformation: function() { + const dfpInfo = { + dfpAdvertiserId: 1, + dfpLineItemId: 2, + dfpCreativeId: 3 + }; + return dfpInfo; + }, + getTargeting: function(v) { + return ['4587fec4900b81']; + } + }; + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12, + googlePublisherTag: 'testGPT', + wireGooglePublisherTag: function(gpt, cb) { + call = cb; + }, + getHighestPrebidAdImpressionPartner: function(slot, version) { + return 'testbidder4'; + }, + getHighestPrebidAdImpressionValue: function(slot, version) { + return 12; + }, + postProcessResolution: function( + resolution, + slot, + hbPartner, + hbValue, + version + ) { + return resolution; + }, + getAdUnitNameByGooglePublisherTagSlot: function(slot, version) { + return 'testUnit'; + } + } + }; + liveyield.enableAnalytics(options); + const bidWon = { + adId: '4587fec4900b81' + }; + events.emit(BID_WON, bidWon); + call(slot); + expect(rtaCalls[1].callArgs['2'].prebidWon).to.equal(true); + expect(rtaCalls[1].callArgs['2'].prebidPartner).to.equal('testbidder4'); + }); + it('should handle BID_WON event for dfp', function() { + let call; + const slot = { + getResponseInformation: function() { + const dfpInfo = { + dfpAdvertiserId: 1, + dfpLineItemId: 2, + dfpCreativeId: 3 + }; + return dfpInfo; + } + }; + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12, + googlePublisherTag: 'testGPT', + wireGooglePublisherTag: function(gpt, cb) { + call = cb; + }, + getHighestPrebidAdImpressionPartner: function(slot, version) { + return 'testbidder4'; + }, + getHighestPrebidAdImpressionValue: function(slot, version) { + return 12; + }, + postProcessResolution: function(slot, version) { + return { partner: slot.prebidPartner, value: slot.prebidValue }; + }, + getAdUnitNameByGooglePublisherTagSlot: function(slot, version) { + return 'testUnit'; + }, + isPrebidAdImpression: function(slot) { + return true; + } + } + }; + liveyield.enableAnalytics(options); + call(slot); + expect(rtaCalls.length).to.equal(2); + expect(rtaCalls[1].callArgs[0]).to.equal('resolveSlot'); + expect(rtaCalls[1].callArgs[1]).to.equal('testUnit'); + expect(rtaCalls[1].callArgs[2].partner).to.equal('testbidder4'); + expect(rtaCalls[1].callArgs[2].value).to.equal(12000); + }); + it('should work with defaults: prebid won', () => { + let call; + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12, + googlePublisherTag: 'testGPT', + wireGooglePublisherTag: (gpt, cb) => (call = cb), + getAdUnitName: adUnitCode => + adUnitCode === 'PREBID_UNIT' ? 'ADUNIT' : null, + getAdUnitNameByGooglePublisherTagSlot: slot => + slot.getSlotElementId() === 'div-gpt-ad-1438287399331-0' + ? 'ADUNIT' + : null + } + }; + liveyield.enableAnalytics(options); + + const bidResponse = { + adId: 'defaults_with_cache', + statusMessage: 'Bid available', + cpm: 0.5, + bidder: 'appnexus', + adUnitCode: 'PREBID_UNIT' + }; + events.emit(BID_RESPONSE, bidResponse); + + const bidWon = { + adId: bidResponse.adId, + cpm: 1.962, // adjusted, this one shall be used, not the one from bidResponse + bidderCode: 'appnexus_from_bid_won_event', + adUnitCode: 'PREBID_UNIT' + }; + events.emit(BID_WON, bidWon); + + const slot = { + getTargeting: key => (key === 'hb_adid' ? [bidResponse.adId] : []), + getResponseInformation: () => null, + getSlotElementId: () => 'div-gpt-ad-1438287399331-0' + }; + call(slot); + + expect(rtaCalls[2].callArgs[0]).to.equal('resolveSlot'); + expect(rtaCalls[2].callArgs[1]).to.equal('ADUNIT'); + expect(rtaCalls[2].callArgs[2]).to.deep.equal({ + targetings: [], + prebidWon: true, + prebidPartner: 'appnexus_from_bid_won_event', + prebidValue: 1962 + }); + }); + it('should work with defaults: dfp won, prebid bid response', () => { + let call; + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12, + googlePublisherTag: 'testGPT', + wireGooglePublisherTag: (gpt, cb) => (call = cb), + getAdUnitName: adUnitCode => + adUnitCode === 'PREBID_UNIT' ? 'ADUNIT' : null, + getAdUnitNameByGooglePublisherTagSlot: slot => + slot.getSlotElementId() === 'div-gpt-ad-1438287399331-0' + ? 'ADUNIT' + : null + } + }; + liveyield.enableAnalytics(options); + + const bidResponse = { + adId: 'defaults_with_cache_no_prebid_win', + statusMessage: 'Bid available', + cpm: 0.5, + bidder: 'appnexus', + adUnitCode: 'PREBID_UNIT' + }; + events.emit(BID_RESPONSE, bidResponse); + + const slot = { + getTargeting: key => (key === 'hb_adid' ? [bidResponse.adId] : []), + getResponseInformation: () => null, + getSlotElementId: () => 'div-gpt-ad-1438287399331-0' + }; + call(slot); + + expect(rtaCalls[2].callArgs[0]).to.equal('resolveSlot'); + expect(rtaCalls[2].callArgs[1]).to.equal('ADUNIT'); + expect(rtaCalls[2].callArgs[2]).to.deep.equal({ + targetings: [], + prebidPartner: 'appnexus', + prebidValue: 500 + }); + }); + it('should work with defaults: dfp won, without prebid', () => { + let call; + const options = { + provider: 'liveyield', + options: { + customerId: 'd6a6f8da-190f-47d6-ae11-f1a4469083fa', + customerName: 'pubocean', + customerSite: 'scribol.com', + sessionTimezoneOffset: 12, + googlePublisherTag: 'testGPT', + wireGooglePublisherTag: (gpt, cb) => (call = cb), + getAdUnitName: adUnitCode => + adUnitCode === 'PREBID_UNIT' ? 'ADUNIT' : null, + getAdUnitNameByGooglePublisherTagSlot: slot => + slot.getSlotElementId() === 'div-gpt-ad-1438287399331-0' + ? 'ADUNIT' + : null + } + }; + liveyield.enableAnalytics(options); + + const slot = { + getTargeting: key => (key === 'hb_adid' ? ['does-not-exist'] : []), + getResponseInformation: () => null, + getSlotElementId: () => 'div-gpt-ad-1438287399331-0' + }; + call(slot); + + expect(rtaCalls[1].callArgs[0]).to.equal('resolveSlot'); + expect(rtaCalls[1].callArgs[1]).to.equal('ADUNIT'); + expect(rtaCalls[1].callArgs[2]).to.deep.equal({ + targetings: [] + }); + }); + }); +}); diff --git a/test/spec/modules/lkqdBidAdapter_spec.js b/test/spec/modules/lkqdBidAdapter_spec.js index d3e16f0c99a..73a4824f0ef 100644 --- a/test/spec/modules/lkqdBidAdapter_spec.js +++ b/test/spec/modules/lkqdBidAdapter_spec.js @@ -2,16 +2,16 @@ import { spec } from 'modules/lkqdBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; const { expect } = require('chai'); -describe('LKQD Bid Adapter Test', () => { +describe('LKQD Bid Adapter Test', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'lkqd', 'params': { @@ -26,11 +26,11 @@ describe('LKQD Bid Adapter Test', () => { 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -41,7 +41,7 @@ describe('LKQD Bid Adapter Test', () => { }); describe('buildRequests', () => { - const ENDPOINT = 'https://ssp.lkqd.net/ad?'; + const ENDPOINT = 'https://v.lkqd.net/ad'; let bidRequests = [ { 'bidder': 'lkqd', @@ -73,47 +73,61 @@ describe('LKQD Bid Adapter Test', () => { } ]; - it('should populate available parameters', () => { + it('should populate available parameters', function () { const requests = spec.buildRequests(bidRequests); expect(requests.length).to.equal(2); - const r1 = requests[0].url; - expect(r1).to.contain('?pid=263'); - expect(r1).to.contain('&sid=662921'); - expect(r1).to.contain('&width=300'); - expect(r1).to.contain('&height=250'); - const r2 = requests[1].url; - expect(r2).to.contain('?pid=263'); - expect(r2).to.contain('&sid=662921'); - expect(r2).to.contain('&width=640'); - expect(r2).to.contain('&height=480'); + const r1 = requests[0].data; + expect(r1).to.have.property('pid'); + expect(r1.pid).to.equal('263'); + expect(r1).to.have.property('sid'); + expect(r1.sid).to.equal('662921'); + expect(r1).to.have.property('width'); + expect(r1.width).to.equal(300); + expect(r1).to.have.property('height'); + expect(r1.height).to.equal(250); + const r2 = requests[1].data; + expect(r2).to.have.property('pid'); + expect(r2.pid).to.equal('263'); + expect(r2).to.have.property('sid'); + expect(r2.sid).to.equal('662921'); + expect(r2).to.have.property('width'); + expect(r2.width).to.equal(640); + expect(r2).to.have.property('height'); + expect(r2.height).to.equal(480); }); - it('should not populate unspecified parameters', () => { + it('should not populate unspecified parameters', function () { const requests = spec.buildRequests(bidRequests); expect(requests.length).to.equal(2); - const r1 = requests[0].url; - expect(r1).to.contain('‌&contentid=[CONTENT_ID]'); - expect(r1).to.contain('‌&contenttitle=[CONTENT_TITLE]'); - expect(r1).to.contain('‌&contentlength=[CONTENT_LENGTH]'); - expect(r1).to.contain('&height=250'); - const r2 = requests[1].url; - expect(r2).to.contain('‌&contentid=[CONTENT_ID]'); - expect(r2).to.contain('‌&contenttitle=[CONTENT_TITLE]'); - expect(r2).to.contain('‌&contentlength=[CONTENT_LENGTH]'); - expect(r2).to.contain('‌&contenturl=[CONTENT_URL]'); + const r1 = requests[0].data; + expect(r1).to.not.have.property('dnt'); + expect(r1).to.not.have.property('contentid'); + expect(r1).to.not.have.property('contenttitle'); + expect(r1).to.not.have.property('contentlength'); + expect(r1).to.not.have.property('contenturl'); + const r2 = requests[1].data; + expect(r2).to.not.have.property('dnt'); + expect(r2).to.not.have.property('contentid'); + expect(r2).to.not.have.property('contenttitle'); + expect(r2).to.not.have.property('contentlength'); + expect(r2).to.not.have.property('contenturl'); }); - it('should handle single size request', () => { + it('should handle single size request', function () { const requests = spec.buildRequests(bidRequest); expect(requests.length).to.equal(1); - const r1 = requests[0].url; - expect(r1).to.contain('?pid=263'); - expect(r1).to.contain('&sid=662921'); - expect(r1).to.contain('&width=640'); - expect(r1).to.contain('&height=480'); + const r1 = requests[0].data; + expect(r1).to.have.property('pid'); + expect(r1.pid).to.equal('263'); + expect(r1).to.have.property('sid'); + expect(r1.sid).to.equal('662921'); + expect(r1).to.have.property('width'); + expect(r1.width).to.equal(640); + expect(r1).to.have.property('height'); + expect(r1.height).to.equal(480); }); - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { const requests = spec.buildRequests(bidRequests); expect(requests.length).to.equal(2); const r1 = requests[0]; @@ -125,7 +139,7 @@ describe('LKQD Bid Adapter Test', () => { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let bidRequest = { 'url': 'https://ssp.lkqd.net/ad?pid=263&sid=662921&output=vast&execution=any&placement=&playinit=auto&volume=100&timeout=&width=300%E2%80%8C&height=250&pbt=[PREBID_TOKEN]%E2%80%8C&dnt=[DO_NOT_TRACK]%E2%80%8C&pageurl=[PAGEURL]%E2%80%8C&contentid=[CONTENT_ID]%E2%80%8C&contenttitle=[CONTENT_TITLE]%E2%80%8C&contentlength=[CONTENT_LENGTH]%E2%80%8C&contenturl=[CONTENT_URL]&prebid=true%E2%80%8C&rnd=874313435?bidId=253dcb69fb2577&bidWidth=300&bidHeight=250&', 'data': { @@ -306,7 +320,7 @@ https://creative.lkqd.net/internal/lkqd_300x250.mp4
    `; - it('should correctly parse valid bid response', () => { + it('should correctly parse valid bid response', function () { const BIDDER_CODE = 'lkqd'; let bidResponses = spec.interpretResponse(serverResponse, bidRequest); expect(bidResponses.length).to.equal(1); @@ -324,7 +338,7 @@ https://creative.lkqd.net/internal/lkqd_300x250.mp4 expect(bidResponse.mediaType).to.equal('video'); }); - it('safely handles XML parsing failure from invalid bid response', () => { + it('safely handles XML parsing failure from invalid bid response', function () { let invalidServerResponse = {}; invalidServerResponse.body = ''; @@ -332,7 +346,7 @@ https://creative.lkqd.net/internal/lkqd_300x250.mp4 expect(result.length).to.equal(0); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let nobidResponse = {}; nobidResponse.body = ''; diff --git a/test/spec/modules/lockerdomeBidAdapter_spec.js b/test/spec/modules/lockerdomeBidAdapter_spec.js index 84f0dd43e72..6a3fd814030 100644 --- a/test/spec/modules/lockerdomeBidAdapter_spec.js +++ b/test/spec/modules/lockerdomeBidAdapter_spec.js @@ -2,11 +2,11 @@ import { expect } from 'chai'; import { spec } from '../../../modules/lockerdomeBidAdapter'; import * as utils from 'src/utils'; -describe('LockerDomeAdapter', () => { +describe('LockerDomeAdapter', function () { const bidRequests = [{ bidder: 'lockerdome', params: { - adUnitId: 10809467961050726 + adUnitId: 'LD10809467961050726' }, mediaTypes: { banner: { @@ -22,7 +22,7 @@ describe('LockerDomeAdapter', () => { }, { bidder: 'lockerdome', params: { - adUnitId: 9434769725128806 + adUnitId: 'LD9434769725128806' }, mediaTypes: { banner: { @@ -37,20 +37,20 @@ describe('LockerDomeAdapter', () => { auctionId: 'd4c83108-615d-4c2c-9384-dac9ffd4fd72' }]; - describe('isBidRequestValid', () => { - it('should return true if the adUnitId parameter is present', () => { + describe('isBidRequestValid', function () { + it('should return true if the adUnitId parameter is present', function () { expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; expect(spec.isBidRequestValid(bidRequests[1])).to.be.true; }); - it('should return false if the adUnitId parameter is not present', () => { + it('should return false if the adUnitId parameter is not present', function () { let bidRequest = utils.deepClone(bidRequests[0]); delete bidRequest.params.adUnitId; expect(spec.isBidRequestValid(bidRequest)).to.be.false; }); }); - describe('buildRequests', () => { - it('should generate a valid single POST request for multiple bid requests', () => { + describe('buildRequests', function () { + it('should generate a valid single POST request for multiple bid requests', function () { const request = spec.buildRequests(bidRequests); expect(request.method).to.equal('POST'); expect(request.url).to.equal('https://lockerdome.com/ladbid/prebid'); @@ -65,19 +65,21 @@ describe('LockerDomeAdapter', () => { expect(bids).to.have.lengthOf(2); expect(bids[0].requestId).to.equal('2652ca954bce9'); - expect(bids[0].adUnitId).to.equal(10809467961050726); + expect(bids[0].adUnitCode).to.equal('ad-1'); + expect(bids[0].adUnitId).to.equal('LD10809467961050726'); expect(bids[0].sizes).to.have.lengthOf(1); expect(bids[0].sizes[0][0]).to.equal(300); expect(bids[0].sizes[0][1]).to.equal(250); expect(bids[1].requestId).to.equal('4510f2834773ce'); - expect(bids[1].adUnitId).to.equal(9434769725128806); + expect(bids[1].adUnitCode).to.equal('ad-2'); + expect(bids[1].adUnitId).to.equal('LD9434769725128806'); expect(bids[1].sizes).to.have.lengthOf(1); expect(bids[1].sizes[0][0]).to.equal(300); expect(bids[1].sizes[0][1]).to.equal(600); }); - it('should add GDPR data to request if available', () => { + it('should add GDPR data to request if available', function () { const bidderRequest = { gdprConsent: { consentString: 'AAABBB', @@ -93,13 +95,13 @@ describe('LockerDomeAdapter', () => { }); }); - describe('interpretResponse', () => { - it('should return an empty array if an invalid response is passed', () => { + describe('interpretResponse', function () { + it('should return an empty array if an invalid response is passed', function () { const interpretedResponse = spec.interpretResponse({ body: {} }); expect(interpretedResponse).to.be.an('array').that.is.empty; }); - it('should return valid response when passed valid server response', () => { + it('should return valid response when passed valid server response', function () { const serverResponse = { body: { bids: [{ diff --git a/test/spec/modules/logicadBidAdapter_spec.js b/test/spec/modules/logicadBidAdapter_spec.js new file mode 100644 index 00000000000..effb7334b69 --- /dev/null +++ b/test/spec/modules/logicadBidAdapter_spec.js @@ -0,0 +1,119 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/logicadBidAdapter'; +import * as utils from 'src/utils'; + +describe('LogicadAdapter', function () { + const bidRequests = [{ + bidder: 'logicad', + bidId: '51ef8751f9aead', + params: { + tid: 'PJ2P', + page: 'http://www.logicad.com/' + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + sizes: [[300, 250], [300, 600]], + bidderRequestId: '418b37f85e772c', + auctionId: '18fd8b8b0bd757', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + } + }]; + const bidderRequest = { + refererInfo: { + referer: 'fakeReferer', + reachedTop: true, + numIframes: 1, + stack: [] + }, + auctionStart: 1563337198010 + }; + const serverResponse = { + body: { + seatbid: + [{ + bid: { + requestId: '51ef8751f9aead', + cpm: 101.0234, + width: 300, + height: 250, + creativeId: '2019', + currency: 'JPY', + netRevenue: true, + ttl: 60, + ad: '
    TEST
    ' + } + }], + userSync: { + type: 'image', + url: 'https://cr-p31.ladsp.jp/cookiesender/31' + } + } + }; + + describe('isBidRequestValid', function () { + it('should return true if the tid parameter is present', function () { + expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; + }); + + it('should return false if the tid parameter is not present', function () { + let bidRequest = utils.deepClone(bidRequests[0]); + delete bidRequest.params.tid; + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }); + + it('should return false if the params object is not present', function () { + let bidRequest = utils.deepClone(bidRequests); + delete bidRequest[0].params; + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }); + }); + + describe('buildRequests', function () { + it('should generate a valid single POST request for multiple bid requests', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://pb.ladsp.com/adrequest/prebid'); + expect(request.data).to.exist; + }); + }); + + describe('interpretResponse', function () { + it('should return an empty array if an invalid response is passed', function () { + const interpretedResponse = spec.interpretResponse({}, {}); + expect(interpretedResponse).to.be.an('array').that.is.empty; + }); + + it('should return valid response when passed valid server response', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + const interpretedResponse = spec.interpretResponse(serverResponse, request); + + expect(interpretedResponse).to.have.lengthOf(1); + + expect(interpretedResponse[0].requestId).to.equal(serverResponse.body.seatbid[0].bid.requestId); + expect(interpretedResponse[0].cpm).to.equal(serverResponse.body.seatbid[0].bid.cpm); + expect(interpretedResponse[0].width).to.equal(serverResponse.body.seatbid[0].bid.width); + expect(interpretedResponse[0].height).to.equal(serverResponse.body.seatbid[0].bid.height); + expect(interpretedResponse[0].creativeId).to.equal(serverResponse.body.seatbid[0].bid.creativeId); + expect(interpretedResponse[0].currency).to.equal(serverResponse.body.seatbid[0].bid.currency); + expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.seatbid[0].bid.netRevenue); + expect(interpretedResponse[0].ad).to.equal(serverResponse.body.seatbid[0].bid.ad); + expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.seatbid[0].bid.ttl); + }); + }); + + describe('getUserSyncs', function () { + it('should perform usersync', function () { + let syncs = spec.getUserSyncs({pixelEnabled: false}, [serverResponse]); + expect(syncs).to.have.length(0); + + syncs = spec.getUserSyncs({pixelEnabled: true}, [serverResponse]); + expect(syncs).to.have.length(1); + + expect(syncs[0]).to.have.property('type', 'image'); + expect(syncs[0]).to.have.property('url', 'https://cr-p31.ladsp.jp/cookiesender/31'); + }); + }); +}); diff --git a/test/spec/modules/loopmeBidAdapter_spec.js b/test/spec/modules/loopmeBidAdapter_spec.js new file mode 100644 index 00000000000..0d7e71c22db --- /dev/null +++ b/test/spec/modules/loopmeBidAdapter_spec.js @@ -0,0 +1,167 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/loopmeBidAdapter'; +import * as utils from 'src/utils'; + +describe('LoopMeAdapter', function () { + const bidRequests = [{ + bidder: 'loopme', + params: { + ak: 'b510d5bcda' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + adUnitCode: 'ad-1', + bidId: '2652ca954bce9' + }, { + bidder: 'loopme', + params: { + ak: 'b510d5bcda' + }, + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'outstream' + } + }, + adUnitCode: 'ad-1', + bidId: '2652ca954bce9' + } + ]; + + describe('isBidRequestValid', function () { + it('should return true if the ak parameter is present', function () { + expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[1])).to.be.true; + }); + + it('should return false if the ak parameter is not present', function () { + let bannerBidRequest = utils.deepClone(bidRequests[0]); + delete bannerBidRequest.params.ak; + expect(spec.isBidRequestValid(bannerBidRequest)).to.be.false; + + let videoBidRequest = utils.deepClone(bidRequests[1]); + delete videoBidRequest.params.ak; + expect(spec.isBidRequestValid(videoBidRequest)).to.be.false; + }); + + it('should return false if the params object is not present', function () { + let bannerBidRequest = utils.deepClone(bidRequests)[0]; + delete bannerBidRequest.params; + expect(spec.isBidRequestValid(bannerBidRequest)).to.be.false; + + let videoBidRequest = utils.deepClone(bidRequests)[1]; + delete videoBidRequest.params; + expect(spec.isBidRequestValid(videoBidRequest)).to.be.false; + }); + }); + + describe('buildRequests', function () { + it('should generate a valid single GET request for multiple bid requests', function () { + const bannerRequest = spec.buildRequests(bidRequests)[0]; + expect(bannerRequest.method).to.equal('GET'); + expect(bannerRequest.url).to.equal('https://loopme.me/api/hb'); + expect(bannerRequest.bidId).to.equal('2652ca954bce9'); + expect(bannerRequest.data).to.exist; + + const bannerRequestData = bannerRequest.data; + expect(bannerRequestData).to.contain('ak=b510d5bcda'); + expect(bannerRequestData).to.contain('sizes=300x250'); + + const videoRequest = spec.buildRequests(bidRequests)[1]; + expect(videoRequest.method).to.equal('GET'); + expect(videoRequest.url).to.equal('https://loopme.me/api/hb'); + expect(videoRequest.bidId).to.equal('2652ca954bce9'); + expect(videoRequest.data).to.exist; + + const videoRquestData = videoRequest.data; + expect(videoRquestData).to.contain('ak=b510d5bcda'); + expect(videoRquestData).to.contain('sizes=640x480'); + expect(videoRquestData).to.contain('media_type=video'); + }); + + it('should add GDPR data to request if available', function () { + const bidderRequest = { + gdprConsent: { + consentString: 'AAABBB' + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + const requestData = request.data; + + expect(requestData).to.contain('user_consent=AAABBB'); + }); + }); + + describe('interpretResponse', function () { + it('should return an empty array if an invalid response is passed', function () { + const interpretedResponse = spec.interpretResponse({}); + expect(interpretedResponse).to.be.an('array').that.is.empty; + }); + + xit('should return valid response when passed valid server response', function () { + const serverResponse = { + body: { + 'requestId': '2652ca954bce9', + 'cpm': 1, + 'width': 480, + 'height': 320, + 'creativeId': '20154', + 'currency': 'USD', + 'netRevenue': false, + 'ttl': 360, + 'ad': `
    Hello
    ` + } + }; + + const request = spec.buildRequests(bidRequests)[0]; + const interpretedResponse = spec.interpretResponse(serverResponse, request); + + expect(interpretedResponse).to.have.lengthOf(1); + + expect(interpretedResponse[0].requestId).to.equal(serverResponse.body.requestId); + expect(interpretedResponse[0].cpm).to.equal(serverResponse.body.cpm); + expect(interpretedResponse[0].width).to.equal(serverResponse.body.width); + expect(interpretedResponse[0].height).to.equal(serverResponse.body.height); + expect(interpretedResponse[0].creativeId).to.equal(serverResponse.body.creativeId); + expect(interpretedResponse[0].currency).to.equal(serverResponse.body.currency); + expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.netRevenue); + expect(interpretedResponse[0].ad).to.equal(serverResponse.body.ad); + expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.ttl); + }); + + it('should return valid VAST response when passed valid server response', function () { + const serverResponse = { + body: { + 'requestId': '2652ca954bce9', + 'cpm': 1, + 'width': 640, + 'height': 480, + 'creativeId': '20154', + 'currency': 'USD', + 'netRevenue': false, + 'ttl': 360, + 'vastUrl': 'https://loopme.me/ads/vast?ak=cc885e3acc' + } + }; + + const request = spec.buildRequests(bidRequests)[1]; + const interpretedResponse = spec.interpretResponse(serverResponse, request); + + expect(interpretedResponse).to.have.lengthOf(1); + + expect(interpretedResponse[0].requestId).to.equal(serverResponse.body.requestId); + expect(interpretedResponse[0].cpm).to.equal(serverResponse.body.cpm); + expect(interpretedResponse[0].width).to.equal(serverResponse.body.width); + expect(interpretedResponse[0].height).to.equal(serverResponse.body.height); + expect(interpretedResponse[0].creativeId).to.equal(serverResponse.body.creativeId); + expect(interpretedResponse[0].currency).to.equal(serverResponse.body.currency); + expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.netRevenue); + expect(interpretedResponse[0].vastUrl).to.equal(serverResponse.body.vastUrl); + expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.ttl); + expect(interpretedResponse[0].renderer).to.have.property('render'); + }); + }); +}); diff --git a/test/spec/modules/madvertiseBidAdapter_spec.js b/test/spec/modules/madvertiseBidAdapter_spec.js index 64ecf556aa5..b2a08410532 100644 --- a/test/spec/modules/madvertiseBidAdapter_spec.js +++ b/test/spec/modules/madvertiseBidAdapter_spec.js @@ -1,9 +1,11 @@ import {expect} from 'chai'; +import {config} from 'src/config'; +import * as utils from 'src/utils'; import {spec} from 'modules/madvertiseBidAdapter'; -describe('madvertise adapater', () => { - describe('Test validate req', () => { - it('should accept minimum valid bid', () => { +describe('madvertise adapater', function () { + describe('Test validate req', function () { + it('should accept minimum valid bid', function () { let bid = { bidder: 'madvertise', sizes: [[728, 90]], @@ -15,7 +17,7 @@ describe('madvertise adapater', () => { expect(isValid).to.equal(true); }); - it('should reject no sizes', () => { + it('should reject no sizes', function () { let bid = { bidder: 'madvertise', params: { @@ -26,7 +28,7 @@ describe('madvertise adapater', () => { expect(isValid).to.equal(false); }); - it('should reject empty sizes', () => { + it('should reject empty sizes', function () { let bid = { bidder: 'madvertise', sizes: [], @@ -38,7 +40,7 @@ describe('madvertise adapater', () => { expect(isValid).to.equal(false); }); - it('should reject wrong format sizes', () => { + it('should reject wrong format sizes', function () { let bid = { bidder: 'madvertise', sizes: [['728x90']], @@ -49,7 +51,7 @@ describe('madvertise adapater', () => { const isValid = spec.isBidRequestValid(bid); expect(isValid).to.equal(false); }); - it('should reject no params', () => { + it('should reject no params', function () { let bid = { bidder: 'madvertise', sizes: [[728, 90]] @@ -58,7 +60,7 @@ describe('madvertise adapater', () => { expect(isValid).to.equal(false); }); - it('should reject missing s', () => { + it('should reject missing s', function () { let bid = { bidder: 'madvertise', params: {} @@ -69,32 +71,76 @@ describe('madvertise adapater', () => { }); }); - describe('Test build request', () => { - it('minimum request', () => { - let bid = [{ - bidder: 'madvertise', - sizes: [[728, 90], [300, 100]], - bidId: '51ef8751f9aead', - adUnitCode: 'div-gpt-ad-1460505748561-0', - transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - auctionId: '18fd8b8b0bd757', - bidderRequestId: '418b37f85e772c', - params: { - s: 'test', + describe('Test build request', function () { + beforeEach(function () { + let mockConfig = { + consentManagement: { + cmpApi: 'IAB', + timeout: 1111, + allowAuctionWithoutConsent: 'cancel' } - }]; - const req = spec.buildRequests(bid); + }; + + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + afterEach(function () { + config.getConfig.restore(); + }); + let bid = [{ + bidder: 'madvertise', + sizes: [[728, 90], [300, 100]], + bidId: '51ef8751f9aead', + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + params: { + s: 'test', + } + }]; + it('minimum request with gdpr consent', function () { + let bidderRequest = { + gdprConsent: { + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + vendorData: {}, + gdprApplies: true + } + }; + const req = spec.buildRequests(bid, bidderRequest); + + expect(req).to.exist.and.to.be.a('array'); + expect(req[0]).to.have.property('method'); + expect(req[0].method).to.equal('GET'); + expect(req[0]).to.have.property('url'); + expect(req[0].url).to.contain('//mobile.mng-ads.com/?rt=bid_request&v=1.0'); + expect(req[0].url).to.contain(`&s=test`); + expect(req[0].url).to.contain(`&sizes[0]=728x90`); + expect(req[0].url).to.contain(`&gdpr=1`); + expect(req[0].url).to.contain(`&consent[0][format]=IAB`); + expect(req[0].url).to.contain(`&consent[0][value]=BOJ/P2HOJ/P2HABABMAAAAAZ+A==`) + }); + + it('minimum request without gdpr consent', function () { + let bidderRequest = {}; + const req = spec.buildRequests(bid, bidderRequest); expect(req).to.exist.and.to.be.a('array'); expect(req[0]).to.have.property('method'); expect(req[0].method).to.equal('GET'); expect(req[0]).to.have.property('url'); - expect(req[0].url).to.contain('//mobile.mng-ads.com/?rt=bid_request&v=1.0').and.to.contain(`&s=test`).and.to.contain(`&sizes[0]=728x90`) + expect(req[0].url).to.contain('//mobile.mng-ads.com/?rt=bid_request&v=1.0'); + expect(req[0].url).to.contain(`&s=test`); + expect(req[0].url).to.contain(`&sizes[0]=728x90`); + expect(req[0].url).not.to.contain(`&gdpr=1`); + expect(req[0].url).not.to.contain(`&consent[0][format]=`); + expect(req[0].url).not.to.contain(`&consent[0][value]=`) }); }); - describe('Test interpret response', () => { - it('General banner response', () => { + describe('Test interpret response', function () { + it('General banner response', function () { let bid = { bidder: 'madvertise', sizes: [[728, 90]], @@ -134,7 +180,7 @@ describe('madvertise adapater', () => { expect(resp[0]).to.have.property('currency', 'EUR'); expect(resp[0]).to.have.property('dealId', 'DEAL_ID'); }); - it('No response', () => { + it('No response', function () { let bid = { bidder: 'madvertise', sizes: [[728, 90]], diff --git a/test/spec/modules/mantisBidAdapter_spec.js b/test/spec/modules/mantisBidAdapter_spec.js index e2cd4df9a07..df2c6bb9a13 100644 --- a/test/spec/modules/mantisBidAdapter_spec.js +++ b/test/spec/modules/mantisBidAdapter_spec.js @@ -2,10 +2,10 @@ import {expect} from 'chai'; import {spec} from 'modules/mantisBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; -describe('MantisAdapter', () => { +describe('MantisAdapter', function () { const adapter = newBidder(spec); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'mantis', 'params': { @@ -19,11 +19,11 @@ describe('MantisAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = {}; @@ -31,7 +31,7 @@ describe('MantisAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': 'mantis', @@ -47,7 +47,7 @@ describe('MantisAdapter', () => { } ]; - it('domain override', () => { + it('domain override', function () { window.mantis_domain = 'http://foo'; const request = spec.buildRequests(bidRequests); @@ -56,7 +56,7 @@ describe('MantisAdapter', () => { delete window.mantis_domain; }); - it('standard request', () => { + it('standard request', function () { const request = spec.buildRequests(bidRequests); expect(request.url).to.include('property=10433394'); @@ -68,7 +68,7 @@ describe('MantisAdapter', () => { expect(request.url).to.include('bids[0][sizes][1][height]=600'); }); - it('use window uuid', () => { + it('use window uuid', function () { window.mantis_uuid = 'foo'; const request = spec.buildRequests(bidRequests); @@ -78,7 +78,7 @@ describe('MantisAdapter', () => { delete window.mantis_uuid; }); - it('use storage uuid', () => { + it('use storage uuid', function () { window.localStorage.setItem('mantis:uuid', 'bar'); const request = spec.buildRequests(bidRequests); @@ -88,7 +88,7 @@ describe('MantisAdapter', () => { window.localStorage.removeItem('mantis:uuid'); }); - it('detect amp', () => { + it('detect amp', function () { var oldContext = window.context; window.context = {}; @@ -107,8 +107,8 @@ describe('MantisAdapter', () => { }); }); - describe('getUserSyncs', () => { - it('iframe', () => { + describe('getUserSyncs', function () { + it('iframe', function () { let result = spec.getUserSyncs({ iframeEnabled: true }); @@ -117,7 +117,7 @@ describe('MantisAdapter', () => { expect(result[0].url).to.include('https://mantodea.mantisadnetwork.com/prebid/iframe'); }); - it('pixel', () => { + it('pixel', function () { let result = spec.getUserSyncs({ pixelEnabled: true }); @@ -127,10 +127,86 @@ describe('MantisAdapter', () => { }); }); - describe('interpretResponse', () => { - it('display ads returned', () => { + describe('interpretResponse', function () { + it('use ad ttl if provided', function () { let response = { body: { + ttl: 360, + uuid: 'uuid', + ads: [ + { + bid: 'bid', + cpm: 1, + view: 'view', + width: 300, + ttl: 250, + height: 250, + html: '' + } + ] + } + }; + + let expectedResponse = [ + { + requestId: 'bid', + cpm: 1, + width: 300, + height: 250, + ttl: 250, + ad: '', + creativeId: 'view', + netRevenue: true, + currency: 'USD' + } + ]; + let bidderRequest; + + let result = spec.interpretResponse(response, {bidderRequest}); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('use global ttl if provded', function () { + let response = { + body: { + ttl: 360, + uuid: 'uuid', + ads: [ + { + bid: 'bid', + cpm: 1, + view: 'view', + width: 300, + height: 250, + html: '' + } + ] + } + }; + + let expectedResponse = [ + { + requestId: 'bid', + cpm: 1, + width: 300, + height: 250, + ttl: 360, + ad: '', + creativeId: 'view', + netRevenue: true, + currency: 'USD' + } + ]; + let bidderRequest; + + let result = spec.interpretResponse(response, {bidderRequest}); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('display ads returned', function () { + let response = { + body: { + uuid: 'uuid', ads: [ { bid: 'bid', @@ -161,9 +237,11 @@ describe('MantisAdapter', () => { let result = spec.interpretResponse(response, {bidderRequest}); expect(result[0]).to.deep.equal(expectedResponse[0]); + expect(window.mantis_uuid).to.equal(response.body.uuid); + expect(window.localStorage.getItem('mantis:uuid')).to.equal(response.body.uuid); }); - it('no ads returned', () => { + it('no ads returned', function () { let response = { body: { ads: [] diff --git a/test/spec/modules/marsmediaBidAdapter_spec.js b/test/spec/modules/marsmediaBidAdapter_spec.js new file mode 100644 index 00000000000..a58857e0f3f --- /dev/null +++ b/test/spec/modules/marsmediaBidAdapter_spec.js @@ -0,0 +1,122 @@ +import { expect } from 'chai' +import { spec, _getPlatform } from 'modules/marsmediaBidAdapter' +import { newBidder } from 'src/adapters/bidderFactory' + +describe('marsmediaBidAdapter', function () { + const adapter = newBidder(spec) + + let bidRequest = { + 'bidId': '123', + 'sizes': [[ 300, 250 ]], + 'params': { + 'publisherID': 9999, + 'floor': 0.1 + } + } + + describe('codes', function () { + it('should return a bidder code of marsmedia', function () { + expect(spec.code).to.equal('marsmedia') + }) + it('should alias mars', function () { + expect(spec.aliases.length > 0 && spec.aliases[0] === 'mars').to.be.true + }) + }) + + describe('isBidRequestValid', function () { + it('should return true if all params present', function () { + expect(spec.isBidRequestValid(bidRequest)).to.be.true + }) + + it('should return false if any parameter missing', function () { + expect(spec.isBidRequestValid(Object.assign(bidRequest, { params: { publisherID: null } }))).to.be.false + }) + }) + + describe('buildRequests', function () { + let req = spec.buildRequests([ bidRequest ], { refererInfo: { } }) + let rdata + + it('should return request object', function () { + expect(req).to.not.be.null + }) + + it('should build request data', function () { + expect(req.data).to.not.be.null + }) + + it('should include one request', function () { + rdata = JSON.parse(req.data) + expect(rdata.imp.length).to.equal(1) + }) + + it('should include all publisher params', function () { + let r = rdata.imp[0] + expect(r.publisherID !== null).to.be.true + }) + }) + + describe('interpretResponse', function () { + let response; + beforeEach(function () { + response = { + body: { + 'id': '37386aade21a71', + 'seatbid': [{ + 'bid': [{ + 'id': '1', + 'impid': '1', + 'cid': '1', + 'price': 0.1, + 'nurl': '', + 'adm': '', + 'w': 320, + 'h': 250 + }] + }] + } + }; + }); + + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': '37386aade21a71', + 'cpm': 0.1, + 'width': 320, + 'height': 250, + 'creativeId': '1', + 'currency': 'USD', + 'netRevenue': true, + 'ad': ``, + 'ttl': 60 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('handles empty bid response', function () { + let response = { + body: '' + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + /* it('should return iframe sync', function () { + let sync = spec.getUserSyncs({ iframeEnabled: true }) + expect(sync.length).to.equal(1) + expect(sync[0].type === 'iframe') + expect(typeof sync[0].url === 'string') + }) + + it('should return pixel sync', function () { + let sync = spec.getUserSyncs({ pixelEnabled: true }) + expect(sync.length).to.equal(1) + expect(sync[0].type === 'image') + expect(typeof sync[0].url === 'string') + }) */ + }) +}) diff --git a/test/spec/modules/meazyBidAdapter_spec.js b/test/spec/modules/meazyBidAdapter_spec.js new file mode 100644 index 00000000000..9abe37ece62 --- /dev/null +++ b/test/spec/modules/meazyBidAdapter_spec.js @@ -0,0 +1,177 @@ +import * as utils from 'src/utils'; +import { expect } from 'chai'; +import { spec } from 'modules/meazyBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const MEAZY_PID = '6910b7344ae566a1' +const VALID_ENDPOINT = `//rtb-filter.meazy.co/pbjs?host=${utils.getOrigin()}&api_key=${MEAZY_PID}`; + +const bidderRequest = { + refererInfo: { + referer: 'page', + stack: ['page', 'page1'] + } +}; + +const bidRequest = { + bidder: 'meazy', + adUnitCode: 'test-div', + sizes: [[300, 250], [300, 600]], + params: { + pid: MEAZY_PID + }, + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', +}; + +const bidContent = { + 'id': '30b31c1838de1e', + 'bidid': '9780a52ff05c0e92780f5baf9cf3f4e8', + 'cur': 'USD', + 'seatbid': [{ + 'bid': [{ + 'id': 'ccf05fb8effb3d02', + 'impid': 'B19C34BBD69DAF9F', + 'burl': 'https://track.meazy.co/imp?bidid=9780a52ff05c0e92780f5baf9cf3f4e8&user=fdc401a2-92f1-42bd-ac22-d570520ad0ec&burl=1&ssp=5&project=2&cost=${AUCTION_PRICE}', + 'adm': '', + 'adid': 'ad-2.6.75.300x250', + 'price': 1.5, + 'w': 300, + 'h': 250, + 'cid': '2.6.75', + 'crid': '2.6.75.300x250', + 'dealid': 'default' + }], + 'seat': '2' + }] +}; + +const bidContentExt = { + ...bidContent, + ext: { + 'syncUrl': '//sync.meazy.co/sync/img?api_key=6910b7344ae566a1' + } +}; + +const bidResponse = { + body: bidContent +}; + +const noBidResponse = { body: {'nbr': 2} }; + +describe('meazyBidAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return false', function () { + let bid = Object.assign({}, bidRequest); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true', function () { + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + it('should format valid url', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + expect(request.url).to.equal(VALID_ENDPOINT); + }); + + it('should format valid url', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + expect(request.url).to.equal(VALID_ENDPOINT); + }); + + it('should format valid request body', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.id).to.exist; + expect(payload.imp).to.exist; + expect(payload.imp[0]).to.exist; + expect(payload.imp[0].banner).to.exist; + expect(payload.imp[0].banner.format).to.exist; + expect(payload.device).to.exist; + expect(payload.site).to.exist; + expect(payload.site.domain).to.exist; + expect(payload.cur).to.exist; + }); + + it('should format valid url', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + expect(request.url).to.equal(VALID_ENDPOINT); + }); + + it('should not fill user.ext object', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.user.ext).to.equal(undefined); + }); + + it('should fill user.ext object', function () { + const consentString = 'hellogdpr'; + const request = spec.buildRequests([bidRequest], { ...bidderRequest, gdprConsent: { gdprApplies: true, consentString } }); + const payload = JSON.parse(request.data); + expect(payload.user.ext).to.exist.and.to.be.a('object'); + expect(payload.user.ext.consent).to.equal(consentString); + expect(payload.user.ext.gdpr).to.equal(1); + }); + }); + + describe('interpretResponse', function () { + it('should get correct bid response', function () { + const result = spec.interpretResponse(bidResponse); + const validResponse = [{ + requestId: '30b31c1838de1e', + cpm: 1.5, + width: 300, + height: 250, + creativeId: '2.6.75.300x250', + netRevenue: true, + dealId: 'default', + currency: 'USD', + ttl: 900, + ad: '' + }]; + + expect(result).to.deep.equal(validResponse); + }); + + it('handles nobid responses', function () { + let result = spec.interpretResponse(noBidResponse); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + const syncOptionsFF = { iframeEnabled: false }; + const syncOptionsEF = { iframeEnabled: true }; + const syncOptionsEE = { pixelEnabled: true, iframeEnabled: true }; + const syncOptionsFE = { pixelEnabled: true, iframeEnabled: false }; + + const successIFrame = { type: 'iframe', url: '//sync.meazy.co/sync/iframe' }; + const successPixel = { type: 'image', url: '//sync.meazy.co/sync/img?api_key=6910b7344ae566a1' }; + + it('should return an empty array', function () { + expect(spec.getUserSyncs(syncOptionsFF, [])).to.be.empty; + expect(spec.getUserSyncs(syncOptionsFF, [ bidResponse ])).to.be.empty; + expect(spec.getUserSyncs(syncOptionsFE, [ bidResponse ])).to.be.empty; + }); + + it('should be equal to the expected result', function () { + expect(spec.getUserSyncs(syncOptionsEF, [ bidResponse ])).to.deep.equal([successIFrame]); + expect(spec.getUserSyncs(syncOptionsFE, [ { body: bidContentExt } ])).to.deep.equal([successPixel]); + expect(spec.getUserSyncs(syncOptionsEE, [ { body: bidContentExt } ])).to.deep.equal([successPixel, successIFrame]); + expect(spec.getUserSyncs(syncOptionsEE, [])).to.deep.equal([successIFrame]); + }) + }); +}); diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index a10dcb2624d..336a86a7793 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -17,7 +17,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 250]], 'bidId': '28f8f8130a583e', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }, { 'bidder': 'medianet', 'params': { @@ -33,7 +34,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 251]], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }], VALID_BID_REQUEST_INVALID_BIDFLOOR = [{ 'bidder': 'medianet', @@ -51,7 +53,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 250]], 'bidId': '28f8f8130a583e', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }, { 'bidder': 'medianet', 'params': { @@ -67,7 +70,103 @@ let VALID_BID_REQUEST = [{ 'sizes': [[300, 251]], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 + }], + VALID_NATIVE_BID_REQUEST = [{ + 'bidder': 'medianet', + 'params': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + 'sizes': [[300, 250]], + 'bidId': '28f8f8130a583e', + 'bidderRequestId': '1e9b1f07797c1c', + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1, + 'nativeParams': { + 'image': { + 'required': true, + 'sizes': [ + 150, + 50 + ], + 'wmin': 50 + }, + 'title': { + 'required': true, + 'len': 80 + }, + 'sponsoredBy': { + 'required': true + }, + 'clickUrl': { + 'required': true + }, + 'body': { + 'required': true + }, + 'icon': { + 'required': true, + 'sizes': [ + 50, + 50 + ] + } + } + }, { + 'bidder': 'medianet', + 'params': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-123', + 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + 'sizes': [[300, 251]], + 'bidId': '3f97ca71b1e5c2', + 'bidderRequestId': '1e9b1f07797c1c', + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1, + 'nativeParams': { + 'image': { + 'required': true, + 'sizes': [ + 150, + 50 + ], + 'wmin': 50 + }, + 'title': { + 'required': true, + 'len': 80 + }, + 'sponsoredBy': { + 'required': true + }, + 'clickUrl': { + 'required': true + }, + 'body': { + 'required': true + }, + 'icon': { + 'required': true, + 'sizes': [ + 50, + 50 + ] + } + } }], VALID_AUCTIONDATA = { 'timeout': config.getConfig('bidderTimeout'), @@ -82,12 +181,29 @@ let VALID_BID_REQUEST = [{ 'customer_id': 'customer_id', 'prebid_version': $$PREBID_GLOBAL$$.version, 'gdpr_applies': false, + 'screen': { + 'w': 1000, + 'h': 1000 + } }, 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-0' + 'dfp_id': 'div-gpt-ad-1460505748561-0', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -105,7 +221,20 @@ let VALID_BID_REQUEST = [{ }, { 'id': '3f97ca71b1e5c2', 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-123' + 'dfp_id': 'div-gpt-ad-1460505748561-123', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -122,6 +251,87 @@ let VALID_BID_REQUEST = [{ }], 'tmax': config.getConfig('bidderTimeout') }, + VALID_PAYLOAD_NATIVE = { + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + }, + 'ext': { + 'customer_id': 'customer_id', + 'prebid_version': $$PREBID_GLOBAL$$.version, + 'gdpr_applies': false, + 'screen': { + 'w': 1000, + 'h': 1000 + } + }, + 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'imp': [{ + 'id': '28f8f8130a583e', + 'ext': { + 'dfp_id': 'div-gpt-ad-1460505748561-0', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 + }, + 'banner': [{ + 'w': 300, + 'h': 250 + }], + 'native': '{\"image\":{\"required\":true,\"sizes\":[150,50],\"wmin\":50},\"title\":{\"required\":true,\"len\":80},\"sponsoredBy\":{\"required\":true},\"clickUrl\":{\"required\":true},\"body\":{\"required\":true},\"icon\":{\"required\":true,\"sizes\":[50,50]}}', + 'all': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + } + }, { + 'id': '3f97ca71b1e5c2', + 'ext': { + 'dfp_id': 'div-gpt-ad-1460505748561-123', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 + }, + 'banner': [{ + 'w': 300, + 'h': 251 + }], + 'native': '{\"image\":{\"required\":true,\"sizes\":[150,50],\"wmin\":50},\"title\":{\"required\":true,\"len\":80},\"sponsoredBy\":{\"required\":true},\"clickUrl\":{\"required\":true},\"body\":{\"required\":true},\"icon\":{\"required\":true,\"sizes\":[50,50]}}', + 'all': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest' + } + } + }], + 'tmax': config.getConfig('bidderTimeout') + }, VALID_PAYLOAD = { 'site': { 'page': 'http://media.net/prebidtest', @@ -131,13 +341,30 @@ let VALID_BID_REQUEST = [{ 'ext': { 'customer_id': 'customer_id', 'prebid_version': $$PREBID_GLOBAL$$.version, - 'gdpr_applies': false + 'gdpr_applies': false, + 'screen': { + 'w': 1000, + 'h': 1000 + } }, 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-0' + 'dfp_id': 'div-gpt-ad-1460505748561-0', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -154,7 +381,20 @@ let VALID_BID_REQUEST = [{ }, { 'id': '3f97ca71b1e5c2', 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-123' + 'dfp_id': 'div-gpt-ad-1460505748561-123', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -316,7 +556,8 @@ let VALID_BID_REQUEST = [{ 'sizes': [300, 250], 'bidId': '28f8f8130a583e', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }, { 'bidder': 'medianet', 'params': { @@ -332,14 +573,15 @@ let VALID_BID_REQUEST = [{ 'sizes': [300, 251], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', - 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d' + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 }], VALID_BIDDER_REQUEST_WITH_GDPR = { 'gdprConsent': { 'consentString': 'consentString', 'gdprApplies': true, }, - 'timeout': 3000, + 'timeout': 3000 }, VALID_PAYLOAD_FOR_GDPR = { 'site': { @@ -352,12 +594,29 @@ let VALID_BID_REQUEST = [{ 'prebid_version': $$PREBID_GLOBAL$$.version, 'gdpr_consent_string': 'consentString', 'gdpr_applies': true, + 'screen': { + 'w': 1000, + 'h': 1000 + } }, 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', 'imp': [{ 'id': '28f8f8130a583e', 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-0' + 'dfp_id': 'div-gpt-ad-1460505748561-0', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -374,7 +633,20 @@ let VALID_BID_REQUEST = [{ }, { 'id': '3f97ca71b1e5c2', 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-123' + 'dfp_id': 'div-gpt-ad-1460505748561-123', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 }, 'banner': [{ 'w': 300, @@ -392,53 +664,90 @@ let VALID_BID_REQUEST = [{ 'tmax': 3000, }; -describe('Media.net bid adapter', () => { - describe('isBidRequestValid', () => { - it('should accept valid bid params', () => { +describe('Media.net bid adapter', function () { + let sandbox; + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('isBidRequestValid', function () { + it('should accept valid bid params', function () { let isValid = spec.isBidRequestValid(VALID_PARAMS); expect(isValid).to.equal(true); }); - it('should reject bid if cid is not present', () => { + it('should reject bid if cid is not present', function () { let isValid = spec.isBidRequestValid(PARAMS_WITHOUT_CID); expect(isValid).to.equal(false); }); - it('should reject bid if cid is not a string', () => { + it('should reject bid if cid is not a string', function () { let isValid = spec.isBidRequestValid(PARAMS_WITH_INTEGER_CID); expect(isValid).to.equal(false); }); - it('should reject bid if cid is a empty string', () => { + it('should reject bid if cid is a empty string', function () { let isValid = spec.isBidRequestValid(PARAMS_WITH_EMPTY_CID); expect(isValid).to.equal(false); }); }); - describe('buildRequests', () => { - it('should build valid payload on bid', () => { + describe('buildRequests', function () { + beforeEach(function () { + let documentStub = sandbox.stub(document, 'getElementById'); + let boundingRect = { + top: 50, + left: 50, + bottom: 100, + right: 100 + }; + documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ + getBoundingClientRect: () => boundingRect + }); + documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + getBoundingClientRect: () => boundingRect + }); + let windowSizeStub = sandbox.stub(spec, 'getWindowSize'); + windowSizeStub.returns({ + w: 1000, + h: 1000 + }); + }); + + it('should build valid payload on bid', function () { let requestObj = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); expect(JSON.parse(requestObj.data)).to.deep.equal(VALID_PAYLOAD); }); - it('should accept size as a one dimensional array', () => { + it('should accept size as a one dimensional array', function () { let bidReq = spec.buildRequests(BID_REQUEST_SIZE_AS_1DARRAY, VALID_AUCTIONDATA); expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD); }); - it('should ignore bidfloor if not a valid number', () => { + it('should ignore bidfloor if not a valid number', function () { let bidReq = spec.buildRequests(VALID_BID_REQUEST_INVALID_BIDFLOOR, VALID_AUCTIONDATA); expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_INVALID_BIDFLOOR); }); - it('should add gdpr to response ext', () => { + it('should add gdpr to response ext', function () { let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_BIDDER_REQUEST_WITH_GDPR); expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_FOR_GDPR); }); + it('should parse params for native request', function () { + let bidReq = spec.buildRequests(VALID_NATIVE_BID_REQUEST, VALID_AUCTIONDATA); + expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_NATIVE); + }); + describe('build requests: when page meta-data is available', () => { + beforeEach(() => { + spec.clearMnData(); + }); it('should pass canonical, twitter and fb paramters if available', () => { - let sandbox = sinon.sandbox.create(); let documentStub = sandbox.stub(window.top.document, 'querySelector'); documentStub.withArgs('link[rel="canonical"]').returns({ href: 'http://localhost:9999/canonical-test' @@ -451,45 +760,117 @@ describe('Media.net bid adapter', () => { }); let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_PAGE_META); - sandbox.restore(); }); }); }); - describe('getUserSyncs', () => { - it('should exclude iframe syncs if iframe is disabled', () => { + describe('slot visibility', function () { + let documentStub; + beforeEach(function () { + let windowSizeStub = sandbox.stub(spec, 'getWindowSize'); + windowSizeStub.returns({ + w: 1000, + h: 1000 + }); + documentStub = sandbox.stub(document, 'getElementById'); + }); + it('slot visibility should be 2 and ratio 0 when ad unit is BTF', function () { + let boundingRect = { + top: 1010, + left: 1010, + bottom: 1050, + right: 1050 + }; + documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ + getBoundingClientRect: () => boundingRect + }); + documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + getBoundingClientRect: () => boundingRect + }); + + let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); + let data = JSON.parse(bidReq.data); + expect(data.imp[0].ext.visibility).to.equal(2); + expect(data.imp[0].ext.viewability).to.equal(0); + }); + it('slot visibility should be 2 and ratio < 0.5 when ad unit is partially inside viewport', function () { + let boundingRect = { + top: 990, + left: 990, + bottom: 1050, + right: 1050 + }; + documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ + getBoundingClientRect: () => boundingRect + }); + documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + getBoundingClientRect: () => boundingRect + }); + let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); + let data = JSON.parse(bidReq.data); + expect(data.imp[0].ext.visibility).to.equal(2); + expect(data.imp[0].ext.viewability).to.equal(100 / 75000); + }); + it('slot visibility should be 1 and ratio > 0.5 when ad unit mostly in viewport', function () { + let boundingRect = { + top: 800, + left: 800, + bottom: 1050, + right: 1050 + }; + documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ + getBoundingClientRect: () => boundingRect + }); + documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + getBoundingClientRect: () => boundingRect + }); + let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); + let data = JSON.parse(bidReq.data); + expect(data.imp[0].ext.visibility).to.equal(1); + expect(data.imp[0].ext.viewability).to.equal(40000 / 75000); + }); + it('co-ordinates should not be sent and slot visibility should be 0 when ad unit is not present', function () { + let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); + let data = JSON.parse(bidReq.data); + expect(data.imp[1].ext).to.not.have.ownPropertyDescriptor('viewability'); + expect(data.imp[1].ext.visibility).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + it('should exclude iframe syncs if iframe is disabled', function () { let userSyncs = spec.getUserSyncs(SYNC_OPTIONS_PIXEL_ENABLED, SERVER_CSYNC_RESPONSE); expect(userSyncs).to.deep.equal(ENABLED_SYNC_PIXEL); }); - it('should exclude pixel syncs if pixel is disabled', () => { + it('should exclude pixel syncs if pixel is disabled', function () { let userSyncs = spec.getUserSyncs(SYNC_OPTIONS_IFRAME_ENABLED, SERVER_CSYNC_RESPONSE); expect(userSyncs).to.deep.equal(ENABLED_SYNC_IFRAME); }); - it('should choose iframe sync urls if both sync options are enabled', () => { + it('should choose iframe sync urls if both sync options are enabled', function () { let userSyncs = spec.getUserSyncs(SYNC_OPTIONS_BOTH_ENABLED, SERVER_CSYNC_RESPONSE); expect(userSyncs).to.deep.equal(ENABLED_SYNC_IFRAME); }); }); - describe('interpretResponse', () => { - it('should not push bid response if cpm missing', () => { + describe('interpretResponse', function () { + it('should not push bid response if cpm missing', function () { let validBids = []; let bids = spec.interpretResponse(SERVER_RESPONSE_CPM_MISSING, []); expect(bids).to.deep.equal(validBids); }); - it('should not push bid response if cpm 0', () => { + it('should not push bid response if cpm 0', function () { let validBids = []; let bids = spec.interpretResponse(SERVER_RESPONSE_CPM_ZERO, []); expect(bids).to.deep.equal(validBids); }); - it('should not push response if no-bid', () => { + it('should not push response if no-bid', function () { let validBids = []; let bids = spec.interpretResponse(SERVER_RESPONSE_NOBID, []); - expect(bids).to.deep.equal(validBids) + expect(bids).to.deep.equal(validBids); }); }); }); diff --git a/test/spec/modules/mgidBidAdapter_spec.js b/test/spec/modules/mgidBidAdapter_spec.js new file mode 100644 index 00000000000..4a9f25a29a7 --- /dev/null +++ b/test/spec/modules/mgidBidAdapter_spec.js @@ -0,0 +1,697 @@ +import {assert, expect} from 'chai'; +import {spec} from 'modules/mgidBidAdapter'; +import * as utils from '../../../src/utils'; +import * as urlUtils from '../../../src/url'; + +describe('Mgid bid adapter', function () { + let sandbox; + let logErrorSpy; + let logWarnSpy; + beforeEach(function () { + sandbox = sinon.sandbox.create(); + logErrorSpy = sinon.spy(utils, 'logError'); + logWarnSpy = sinon.spy(utils, 'logWarn'); + }); + + afterEach(function () { + sandbox.restore(); + utils.logError.restore(); + utils.logWarn.restore(); + }); + const ua = navigator.userAgent; + const screenHeight = screen.height; + const screenWidth = screen.width; + const dnt = (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0; + const language = navigator.language ? 'language' : 'userLanguage'; + let lang = navigator[language].split('-')[0]; + if (lang.length != 2 && lang.length != 3) { + lang = ''; + } + const secure = window.location.protocol === 'https:' ? 1 : 0; + const mgid_ver = spec.VERSION; + const prebid_ver = $$PREBID_GLOBAL$$.version; + const utcOffset = (new Date()).getTimezoneOffset().toString(); + + describe('isBidRequestValid', function () { + let bid = { + 'adUnitCode': 'div', + 'bidder': 'mgid', + 'params': { + 'property': '10433394', + 'zone': 'zone' + }, + }; + + it('should not accept bid without required params', function () { + let isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }); + + it('should return false when params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when valid params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '', placementId: ''}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when valid params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.adUnitCode = ''; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + bid.params = {accountId: 2, placementId: 1}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when adUnitCode not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.adUnitCode = ''; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + bid.params = {accountId: 2, placementId: 1}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when valid params are passed as nums', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.adUnitCode = 'div'; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + bid.params = {accountId: 2, placementId: 1}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when valid params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.mediaTypes = { + native: { + sizes: [[300, 250]] + } + }; + bid.params = {accountId: '0', placementId: '00'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when valid mediaTypes are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when valid mediaTypes.banner are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when valid mediaTypes.banner.sizes are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + sizes: [] + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when valid mediaTypes.banner.sizes are not valid', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + sizes: [300, 250] + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when valid params are passed as strings', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.adUnitCode = 'div'; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when valid mediaTypes.native is not object', function () { + let bid = Object.assign({}, bid); + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + native: [] + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaTypes.native is empty object', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + native: {} + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaTypes.native is invalid object', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + native: { + image: { + sizes: [80, 80] + }, + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaTypes.native has unsupported required asset', function () { + let bid = Object.assign({}, bid); + bid.params = {accountId: '2', placementId: '1'}; + bid.mediaTypes = { + native: { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + }, + }; + bid.nativeParams = { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + unsupported: {required: true}, + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when mediaTypes.native all assets needed', function () { + let bid = Object.assign({}, bid); + bid.adUnitCode = 'div'; + bid.params = {accountId: '2', placementId: '1'}; + bid.mediaTypes = { + native: { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + }, + }; + bid.nativeParams = { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('override defaults', function () { + let bid = { + bidder: 'mgid', + params: { + accountId: '1', + placementId: '2', + }, + }; + it('should return object', function () { + let bid = Object.assign({}, bid); + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request).to.exist.and.to.be.a('object'); + }); + + it('should return overwrite default bidurl', function () { + let bid = Object.assign({}, bid); + bid.params = { + bidUrl: '//newbidurl.com/', + accountId: '1', + placementId: '2', + }; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request.url).to.include('//newbidurl.com/1'); + }); + it('should return overwrite default bidFloor', function () { + let bid = Object.assign({}, bid); + bid.params = { + bidFloor: 1.1, + accountId: '1', + placementId: '2', + }; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request.data).to.be.a('string'); + const data = JSON.parse(request.data); + expect(data).to.be.a('object'); + expect(data.imp).to.be.a('array'); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor).to.deep.equal(1.1); + }); + it('should return overwrite default currency', function () { + let bid = Object.assign({}, bid); + bid.params = { + cur: 'GBP', + accountId: '1', + placementId: '2', + }; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request.data).to.be.a('string'); + const data = JSON.parse(request.data); + expect(data).to.be.a('object'); + expect(data.cur).to.deep.equal(['GBP']); + }); + }); + + describe('buildRequests', function () { + it('should return undefined if no validBidRequests passed', function () { + expect(spec.buildRequests([])).to.be.undefined; + }); + + let abid = { + adUnitCode: 'div', + bidder: 'mgid', + params: { + accountId: '1', + placementId: '2', + }, + }; + it('should return proper request url', function () { + localStorage.setItem('mgMuidn', 'xxx'); + let bid = Object.assign({}, abid); + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1?muid=xxx'); + localStorage.removeItem('mgMuidn') + }); + it('should proper handle gdpr', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'gdpr', gdprApplies: true}}); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.user).deep.equal({ext: {consent: 'gdpr'}}); + expect(data.regs).deep.equal({ext: {gdpr: 1}}); + }); + it('should return proper banner imp', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const page = top.location.href; + const domain = urlUtils.parse(page).hostname; + const request = spec.buildRequests(bidRequests); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.site.domain).to.deep.equal(domain); + expect(data.site.page).to.deep.equal(page); + expect(data.cur).to.deep.equal(['USD']); + expect(data.device.ua).to.deep.equal(ua); + expect(data.device.dnt).equal(dnt); + expect(data.device.h).equal(screenHeight); + expect(data.device.w).equal(screenWidth); + expect(data.device.language).to.deep.equal(lang); + expect(data.imp[0].tagid).to.deep.equal('2/div'); + expect(data.imp[0].banner).to.deep.equal({w: 300, h: 250}); + expect(data.imp[0].secure).to.deep.equal(secure); + expect(request).to.deep.equal({ + 'method': 'POST', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"banner\":{\"w\":300,\"h\":250}}]}', + }); + }); + it('should not return native imp if minimum asset list not requested', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + native: '', + }; + bid.nativeParams = { + title: {required: true}, + image: {sizes: [80, 80]}, + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.undefined; + }); + it('should return proper native imp', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + native: '', + }; + bid.nativeParams = { + title: {required: true}, + image: {sizes: [80, 80]}, + sponsored: { }, + }; + + let bidRequests = [bid]; + const page = top.location.href; + const domain = urlUtils.parse(page).hostname; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.a('object'); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.site.domain).to.deep.equal(domain); + expect(data.site.page).to.deep.equal(page); + expect(data.cur).to.deep.equal(['USD']); + expect(data.device.ua).to.deep.equal(ua); + expect(data.device.dnt).equal(dnt); + expect(data.device.h).equal(screenHeight); + expect(data.device.w).equal(screenWidth); + expect(data.device.language).to.deep.equal(lang); + expect(data.imp[0].tagid).to.deep.equal('2/div'); + expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 80, 'type': 3, 'w': 80}, 'required': 0}, {'data': {'type': 1}, 'id': 11, 'required': 0}], 'plcmtcnt': 1}}); + expect(data.imp[0].secure).to.deep.equal(secure); + expect(request).to.deep.equal({ + 'method': 'POST', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":0,\"img\":{\"type\":3,\"w\":80,\"h\":80}},{\"id\":11,\"required\":0,\"data\":{\"type\":1}}]}}}]}', + }); + }); + it('should return proper native imp', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + native: '', + }; + bid.nativeParams = { + title: {required: true}, + image: {wmin: 50, hmin: 50, required: true}, + icon: {}, + sponsored: { }, + }; + + let bidRequests = [bid]; + const page = top.location.href; + const domain = urlUtils.parse(page).hostname; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.a('object'); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.site.domain).to.deep.equal(domain); + expect(data.site.page).to.deep.equal(page); + expect(data.cur).to.deep.equal(['USD']); + expect(data.device.ua).to.deep.equal(ua); + expect(data.device.dnt).equal(dnt); + expect(data.device.h).equal(screenHeight); + expect(data.device.w).equal(screenWidth); + expect(data.device.language).to.deep.equal(lang); + expect(data.imp[0].tagid).to.deep.equal('2/div'); + expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 328, hmin: 50, 'type': 3, 'w': 492, wmin: 50}, 'required': 1}, {'id': 3, 'img': {'h': 50, 'type': 1, 'w': 50}, 'required': 0}, {'data': {'type': 1}, 'id': 11, 'required': 0}], 'plcmtcnt': 1}}); + expect(data.imp[0].secure).to.deep.equal(secure); + expect(request).to.deep.equal({ + 'method': 'POST', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"w\":492,\"h\":328,\"wmin\":50,\"hmin\":50}},{\"id\":3,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":11,\"required\":0,\"data\":{\"type\":1}}]}}}]}', + }); + }); + it('should return proper native imp with sponsoredBy', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + native: '', + }; + bid.nativeParams = { + title: {required: true}, + image: {sizes: [80, 80]}, + sponsoredBy: { }, + }; + + let bidRequests = [bid]; + const page = top.location.href; + const domain = urlUtils.parse(page).hostname; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.a('object'); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.site.domain).to.deep.equal(domain); + expect(data.site.page).to.deep.equal(page); + expect(data.cur).to.deep.equal(['USD']); + expect(data.device.ua).to.deep.equal(ua); + expect(data.device.dnt).equal(dnt); + expect(data.device.h).equal(screenHeight); + expect(data.device.w).equal(screenWidth); + expect(data.device.language).to.deep.equal(lang); + expect(data.imp[0].tagid).to.deep.equal('2/div'); + expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 80, 'type': 3, 'w': 80}, 'required': 0}, {'data': {'type': 1}, 'id': 4, 'required': 0}], 'plcmtcnt': 1}}); + expect(data.imp[0].secure).to.deep.equal(secure); + expect(request).to.deep.equal({ + 'method': 'POST', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":0,\"img\":{\"type\":3,\"w\":80,\"h\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":1}}]}}}]}', + }); + }); + it('should return proper banner request', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + banner: { + sizes: [[300, 600], [300, 250]], + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + + const page = top.location.href; + const domain = urlUtils.parse(page).hostname; + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.site.domain).to.deep.equal(domain); + expect(data.site.page).to.deep.equal(page); + expect(data.cur).to.deep.equal(['USD']); + expect(data.device.ua).to.deep.equal(ua); + expect(data.device.dnt).equal(dnt); + expect(data.device.h).equal(screenHeight); + expect(data.device.w).equal(screenWidth); + expect(data.device.language).to.deep.equal(lang); + expect(data.imp[0].tagid).to.deep.equal('2/div'); + expect(data.imp[0].banner).to.deep.equal({w: 300, h: 600, format: [{w: 300, h: 600}, {w: 300, h: 250}]}); + expect(data.imp[0].secure).to.deep.equal(secure); + + expect(request).to.deep.equal({ + 'method': 'POST', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"banner\":{\"w\":300,\"h\":600,\"format\":[{\"w\":300,\"h\":600},{\"w\":300,\"h\":250}]}}]}', + }); + }); + }); + describe('interpretResponse banner', function () { + it('should not push bid response', function () { + let bids = spec.interpretResponse(); + expect(bids).to.be.undefined; + }); + it('should push proper banner bid response', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': '', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': 'html: adm', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2']}], 'seat': '44082'}]} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([ + { + 'ad': 'html: adm', + 'cpm': 1.5, + 'creativeId': '2898532/2419121/2592854/2499195', + 'currency': 'USD', + 'dealId': '', + 'height': 600, + 'isBurl': true, + 'mediaType': 'banner', + 'netRevenue': true, + 'nurl': 'http: nurl', + 'burl': 'http: burl', + 'requestId': '61e40632c53fc2', + 'ttl': 300, + 'width': 300, + } + ]); + }); + }); + describe('interpretResponse native', function () { + it('should not push proper native bid response if adm is missing', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([]) + }); + it('should not push proper native bid response if assets is empty', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([]) + }); + it('should push proper native bid response', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}},{\"id\":4,\"required\":0,\"data\":{\"type\":4,\"value\":\"sponsored\"}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"value\":\"price1\"}},{\"id\":6,\"required\":0,\"data\":{\"type\":7,\"value\":\"price2\"}}],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}], ext: {'muidn': 'userid'}} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([{ + 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}},{\"id\":4,\"required\":0,\"data\":{\"type\":4,\"value\":\"sponsored\"}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"value\":\"price1\"}},{\"id\":6,\"required\":0,\"data\":{\"type\":7,\"value\":\"price2\"}}],\"imptrackers\":[\"imptrackers1\"]}}', + 'burl': 'http: burl', + 'cpm': 1.5, + 'creativeId': '2898532/2419121/2592854/2499195', + 'currency': 'GBP', + 'dealId': '', + 'height': 0, + 'isBurl': true, + 'mediaType': 'native', + 'native': { + 'clickTrackers': [], + 'clickUrl': 'link_url', + 'data': 'price1', + 'icon': { + 'height': 50, + 'url': 'icon_src', + 'width': 50 + }, + 'image': { + 'height': 80, + 'url': 'image_src', + 'width': 80 + }, + 'impressionTrackers': [ + 'imptrackers1' + ], + 'jstracker': [], + 'sponsoredBy': 'sponsored', + 'title': 'title1' + }, + 'netRevenue': true, + 'nurl': 'http: nurl', + 'requestId': '61e40632c53fc2', + 'ttl': 300, + 'width': 0 + }]) + }); + it('should push proper native bid response', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}}],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([ + { + 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}}],\"imptrackers\":[\"imptrackers1\"]}}', + 'cpm': 1.5, + 'creativeId': '2898532/2419121/2592854/2499195', + 'currency': 'GBP', + 'dealId': '', + 'height': 0, + 'isBurl': true, + 'mediaType': 'native', + 'netRevenue': true, + 'nurl': 'http: nurl', + 'burl': 'http: burl', + 'requestId': '61e40632c53fc2', + 'ttl': 300, + 'width': 0, + 'native': { + clickTrackers: [], + title: 'title1', + image: { + url: 'image_src', + width: 80, + height: 80, + }, + icon: { + url: 'icon_src', + width: 50, + height: 50, + }, + impressionTrackers: ['imptrackers1'], + jstracker: [], + clickUrl: 'link_url', + } + } + ]); + }); + }); + + describe('getUserSyncs', function () { + it('should do nothing on getUserSyncs', function () { + spec.getUserSyncs() + }); + }); + describe('on bidWon', function () { + it('should replace nurl and burl for native', function () { + const burl = 'burl&s=${' + 'AUCTION_PRICE}'; + const nurl = 'nurl&s=${' + 'AUCTION_PRICE}'; + const bid = {'bidderCode': 'mgid', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '3d0b6ff1dda89', 'requestId': '2a423489e058a1', 'mediaType': 'native', 'source': 'client', 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"LinkURL\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"TITLE\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"ImageURL\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"IconURL\"}},{\"id\":11,\"required\":0,\"data\":{\"type\":1,\"value\":\"sponsored\"}}],\"imptrackers\":[\"ImpTrackerURL\"]}}', 'cpm': 0.66, 'creativeId': '353538_591471', 'currency': 'USD', 'dealId': '', 'netRevenue': true, 'ttl': 300, 'nurl': nurl, 'burl': burl, 'isBurl': true, 'native': {'title': 'TITLE', 'image': {'url': 'ImageURL', 'height': 80, 'width': 80}, 'icon': {'url': 'IconURL', 'height': 50, 'width': 50}, 'sponsored': 'sponsored', 'clickUrl': 'LinkURL', 'clickTrackers': [], 'impressionTrackers': ['ImpTrackerURL'], 'jstracker': []}, 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', 'responseTimestamp': 1556867386065, 'requestTimestamp': 1556867385916, 'bidder': 'mgid', 'adUnitCode': 'div-gpt-ad-1555415275793-0', 'timeToRespond': 149, 'pbLg': '0.50', 'pbMg': '0.60', 'pbHg': '0.66', 'pbAg': '0.65', 'pbDg': '0.66', 'pbCg': '', 'size': '0x0', 'adserverTargeting': {'hb_bidder': 'mgid', 'hb_adid': '3d0b6ff1dda89', 'hb_pb': '0.66', 'hb_size': '0x0', 'hb_source': 'client', 'hb_format': 'native', 'hb_native_title': 'TITLE', 'hb_native_image': 'hb_native_image:3d0b6ff1dda89', 'hb_native_icon': 'IconURL', 'hb_native_linkurl': 'hb_native_linkurl:3d0b6ff1dda89'}, 'status': 'targetingSet', 'params': [{'accountId': '184', 'placementId': '353538'}]}; + spec.onBidWon(bid); + expect(bid.nurl).to.deep.equal('nurl&s=0.66'); + expect(bid.burl).to.deep.equal('burl&s=0.66'); + }); + it('should replace nurl and burl for banner', function () { + const burl = 'burl&s=${' + 'AUCTION_PRICE}'; + const nurl = 'nurl&s=${' + 'AUCTION_PRICE}'; + const bid = {'bidderCode': 'mgid', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '3d0b6ff1dda89', 'requestId': '2a423489e058a1', 'mediaType': 'banner', 'source': 'client', 'ad': burl, 'cpm': 0.66, 'creativeId': '353538_591471', 'currency': 'USD', 'dealId': '', 'netRevenue': true, 'ttl': 300, 'nurl': nurl, 'burl': burl, 'isBurl': true, 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', 'responseTimestamp': 1556867386065, 'requestTimestamp': 1556867385916, 'bidder': 'mgid', 'adUnitCode': 'div-gpt-ad-1555415275793-0', 'timeToRespond': 149, 'pbLg': '0.50', 'pbMg': '0.60', 'pbHg': '0.66', 'pbAg': '0.65', 'pbDg': '0.66', 'pbCg': '', 'size': '0x0', 'adserverTargeting': {'hb_bidder': 'mgid', 'hb_adid': '3d0b6ff1dda89', 'hb_pb': '0.66', 'hb_size': '0x0', 'hb_source': 'client', 'hb_format': 'banner', 'hb_banner_title': 'TITLE', 'hb_banner_image': 'hb_banner_image:3d0b6ff1dda89', 'hb_banner_icon': 'IconURL', 'hb_banner_linkurl': 'hb_banner_linkurl:3d0b6ff1dda89'}, 'status': 'targetingSet', 'params': [{'accountId': '184', 'placementId': '353538'}]}; + spec.onBidWon(bid); + expect(bid.nurl).to.deep.equal('nurl&s=0.66'); + expect(bid.burl).to.deep.equal(burl); + expect(bid.ad).to.deep.equal('burl&s=0.66'); + }); + }); +}); diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js new file mode 100644 index 00000000000..32bf15d53b9 --- /dev/null +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -0,0 +1,388 @@ +import { expect } from 'chai'; +import { spec } from 'modules/microadBidAdapter'; +import * as utils from 'src/utils'; + +describe('microadBidAdapter', () => { + const bidRequestTemplate = { + bidder: 'microad', + mediaTypes: { + banner: {} + }, + params: { + spot: 'spot-code' + }, + bidId: 'bid-id', + transactionId: 'transaction-id' + }; + + describe('isBidRequestValid', () => { + it('should return true when required parameters are set', () => { + const validBids = [ + bidRequestTemplate, + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + video: {} + } + }) + ]; + validBids.forEach(validBid => { + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + }); + + it('should return false when required parameters are not set', () => { + const bidWithoutParams = utils.deepClone(bidRequestTemplate); + delete bidWithoutParams.params; + const bidWithoutSpot = utils.deepClone(bidRequestTemplate); + delete bidWithoutSpot.params.spot; + const bidWithoutMediaTypes = utils.deepClone(bidRequestTemplate); + delete bidWithoutMediaTypes.mediaTypes; + + const invalidBids = [ + {}, + bidWithoutParams, + bidWithoutSpot, + bidWithoutMediaTypes, + Object.assign({}, bidRequestTemplate, { + mediaTypes: {} + }) + ]; + invalidBids.forEach(invalidBid => { + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + }); + }); + + describe('buildRequests', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://example.com/to', + referer: 'http://example.com/from' + } + }; + const expectedResultTemplate = { + spot: 'spot-code', + url: 'http://example.com/to', + referrer: 'http://example.com/from', + bid_id: 'bid-id', + transaction_id: 'transaction-id', + media_types: 1 + }; + + it('should generate valid media_types', () => { + const bidRequests = [ + bidRequestTemplate, + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, native: {}, video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {}, video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, video: {} + } + }) + ]; + + const results = bidRequests.map(bid => { + const requests = spec.buildRequests([bid], bidderRequest); + return requests[0].data.media_types; + }); + expect(results).to.deep.equal([ + 1, // BANNER + 3, // BANNER + NATIVE + 7, // BANNER + NATIVE + VIDEO + 2, // NATIVE + 6, // NATIVE + VIDEO + 4, // VIDEO + 5 // BANNER + VIDEO + ]); + }); + + it('should use window.location.href if there is no canonicalUrl', () => { + const bidderRequestWithoutCanonicalUrl = { + refererInfo: { + referer: 'http://example.com/from' + } + }; + const requests = spec.buildRequests([bidRequestTemplate], bidderRequestWithoutCanonicalUrl); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + url: window.location.href + }) + ); + }); + }); + + it('should generate valid request with no optional parameters', () => { + const requests = spec.buildRequests([bidRequestTemplate], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt + }) + ); + }); + }); + + it('should add url_macro parameter to response if request parameters contain url', () => { + const bidRequestWithUrl = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + url: '${COMPASS_EXT_URL}url-macro' + } + }); + const requests = spec.buildRequests([bidRequestWithUrl], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + url_macro: 'url-macro' + }) + ); + }); + }); + + it('should add referrer_macro parameter to response if request parameters contain referrer', () => { + const bidRequestWithReferrer = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + referrer: '${COMPASS_EXT_REF}referrer-macro' + } + }); + const requests = spec.buildRequests([bidRequestWithReferrer], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + referrer_macro: 'referrer-macro' + }) + ); + }); + }); + + it('should add ifa parameter to response if request parameters contain ifa', () => { + const bidRequestWithIfa = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + ifa: '${COMPASS_EXT_IFA}ifa' + } + }); + const requests = spec.buildRequests([bidRequestWithIfa], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + ifa: 'ifa' + }) + ); + }); + }); + + it('should add appid parameter to response if request parameters contain appid', () => { + const bidRequestWithAppid = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + appid: '${COMPASS_EXT_APPID}appid' + } + }); + const requests = spec.buildRequests([bidRequestWithAppid], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + appid: 'appid' + }) + ); + }); + }); + + it('should add geo parameter to response if request parameters contain geo', () => { + const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + geo: '${COMPASS_EXT_GEO}35.655275,139.693771' + } + }); + const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + geo: '35.655275,139.693771' + }) + ); + }); + }); + + it('should not add geo parameter to response if request parameters contain invalid geo', () => { + const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + geo: '${COMPASS_EXT_GEO}invalid format geo' + } + }); + const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt + }) + ); + }); + }); + + it('should always use the HTTPS endpoint https://s-rtb-pb.send.microad.jp/prebid even if it is served via HTTP', () => { + const requests = spec.buildRequests([bidRequestTemplate], bidderRequest); + requests.forEach(request => { + expect(request.url.lastIndexOf('https', 0) === 0).to.be.true; + }); + }); + }); + + describe('interpretResponse', () => { + const serverResponseTemplate = { + body: { + requestId: 'request-id', + cpm: 0.1, + width: 200, + height: 100, + ad: '
    test
    ', + ttl: 10, + creativeId: 'creative-id', + netRevenue: true, + currency: 'JPY' + } + }; + const expectedBidResponseTemplate = { + requestId: 'request-id', + cpm: 0.1, + width: 200, + height: 100, + ad: '
    test
    ', + ttl: 10, + creativeId: 'creative-id', + netRevenue: true, + currency: 'JPY' + }; + + it('should return nothing if server response body does not contain cpm', () => { + const emptyResponse = { + body: {} + }; + + expect(spec.interpretResponse(emptyResponse)).to.deep.equal([]); + }); + + it('should return nothing if returned cpm is zero', () => { + const serverResponse = { + body: { + cpm: 0 + } + }; + + expect(spec.interpretResponse(serverResponse)).to.deep.equal([]); + }); + + it('should return a valid bidResponse without deal id if serverResponse is valid, has a nonzero cpm and no deal id', () => { + expect(spec.interpretResponse(serverResponseTemplate)).to.deep.equal([expectedBidResponseTemplate]); + }); + + it('should return a valid bidResponse with deal id if serverResponse is valid, has a nonzero cpm and a deal id', () => { + const serverResponseWithDealId = Object.assign({}, utils.deepClone(serverResponseTemplate)); + serverResponseWithDealId.body['dealId'] = 10001; + const expectedBidResponse = Object.assign({}, expectedBidResponseTemplate, { + dealId: 10001 + }); + + expect(spec.interpretResponse(serverResponseWithDealId)).to.deep.equal([expectedBidResponse]); + }); + }); + + describe('getUserSyncs', () => { + const BOTH_ENABLED = { + iframeEnabled: true, pixelEnabled: true + }; + const IFRAME_ENABLED = { + iframeEnabled: true, pixelEnabled: false + }; + const PIXEL_ENABLED = { + iframeEnabled: false, pixelEnabled: true + }; + const BOTH_DISABLED = { + iframeEnabled: false, pixelEnabled: false + }; + const serverResponseTemplate = { + body: { + syncUrls: { + iframe: ['https://www.exmaple.com/iframe1', 'https://www.exmaple.com/iframe2'], + image: ['https://www.exmaple.com/image1', 'https://www.exmaple.com/image2'] + } + } + }; + const expectedIframeSyncs = [ + {type: 'iframe', url: 'https://www.exmaple.com/iframe1'}, + {type: 'iframe', url: 'https://www.exmaple.com/iframe2'} + ]; + const expectedImageSyncs = [ + {type: 'image', url: 'https://www.exmaple.com/image1'}, + {type: 'image', url: 'https://www.exmaple.com/image2'} + ]; + + it('should return nothing if no sync urls are set', () => { + const serverResponse = utils.deepClone(serverResponseTemplate); + serverResponse.body.syncUrls.iframe = []; + serverResponse.body.syncUrls.image = []; + + const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponse]); + expect(syncs).to.deep.equal([]); + }); + + it('should return nothing if sync is disabled', () => { + const syncs = spec.getUserSyncs(BOTH_DISABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal([]); + }); + + it('should register iframe and image sync urls if sync is enabled', () => { + const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedIframeSyncs.concat(expectedImageSyncs)); + }); + + it('should register iframe sync urls if iframe is enabled', () => { + const syncs = spec.getUserSyncs(IFRAME_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedIframeSyncs); + }); + + it('should register image sync urls if image is enabled', () => { + const syncs = spec.getUserSyncs(PIXEL_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedImageSyncs); + }); + }); +}); diff --git a/test/spec/modules/mobfoxBidAdapter_spec.js b/test/spec/modules/mobfoxBidAdapter_spec.js index 54a057991e3..90125f3e0d0 100644 --- a/test/spec/modules/mobfoxBidAdapter_spec.js +++ b/test/spec/modules/mobfoxBidAdapter_spec.js @@ -1,4 +1,4 @@ -describe('mobfox adapter tests', () => { +describe('mobfox adapter tests', function () { const expect = require('chai').expect; const utils = require('src/utils'); const adapter = require('modules/mobfoxBidAdapter'); @@ -18,7 +18,7 @@ describe('mobfox adapter tests', () => { transactionId: '31f42cba-5920-4e47-adad-69c79d0d4fb4' }]; - describe('validRequests', () => { + describe('validRequests', function () { let bidRequestInvalid1 = [{ code: 'div-gpt-ad-1460505748561-0', sizes: [[320, 480], [300, 250], [300, 600]], @@ -33,19 +33,19 @@ describe('mobfox adapter tests', () => { transactionId: '31f42cba-5920-4e47-adad-69c79d0d4fb4' }]; - it('test valid MF request success', () => { + it('test valid MF request success', function () { let isValid = adapter.spec.isBidRequestValid(bidRequest[0]); expect(isValid).to.equal(true); }); - it('test valid MF request failed1', () => { + it('test valid MF request failed1', function () { let isValid = adapter.spec.isBidRequestValid(bidRequestInvalid1[0]); expect(isValid).to.equal(false); }); }) - describe('buildRequests', () => { - it('test build MF request', () => { + describe('buildRequests', function () { + it('test build MF request', function () { let request = adapter.spec.buildRequests(bidRequest); let payload = request.data.split('&'); expect(payload[0]).to.equal('rt=api-fetchip'); @@ -57,7 +57,7 @@ describe('mobfox adapter tests', () => { expect(payload[7]).to.equal('imp_instl=1'); }); - it('test build MF request', () => { + it('test build MF request', function () { let request = adapter.spec.buildRequests(bidRequest); let payload = request.data.split('&'); expect(payload[0]).to.equal('rt=api-fetchip'); @@ -70,7 +70,7 @@ describe('mobfox adapter tests', () => { }); }) - describe('interceptResponse', () => { + describe('interceptResponse', function () { let mockServerResponse = { body: { request: { @@ -93,7 +93,7 @@ describe('mobfox adapter tests', () => { } } }; - it('test intercept response', () => { + it('test intercept response', function () { let request = adapter.spec.buildRequests(bidRequest); let bidResponses = adapter.spec.interpretResponse(mockServerResponse, request); expect(bidResponses.length).to.equal(1); @@ -109,7 +109,7 @@ describe('mobfox adapter tests', () => { expect(bidResponses[0].width).to.equal('320'); }); - it('test intercept response with empty server response', () => { + it('test intercept response with empty server response', function () { let request = adapter.spec.buildRequests(bidRequest); let serverResponse = { request: { diff --git a/test/spec/modules/my6senseBidAdapter_spec.js b/test/spec/modules/my6senseBidAdapter_spec.js new file mode 100644 index 00000000000..d2c875dad07 --- /dev/null +++ b/test/spec/modules/my6senseBidAdapter_spec.js @@ -0,0 +1,151 @@ +import { expect } from 'chai'; +import { spec } from 'modules/my6senseBidAdapter'; + +describe('My6sense Bid adapter test', function () { + let bidRequests, serverResponses; + beforeEach(function () { + bidRequests = [ + { + // valid 1 + bidder: 'my6sense', + params: { + key: 'DTAeOJN67pCjY36dbhrM3G', + dataVersion: 3, + pageUrl: 'liran.com', + zone: '[ZONE]', + dataParams: '', + dataView: '', + organicClicks: '', + paidClicks: '' + } + }, + { + // invalid 2- no params + bidder: 'my6sense' + }, + { + // invalid 3 - no key in params + bidder: 'my6sense', + params: { + dataVersion: 3, + pageUrl: 'liran.com', + zone: '[ZONE]', + dataParams: '', + dataView: '', + organicClicks: '', + paidClicks: '' + } + }, + { + // invalid 3 - wrong bidder name + bidder: 'test', + params: { + key: 'ZxA0bNhlO9tf5EZ1Q9ZYdS', + dataVersion: 3, + pageUrl: 'liran.com', + zone: '[ZONE]', + dataParams: '', + dataView: '', + organicClicks: '', + paidClicks: '' + } + } + ]; + serverResponses = [ + { + headers: {}, + body: { + cpm: 1.5, + width: 300, + height: 250, + placement_id: 1, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 0, + width: 0, + height: 0, + placement_id: 1, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 0, + width: 0, + height: 0, + placement_id: 0, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 5, + creativeId: '5b29f5d1e4b086e3ee8de36b', + currency: 'USD', + height: 250, + netRevenue: false, + requestId: '2954a0957643bb', + ttl: 360, + width: 300, + adm: '' + } + } + ] + }); + + describe('test if requestIsValid function', function () { + it('with valid data 1', function () { + expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + }); + it('with invalid data 2', function () { + expect(spec.isBidRequestValid(bidRequests[1])).to.equal(false); + }); + it('with invalid data 3', function () { + expect(spec.isBidRequestValid(bidRequests[2])).to.equal(false); + }); + it('with invalid data 3', function () { + expect(spec.isBidRequestValid(bidRequests[3])).to.equal(false); + }); + }); + + describe('test if buildRequests function', function () { + it('normal', function () { + var requests = spec.buildRequests([bidRequests[0]]); + expect(requests).to.be.lengthOf(1); + }); + }); + describe('test bid responses', function () { + it('success 1', function () { + var bids = spec.interpretResponse(serverResponses[0], {'bidRequest': bidRequests[0]}); + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(1.5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].adm).to.have.length.above(1); + }); + it('success 2', function () { + var bids = spec.interpretResponse(serverResponses[3]); + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].netRevenue).to.equal(false); + expect(bids[0].ttl).to.equal(360); + expect(bids[0].currency).to.equal('USD'); + }); + it('fail 1 (cpm=0)', function () { + var bids = spec.interpretResponse(serverResponses[1]); + expect(bids).to.be.lengthOf(1); + }); + it('fail 2 (no response)', function () { + var bids = spec.interpretResponse([]); + expect(bids).to.be.lengthOf(0); + }); + }); +}); diff --git a/test/spec/modules/mytargetBidAdapter_spec.js b/test/spec/modules/mytargetBidAdapter_spec.js new file mode 100644 index 00000000000..4e478fee1f0 --- /dev/null +++ b/test/spec/modules/mytargetBidAdapter_spec.js @@ -0,0 +1,229 @@ +import { expect } from 'chai'; +import { config } from 'src/config'; +import { spec } from 'modules/mytargetBidAdapter'; + +describe('MyTarget Adapter', function() { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + let validBid = { + bidder: 'mytarget', + params: { + placementId: '1' + } + }; + + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + + it('should return false for when required params are not passed', function () { + let invalidBid = { + bidder: 'mytarget', + params: {} + }; + + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + bidId: 'bid1', + bidder: 'mytarget', + params: { + placementId: '1' + } + }, + { + bidId: 'bid2', + bidder: 'mytarget', + params: { + placementId: '2', + position: 1, + response: 1, + bidfloor: 10000 + } + } + ]; + let bidderRequest = { + refererInfo: { + referer: 'https://example.com?param=value' + } + }; + + let bidRequest = spec.buildRequests(bidRequests, bidderRequest); + + it('should build single POST request for multiple bids', function() { + expect(bidRequest.method).to.equal('POST'); + expect(bidRequest.url).to.equal('//ad.mail.ru/hbid_prebid/'); + expect(bidRequest.data).to.be.an('object'); + expect(bidRequest.data.places).to.be.an('array'); + expect(bidRequest.data.places).to.have.lengthOf(2); + }); + + it('should pass bid parameters', function() { + let place1 = bidRequest.data.places[0]; + let place2 = bidRequest.data.places[1]; + + expect(place1.placementId).to.equal('1'); + expect(place2.placementId).to.equal('2'); + expect(place1.id).to.equal('bid1'); + expect(place2.id).to.equal('bid2'); + }); + + it('should pass default position and response type', function() { + let place = bidRequest.data.places[0]; + + expect(place.position).to.equal(0); + expect(place.response).to.equal(0); + }); + + it('should pass provided position and response type', function() { + let place = bidRequest.data.places[1]; + + expect(place.position).to.equal(1); + expect(place.response).to.equal(1); + }); + + it('should not pass default bidfloor', function() { + let place = bidRequest.data.places[0]; + + expect(place.bidfloor).not.to.exist; + }); + + it('should not pass provided bidfloor', function() { + let place = bidRequest.data.places[1]; + + expect(place.bidfloor).to.exist; + expect(place.bidfloor).to.equal(10000); + }); + + it('should pass site parameters', function() { + let site = bidRequest.data.site; + + expect(site).to.be.an('object'); + expect(site.sitename).to.equal('example.com'); + expect(site.page).to.equal('https://example.com?param=value'); + }); + + it('should pass settings', function() { + let settings = bidRequest.data.settings; + + expect(settings).to.be.an('object'); + expect(settings.currency).to.equal('RUB'); + expect(settings.windowSize).to.be.an('object'); + expect(settings.windowSize.width).to.equal(window.screen.width); + expect(settings.windowSize.height).to.equal(window.screen.height); + }); + + it('should pass currency from currency.adServerCurrency', function() { + const configStub = sinon.stub(config, 'getConfig').callsFake( + key => key === 'currency.adServerCurrency' ? 'USD' : ''); + + let bidRequest = spec.buildRequests(bidRequests, bidderRequest); + let settings = bidRequest.data.settings; + + expect(settings).to.be.an('object'); + expect(settings.currency).to.equal('USD'); + expect(settings.windowSize).to.be.an('object'); + expect(settings.windowSize.width).to.equal(window.screen.width); + expect(settings.windowSize.height).to.equal(window.screen.height); + + configStub.restore(); + }); + + it('should ignore currency other than "RUB" or "USD"', function() { + const configStub = sinon.stub(config, 'getConfig').callsFake( + key => key === 'currency.adServerCurrency' ? 'EUR' : ''); + + let bidRequest = spec.buildRequests(bidRequests, bidderRequest); + let settings = bidRequest.data.settings; + + expect(settings).to.be.an('object'); + expect(settings.currency).to.equal('RUB'); + + configStub.restore(); + }); + }); + + describe('interpretResponse', function () { + let serverResponse = { + body: { + 'bidder_status': + [ + { + 'bidder': 'mail.ru', + 'response_time_ms': 100, + 'num_bids': 2 + } + ], + 'bids': + [ + { + 'displayUrl': 'https://ad.mail.ru/hbid_imp/12345', + 'size': + { + 'height': '400', + 'width': '240' + }, + 'id': '1', + 'currency': 'RUB', + 'price': 100, + 'ttl': 360, + 'creativeId': '123456' + }, + { + 'adm': '

    Ad

    ', + 'size': + { + 'height': '250', + 'width': '300' + }, + 'id': '2', + 'price': 200 + } + ] + } + }; + + let bids = spec.interpretResponse(serverResponse); + + it('should return empty array for response with no bids', function() { + let emptyBids = spec.interpretResponse({ body: {} }); + + expect(emptyBids).to.have.lengthOf(0); + }); + + it('should parse all bids from response', function() { + expect(bids).to.have.lengthOf(2); + }); + + it('should parse bid with ad url', function() { + expect(bids[0].requestId).to.equal('1'); + expect(bids[0].cpm).to.equal(100); + expect(bids[0].width).to.equal('240'); + expect(bids[0].height).to.equal('400'); + expect(bids[0].ttl).to.equal(360); + expect(bids[0].currency).to.equal('RUB'); + expect(bids[0]).to.have.property('creativeId'); + expect(bids[0].creativeId).to.equal('123456'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].adUrl).to.equal('https://ad.mail.ru/hbid_imp/12345'); + expect(bids[0]).to.not.have.property('ad'); + }); + + it('should parse bid with ad markup', function() { + expect(bids[1].requestId).to.equal('2'); + expect(bids[1].cpm).to.equal(200); + expect(bids[1].width).to.equal('300'); + expect(bids[1].height).to.equal('250'); + expect(bids[1].ttl).to.equal(180); + expect(bids[1].currency).to.equal('RUB'); + expect(bids[1]).to.have.property('creativeId'); + expect(bids[1].creativeId).not.to.equal('123456'); + expect(bids[1].netRevenue).to.equal(true); + expect(bids[1].ad).to.equal('

    Ad

    '); + expect(bids[1]).to.not.have.property('adUrl'); + }); + }); +}); diff --git a/test/spec/modules/nafdigitalBidAdapter_spec.js b/test/spec/modules/nafdigitalBidAdapter_spec.js new file mode 100644 index 00000000000..ca486c632c7 --- /dev/null +++ b/test/spec/modules/nafdigitalBidAdapter_spec.js @@ -0,0 +1,283 @@ +import { expect } from 'chai'; +import * as utils from 'src/utils'; +import { spec } from 'modules/nafdigitalBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const URL = 'https://nafdigitalbidder.com/hb'; + +describe('nafdigitalBidAdapter', function() { + const adapter = newBidder(spec); + let element, win; + let bidRequests; + let sandbox; + + beforeEach(function() { + element = { + x: 0, + y: 0, + + width: 0, + height: 0, + + getBoundingClientRect: () => { + return { + width: element.width, + height: element.height, + + left: element.x, + top: element.y, + right: element.x + element.width, + bottom: element.y + element.height + }; + } + }; + win = { + document: { + visibilityState: 'visible' + }, + + innerWidth: 800, + innerHeight: 600 + }; + bidRequests = [{ + 'bidder': 'nafdigital', + 'params': { + 'publisherId': 1234567 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '5fb26ac22bde4', + 'bidderRequestId': '4bf93aeb730cb9', + 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e' + }]; + + sandbox = sinon.sandbox.create(); + sandbox.stub(document, 'getElementById').withArgs('adunit-code').returns(element); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns(win); + }); + + afterEach(function() { + sandbox.restore(); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'nafdigital', + 'params': { + 'publisherId': 1234567 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '5fb26ac22bde4', + 'bidderRequestId': '4bf93aeb730cb9', + 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when tagid not passed correctly', function () { + bid.params.publisherId = undefined; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('sends bid request to our endpoint via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.method).to.equal('POST'); + }); + + it('request url should match our endpoint url', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(URL); + }); + + it('sets the proper banner object', function() { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + }); + + it('accepts a single array as a size', function() { + bidRequests[0].sizes = [300, 250]; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); + }); + + it('sends bidfloor param if present', function () { + bidRequests[0].params.bidFloor = 0.05; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].bidfloor).to.equal(0.05); + }); + + it('sends tagid', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].tagid).to.equal('adunit-code'); + }); + + it('sends publisher id', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.site.publisher.id).to.equal(1234567); + }); + + context('when element is fully in view', function() { + it('returns 100', function() { + Object.assign(element, { width: 600, height: 400 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(100); + }); + }); + + context('when element is out of view', function() { + it('returns 0', function() { + Object.assign(element, { x: -300, y: 0, width: 207, height: 320 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(0); + }); + }); + + context('when element is partially in view', function() { + it('returns percentage', function() { + Object.assign(element, { width: 800, height: 800 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(75); + }); + }); + + context('when width or height of the element is zero', function() { + it('try to use alternative values', function() { + Object.assign(element, { width: 0, height: 0 }); + bidRequests[0].sizes = [[800, 2400]]; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(25); + }); + }); + + context('when nested iframes', function() { + it('returns \'na\'', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + utils.getWindowSelf.restore(); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns({}); + + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal('na'); + }); + }); + + context('when tab is inactive', function() { + it('returns 0', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + win.document.visibilityState = 'hidden'; + sandbox.stub(utils, 'getWindowTop').returns(win); + + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(0); + }); + }); + }); + + describe('interpretResponse', function () { + let response; + beforeEach(function () { + response = { + body: { + 'id': '37386aade21a71', + 'seatbid': [{ + 'bid': [{ + 'id': '376874781', + 'impid': '283a9f4cd2415d', + 'price': 0.35743275, + 'nurl': '', + 'adm': '', + 'w': 300, + 'h': 250 + }] + }] + } + }; + }); + + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': '283a9f4cd2415d', + 'cpm': 0.35743275, + 'width': 300, + 'height': 250, + 'creativeId': '376874781', + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': `
    `, + 'ttl': 60 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('crid should default to the bid id if not on the response', function () { + let expectedResponse = [{ + 'requestId': '283a9f4cd2415d', + 'cpm': 0.35743275, + 'width': 300, + 'height': 250, + 'creativeId': response.body.seatbid[0].bid[0].id, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': `
    `, + 'ttl': 60 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('handles empty bid response', function () { + let response = { + body: '' + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs ', () => { + let syncOptions = {iframeEnabled: true, pixelEnabled: true}; + + it('should not return', () => { + let returnStatement = spec.getUserSyncs(syncOptions, []); + expect(returnStatement).to.be.empty; + }); + }); +}); diff --git a/test/spec/modules/nanointeractiveBidAdapter_spec.js b/test/spec/modules/nanointeractiveBidAdapter_spec.js index 92b6fe8d797..a357327fe96 100644 --- a/test/spec/modules/nanointeractiveBidAdapter_spec.js +++ b/test/spec/modules/nanointeractiveBidAdapter_spec.js @@ -1,109 +1,465 @@ -import { expect } from 'chai'; +import {expect} from 'chai'; import * as utils from 'src/utils'; +import * as sinon from 'sinon'; import { - BIDDER_CODE, CATEGORY, DATA_PARTNER_PIXEL_ID, ENGINE_BASE_URL, NQ, NQ_NAME, SUB_ID, - spec + BIDDER_CODE, + CATEGORY, + CATEGORY_NAME, + SSP_PLACEMENT_ID, + END_POINT_URL, + NQ, + NQ_NAME, + REF, + spec, + SUB_ID } from '../../../modules/nanointeractiveBidAdapter'; describe('nanointeractive adapter tests', function () { - const SEARCH_QUERY = 'rumpelstiltskin'; - const WIDTH = 300; - const HEIGHT = 250; - const SIZES = [[WIDTH, HEIGHT]]; + const SIZES_PARAM = 'sizes'; + const BID_ID_PARAM = 'bidId'; + const BID_ID_VALUE = '24a1c9ec270973'; + const DATA_PARTNER_PIXEL_ID_VALUE = 'testPID'; + const NQ_VALUE = 'rumpelstiltskin'; + const NQ_NAME_QUERY_PARAM = 'nqName'; + const CATEGORY_VALUE = 'some category'; + const CATEGORY_NAME_QUERY_PARAM = 'catName'; + const SUB_ID_VALUE = '123'; + const REF_NO_VALUE = 'none'; + const REF_OTHER_VALUE = 'other'; + const WIDTH1 = 300; + const HEIGHT1 = 250; + const WIDTH2 = 468; + const HEIGHT2 = 60; + const SIZES_VALUE = [[WIDTH1, HEIGHT1], [WIDTH2, HEIGHT2]]; const AD = ' '; const CPM = 1; - function getBid(isValid) { + function getBidRequest(params) { return { bidder: BIDDER_CODE, - params: (function () { - return { - [DATA_PARTNER_PIXEL_ID]: isValid === true ? 'pid1' : null, - [NQ]: SEARCH_QUERY, - [NQ_NAME]: null, - [CATEGORY]: null, - [SUB_ID]: null, - } - })(), + params: params, placementCode: 'div-gpt-ad-1460505748561-0', transactionId: 'ee335735-ddd3-41f2-b6c6-e8aa99f81c0f', - sizes: SIZES, - bidId: '24a1c9ec270973', + [SIZES_PARAM]: SIZES_VALUE, + [BID_ID_PARAM]: BID_ID_VALUE, bidderRequestId: '189135372acd55', auctionId: 'ac15bb68-4ef0-477f-93f4-de91c47f00a9' - } - } - - const SINGLE_BID_REQUEST = { - [DATA_PARTNER_PIXEL_ID]: 'pid1', - [NQ]: [SEARCH_QUERY, null], - [SUB_ID]: null, - sizes: [WIDTH + 'x' + HEIGHT], - bidId: '24a1c9ec270973', - cors: 'http://localhost' - }; - - function getSingleBidResponse(isValid) { - return { - id: '24a1c9ec270973', - cpm: isValid === true ? CPM : null, - width: WIDTH, - height: HEIGHT, - ad: AD, - ttl: 360, - creativeId: 'TEST_ID', - netRevenue: false, - currency: 'EUR', - } + }; } - const VALID_BID = { - requestId: '24a1c9ec270973', - cpm: CPM, - width: WIDTH, - height: HEIGHT, - ad: AD, - ttl: 360, - creativeId: 'TEST_ID', - netRevenue: false, - currency: 'EUR', - }; - - describe('NanoAdapter', () => { + describe('NanoAdapter', function () { let nanoBidAdapter = spec; - describe('Methods', () => { - it('Test isBidRequestValid() with valid param', function () { - expect(nanoBidAdapter.isBidRequestValid(getBid(true))).to.equal(true); + describe('Methods', function () { + it('Test isBidRequestValid() with valid param(s): pid', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nq', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nq, category', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ, + [CATEGORY]: CATEGORY_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nq, categoryName', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ, + [CATEGORY_NAME_QUERY_PARAM]: CATEGORY_NAME_QUERY_PARAM, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nq, subId', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ, + [SUB_ID]: SUB_ID_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nqName', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ_NAME]: NQ_NAME_QUERY_PARAM, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nqName, category', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ_NAME]: NQ_NAME_QUERY_PARAM, + [CATEGORY]: CATEGORY_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nqName, categoryName', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ_NAME]: NQ_NAME_QUERY_PARAM, + [CATEGORY_NAME_QUERY_PARAM]: CATEGORY_NAME_QUERY_PARAM, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nqName, subId', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ_NAME]: NQ_NAME_QUERY_PARAM, + [SUB_ID]: SUB_ID_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, category', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [CATEGORY]: CATEGORY_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, category, subId', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [CATEGORY]: CATEGORY_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }))).to.equal(true); }); - it('Test isBidRequestValid() with invalid param', function () { - expect(nanoBidAdapter.isBidRequestValid(getBid(false))).to.equal(false); + it('Test isBidRequestValid() with valid param(s): pid, subId', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }))).to.equal(true); }); - it('Test buildRequests()', function () { - let stub = sinon.stub(utils, 'getOrigin').callsFake(() => 'http://localhost'); + it('Test isBidRequestValid() with valid param(s): pid, nq, category, subId', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + [CATEGORY]: CATEGORY_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nqName, categoryName, subId', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ_NAME]: NQ_NAME_QUERY_PARAM, + [CATEGORY_NAME]: CATEGORY_NAME_QUERY_PARAM, + [SUB_ID]: SUB_ID_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nq, category, subId, ref (value none)', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + [CATEGORY]: CATEGORY_VALUE, + [SUB_ID]: SUB_ID_VALUE, + [REF]: REF_NO_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with valid param(s): pid, nq, category, subId, ref (value other)', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + [CATEGORY]: CATEGORY_VALUE, + [SUB_ID]: SUB_ID_VALUE, + [REF]: REF_OTHER_VALUE, + }))).to.equal(true); + }); + it('Test isBidRequestValid() with invalid param(s): empty', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({}))).to.equal(false); + }); + it('Test isBidRequestValid() with invalid param(s): pid missing', function () { + expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ + [NQ]: NQ_VALUE, + [CATEGORY]: CATEGORY_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }))).to.equal(false); + }); + + let sandbox; + + function getMocks() { + let mockOriginAddress = 'http://localhost'; + let mockRefAddress = 'http://some-ref.test'; + return { + 'windowLocationAddress': mockRefAddress, + 'originAddress': mockOriginAddress, + 'refAddress': '', + }; + } + + function setUpMocks() { + sinon.sandbox.restore(); + sandbox = sinon.sandbox.create(); + sandbox.stub(utils, 'getOrigin').callsFake(() => getMocks()['originAddress']); + sandbox.stub(utils, 'deepAccess').callsFake(() => getMocks()['windowLocationAddress']); + + sandbox.stub(utils, 'getParameterByName').callsFake((arg) => { + switch (arg) { + case CATEGORY_NAME_QUERY_PARAM: + return CATEGORY_VALUE; + case NQ_NAME_QUERY_PARAM: + return NQ_VALUE; + } + }); + } + + function assert( + request, + expectedPid, + expectedNq, + expectedCategory, + expectedSubId + ) { + const requestData = JSON.parse(request.data); - let request = nanoBidAdapter.buildRequests([getBid(true)]); expect(request.method).to.equal('POST'); - expect(request.url).to.equal(ENGINE_BASE_URL); - expect(request.data).to.equal(JSON.stringify([SINGLE_BID_REQUEST])); + expect(request.url).to.equal(END_POINT_URL + '/hb'); + expect(requestData[0].pid).to.equal(expectedPid); + expect(requestData[0].nq.toString()).to.equal(expectedNq.toString()); + expect(requestData[0].category.toString()).to.equal(expectedCategory.toString()); + expect(requestData[0].subId).to.equal(expectedSubId); + } + + function tearDownMocks() { + sandbox.restore(); + } + + it('Test buildRequest() - pid', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [null]; + let expectedCategory = [null]; + let expectedSubId = null; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, nq', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [NQ_VALUE]; + let expectedCategory = [null]; + let expectedSubId = null; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, nq, category', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + [CATEGORY]: CATEGORY_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [NQ_VALUE]; + let expectedCategory = [CATEGORY_VALUE]; + let expectedSubId = null; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, nq, categoryName', function () { + setUpMocks(); + + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + [CATEGORY_NAME]: CATEGORY_NAME_QUERY_PARAM, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [NQ_VALUE]; + let expectedCategory = [CATEGORY_VALUE]; + let expectedSubId = null; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, nq, subId', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [NQ_VALUE]; + let expectedCategory = [null]; + let expectedSubId = SUB_ID_VALUE; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, category', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [CATEGORY]: CATEGORY_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [null]; + let expectedCategory = [CATEGORY_VALUE]; + let expectedSubId = null; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, category, subId', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [CATEGORY]: CATEGORY_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [null]; + let expectedCategory = [CATEGORY_VALUE]; + let expectedSubId = SUB_ID_VALUE; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, subId', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [null]; + let expectedCategory = [null]; + let expectedSubId = SUB_ID_VALUE; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, nq, category, subId', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ]: NQ_VALUE, + [CATEGORY]: CATEGORY_VALUE, + [SUB_ID]: SUB_ID_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [NQ_VALUE]; + let expectedCategory = [CATEGORY_VALUE]; + let expectedSubId = SUB_ID_VALUE; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); + + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); + }); + it('Test buildRequest() - pid, nqName, categoryName, subId', function () { + setUpMocks(); + let requestParams = { + [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, + [NQ_NAME]: NQ_NAME_QUERY_PARAM, + [CATEGORY_NAME]: CATEGORY_NAME_QUERY_PARAM, + [SUB_ID]: SUB_ID_VALUE, + }; + let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; + let expectedNq = [NQ_VALUE]; + let expectedCategory = [CATEGORY_VALUE]; + let expectedSubId = SUB_ID_VALUE; + + let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - stub.restore(); + assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); + tearDownMocks(); }); it('Test interpretResponse() length', function () { - let bids = nanoBidAdapter.interpretResponse({body: [getSingleBidResponse(true), getSingleBidResponse(false)]}); + let bids = nanoBidAdapter.interpretResponse({ + body: [ + // valid + { + id: '24a1c9ec270973', + cpm: CPM, + width: WIDTH1, + height: HEIGHT1, + ad: AD, + ttl: 360, + creativeId: 'TEST_ID', + netRevenue: false, + currency: 'EUR', + }, + // invalid + { + id: '24a1c9ec270973', + cpm: null, + width: WIDTH1, + height: HEIGHT1, + ad: AD, + ttl: 360, + creativeId: 'TEST_ID', + netRevenue: false, + currency: 'EUR', + } + ] + }); expect(bids.length).to.equal(1); }); it('Test interpretResponse() bids', function () { - let bid = nanoBidAdapter.interpretResponse({body: [getSingleBidResponse(true), getSingleBidResponse(false)]})[0]; - expect(bid.requestId).to.equal(VALID_BID.requestId); - expect(bid.cpm).to.equal(VALID_BID.cpm); - expect(bid.width).to.equal(VALID_BID.width); - expect(bid.height).to.equal(VALID_BID.height); - expect(bid.ad).to.equal(VALID_BID.ad); - expect(bid.ttl).to.equal(VALID_BID.ttl); - expect(bid.creativeId).to.equal(VALID_BID.creativeId); - expect(bid.currency).to.equal(VALID_BID.currency); + let bid = nanoBidAdapter.interpretResponse({ + body: [ + // valid + { + id: '24a1c9ec270973', + cpm: CPM, + width: WIDTH1, + height: HEIGHT1, + ad: AD, + ttl: 360, + creativeId: 'TEST_ID', + netRevenue: false, + currency: 'EUR', + }, + // invalid + { + id: '24a1c9ec270973', + cpm: null, + width: WIDTH1, + height: HEIGHT1, + ad: AD, + ttl: 360, + creativeId: 'TEST_ID', + netRevenue: false, + currency: 'EUR', + } + ] + })[0]; + expect(bid.requestId).to.equal('24a1c9ec270973'); + expect(bid.cpm).to.equal(CPM); + expect(bid.width).to.equal(WIDTH1); + expect(bid.height).to.equal(HEIGHT1); + expect(bid.ad).to.equal(AD); + expect(bid.ttl).to.equal(360); + expect(bid.creativeId).to.equal('TEST_ID'); + expect(bid.currency).to.equal('EUR'); }); }); }); diff --git a/test/spec/modules/nasmediaAdmixerBidAdapter_spec.js b/test/spec/modules/nasmediaAdmixerBidAdapter_spec.js index 7ed65718657..dd6ed4686d1 100644 --- a/test/spec/modules/nasmediaAdmixerBidAdapter_spec.js +++ b/test/spec/modules/nasmediaAdmixerBidAdapter_spec.js @@ -2,16 +2,16 @@ import {expect} from 'chai'; import {spec} from 'modules/nasmediaAdmixerBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; -describe('nasmediaAdmixerBidAdapter', () => { +describe('nasmediaAdmixerBidAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { const bid = { 'bidder': 'nasmediaAdmixer', 'params': { @@ -24,11 +24,11 @@ describe('nasmediaAdmixerBidAdapter', () => { 'auctionId': '124cb070528662', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { const bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -38,7 +38,7 @@ describe('nasmediaAdmixerBidAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { const bidRequests = [ { 'bidder': 'nasmediaAdmixer', @@ -53,14 +53,14 @@ describe('nasmediaAdmixerBidAdapter', () => { } ]; - it('sends bid request to url via GET', () => { + it('sends bid request to url via GET', function () { const request = spec.buildRequests(bidRequests)[0]; expect(request.method).to.equal('GET'); expect(request.url).to.match(new RegExp(`https://adn.admixer.co.kr`)); }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const response = { 'body': { 'bidder': 'nasmedia_admixer', @@ -94,7 +94,7 @@ describe('nasmediaAdmixerBidAdapter', () => { 'auctionId': '169827a33f03cc', }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const expectedResponse = [ { 'requestId': '861a8e7952c82c', @@ -122,7 +122,7 @@ describe('nasmediaAdmixerBidAdapter', () => { }); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { response.body = { 'bidder': 'nasmedia_admixer', 'req_id': '861a8e7952c82c', diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js new file mode 100644 index 00000000000..087f06d7e8e --- /dev/null +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -0,0 +1,91 @@ +import { expect } from 'chai'; +import { spec } from 'modules/nextMillenniumBidAdapter'; + +describe('nextMillenniumBidAdapterTests', function() { + let bidRequestData = { + bids: [ + { + bidId: 'transaction_1234', + bidder: 'nextMillennium', + params: { + placement_id: 12345 + }, + sizes: [[300, 250]] + } + ] + }; + let request = []; + + it('validate_pub_params', function() { + expect( + spec.isBidRequestValid({ + bidder: 'nextMillennium', + params: { + placement_id: 12345 + } + }) + ).to.equal(true); + }); + + it('validate_generated_params', function() { + let bidRequestData = [ + { + bidId: 'bid1234', + bidder: 'nextMillennium', + params: { placement_id: -1 }, + sizes: [[300, 250]] + } + ]; + let request = spec.buildRequests(bidRequestData); + expect(request[0].bidId).to.equal('bid1234'); + }); + + it('validate_getUserSyncs_function', function() { + expect(spec.getUserSyncs({ iframeEnabled: true })).to.have.lengthOf(1); + expect(spec.getUserSyncs({ iframeEnabled: false })).to.have.lengthOf(0); + + let pixel = spec.getUserSyncs({ iframeEnabled: true }); + expect(pixel[0].type).to.equal('iframe'); + expect(pixel[0].url).to.equal('https://brainlyads.com/hb/s2s/matching'); + }); + + it('validate_response_params', function() { + let serverResponse = { + body: { + cpm: 1.7, + width: 300, + height: 250, + creativeId: 'p35t0enob6twbt9mofjc8e', + ad: 'Hello! It\'s a test ad!' + } + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData.bids[0]); + expect(bids).to.have.lengthOf(1); + + let bid = bids[0]; + + expect(bid.creativeId).to.equal('p35t0enob6twbt9mofjc8e'); + expect(bid.ad).to.equal('Hello! It\'s a test ad!'); + expect(bid.cpm).to.equal(1.7); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.currency).to.equal('USD'); + }); + + it('validate_response_params_with passback', function() { + let serverResponse = { + body: [ + { + hash: '1e100887dd614b0909bf6c49ba7f69fdd1360437', + content: 'Ad html passback', + size: [300, 250], + is_passback: 1 + } + ] + }; + let bids = spec.interpretResponse(serverResponse); + + expect(bids).to.have.lengthOf(0); + }); +}); diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js new file mode 100644 index 00000000000..5134958d218 --- /dev/null +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -0,0 +1,256 @@ +import { expect } from 'chai'; +import { spec } from 'modules/nobidBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { deepClone } from 'src/utils'; + +describe('Nobid Adapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'nobid', + 'params': { + 'siteId': 2 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when required params found', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'siteId': 2 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'siteId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const SITE_ID = 2; + const REFERER = 'https://www.examplereferer.com'; + let bidRequests = [ + { + 'bidder': 'nobid', + 'params': { + 'siteId': SITE_ID + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + let bidderRequest = { + refererInfo: {referer: REFERER} + } + + it('should add source and verison to the tag', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.sid).to.equal(SITE_ID); + expect(payload.l).to.exist.and.to.equal(encodeURIComponent(REFERER)); + expect(payload.tt).to.exist; + expect(payload.a).to.exist; + expect(payload.t).to.exist; + expect(payload.tz).to.exist; + expect(payload.r).to.exist; + expect(payload.lang).to.exist; + expect(payload.ref).to.exist; + expect(payload.gdpr).to.exist; + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.contain('ads.servenobid.com/adreq'); + expect(request.method).to.equal('POST'); + }); + + it('should add gdpr consent information to the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'nobid', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr).to.exist; + expect(payload.gdpr.consentString).to.exist.and.to.equal(consentString); + expect(payload.gdpr.consentRequired).to.exist.and.to.be.true; + }); + }); + + describe('interpretResponse', function () { + const CREATIVE_ID_300x250 = 'CREATIVE-100'; + const ADUNIT_300x250 = 'ADUNIT-1'; + const ADMARKUP_300x250 = 'ADMARKUP-300x250'; + const PRICE_300x250 = 0.51; + const REQUEST_ID = '3db3773286ee59'; + const DEAL_ID = 'deal123'; + let response = { + country: 'US', + ip: '68.83.15.75', + device: 'COMPUTER', + site: 2, + bids: [ + {id: 1, + bdrid: 101, + divid: ADUNIT_300x250, + dealid: DEAL_ID, + creativeid: CREATIVE_ID_300x250, + size: {'w': 300, 'h': 250}, + adm: ADMARKUP_300x250, + price: '' + PRICE_300x250 + } + ] + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: REQUEST_ID, + cpm: PRICE_300x250, + width: 300, + height: 250, + creativeId: CREATIVE_ID_300x250, + dealId: DEAL_ID, + currency: 'USD', + netRevenue: true, + ttl: 300, + ad: ADMARKUP_300x250, + mediaType: 'banner' + } + ]; + + let bidderRequest = { + bids: [{ + bidId: REQUEST_ID, + adUnitCode: ADUNIT_300x250 + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + expect(result.length).to.equal(expectedResponse.length); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].requestId).to.equal(expectedResponse[0].requestId); + expect(result[0].cpm).to.equal(expectedResponse[0].cpm); + }); + + it('should get correct empty response', function () { + let bidderRequest = { + bids: [{ + bidId: REQUEST_ID, + adUnitCode: ADUNIT_300x250 + '1' + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + expect(result.length).to.equal(0); + }); + + it('should get correct deal id', function () { + let expectedResponse = [ + { + requestId: REQUEST_ID, + cpm: PRICE_300x250, + width: 300, + height: 250, + creativeId: CREATIVE_ID_300x250, + dealId: DEAL_ID, + currency: 'USD', + netRevenue: true, + ttl: 300, + ad: ADMARKUP_300x250, + mediaType: 'banner' + } + ]; + + let bidderRequest = { + bids: [{ + bidId: REQUEST_ID, + adUnitCode: ADUNIT_300x250 + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + expect(result.length).to.equal(expectedResponse.length); + expect(result[0].dealId).to.equal(expectedResponse[0].dealId); + }); + }); + + describe('getUserSyncs', function () { + const GDPR_CONSENT_STRING = 'GDPR_CONSENT_STRING'; + it('should get correct user sync when iframeEnabled', function () { + let pixel = spec.getUserSyncs({iframeEnabled: true}) + expect(pixel[0].type).to.equal('iframe'); + expect(pixel[0].url).to.equal('https://s3.amazonaws.com/nobid-public/sync.html'); + }); + + it('should get correct user sync when iframeEnabled and pixelEnabled', function () { + let pixel = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}) + expect(pixel[0].type).to.equal('iframe'); + expect(pixel[0].url).to.equal('https://s3.amazonaws.com/nobid-public/sync.html'); + }); + + it('should get correct user sync when iframeEnabled', function () { + let pixel = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true, consentString: GDPR_CONSENT_STRING}) + expect(pixel[0].type).to.equal('iframe'); + expect(pixel[0].url).to.equal('https://s3.amazonaws.com/nobid-public/sync.html?gdpr=1&gdpr_consent=' + GDPR_CONSENT_STRING); + }); + + it('should get correct user sync when !iframeEnabled', function () { + let pixel = spec.getUserSyncs({iframeEnabled: false}) + expect(pixel.length).to.equal(0); + }); + + it('should get correct user sync when !iframeEnabled', function () { + let pixel = spec.getUserSyncs({}) + expect(pixel.length).to.equal(0); + }); + }); + + describe('onTimeout', function (syncOptions) { + it('should increment timeoutTotal', function () { + let timeoutTotal = spec.onTimeout() + expect(timeoutTotal).to.equal(1); + }); + }); + + describe('onBidWon', function (syncOptions) { + it('should increment bidWonTotal', function () { + let bidWonTotal = spec.onBidWon() + expect(bidWonTotal).to.equal(1); + }); + }); +}); diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 3d7bba417f9..58b90b0a017 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -1,15 +1,25 @@ import { expect } from 'chai'; import { spec } from 'modules/oneVideoBidAdapter'; import * as utils from 'src/utils'; +import {config} from 'src/config'; -describe('OneVideoBidAdapter', () => { +describe('OneVideoBidAdapter', function () { let bidRequest; + let bidderRequest; + let mockConfig; - beforeEach(() => { + beforeEach(function () { bidRequest = { + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + } + }, bidder: 'oneVideo', sizes: [640, 480], bidId: '30b3efwfwe1e', + adUnitCode: 'video1', params: { video: { playerWidth: 640, @@ -18,7 +28,11 @@ describe('OneVideoBidAdapter', () => { protocols: [2, 5], api: [2], position: 1, - delivery: [2] + delivery: [2], + playbackmethod: [1, 5], + placement: 123, + sid: 134, + rewarded: 1 }, site: { id: 1, @@ -30,19 +44,19 @@ describe('OneVideoBidAdapter', () => { }; }); - describe('spec.isBidRequestValid', () => { - it('should return true when the required params are passed', () => { + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed', function () { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the "video" param is missing', () => { + it('should return false when the "video" param is missing', function () { bidRequest.params = { pubId: 'brxd' }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when the "pubId" param is missing', () => { + it('should return false when the "pubId" param is missing', function () { bidRequest.params = { video: { playerWidth: 480, @@ -51,40 +65,67 @@ describe('OneVideoBidAdapter', () => { protocols: [2, 5], api: [2], position: 1, - delivery: [2] + delivery: [2], + playbackmethod: [1, 5], + placement: 123, + sid: 134, + rewarded: 1 } }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); + it('should return true when the "pubId" param is missing', function () { + bidRequest.params = { + video: { + playerWidth: 480, + playerHeight: 640, + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [2], + position: 1, + delivery: [2], + playbackmethod: [1, 5], + placement: 123, + sid: 134, + rewarded: 1 + }, + pubId: 'brxd' + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); - it('should return false when no bid params are passed', () => { + it('should return false when no bid params are passed', function () { bidRequest.params = {}; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); }); - describe('spec.buildRequests', () => { - it('should create a POST request for every bid', () => { + describe('spec.buildRequests', function () { + it('should create a POST request for every bid', function () { const requests = spec.buildRequests([ bidRequest ]); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(location.protocol + spec.ENDPOINT + bidRequest.params.pubId); }); - it('should attach the bid request object', () => { + it('should attach the bid request object', function () { const requests = spec.buildRequests([ bidRequest ]); expect(requests[0].bidRequest).to.equal(bidRequest); }); - it('should attach request data', () => { + it('should attach request data', function () { const requests = spec.buildRequests([ bidRequest ]); const data = requests[0].data; const [ width, height ] = bidRequest.sizes; + const placement = bidRequest.params.video.placement; + const rewarded = bidRequest.params.video.rewarded; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); + expect(data.imp[0].ext.placement).to.equal(placement); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); + expect(data.imp[0].ext.rewarded).to.equal(rewarded); }); - it('must parse bid size from a nested array', () => { + it('must parse bid size from a nested array', function () { const width = 640; const height = 480; bidRequest.sizes = [[ width, height ]]; @@ -95,41 +136,80 @@ describe('OneVideoBidAdapter', () => { }); }); - describe('spec.interpretResponse', () => { - it('should return no bids if the response is not valid', () => { + describe('spec.interpretResponse', function () { + it('should return no bids if the response is not valid', function () { const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response "nurl" and "adm" are missing', () => { + it('should return no bids if the response "nurl" and "adm" are missing', function () { const serverResponse = {seatbid: [{bid: [{price: 6.01}]}]}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response "price" is missing', () => { + it('should return no bids if the response "price" is missing', function () { const serverResponse = {seatbid: [{bid: [{adm: ''}]}]}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return a valid bid response with just "adm"', () => { - const serverResponse = {seatbid: [{bid: [{id: 1, price: 6.01, adm: ''}]}], cur: 'USD'}; + it('should return a valid bid response with just "adm"', function () { + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); let o = { requestId: bidRequest.bidId, bidderCode: spec.code, cpm: serverResponse.seatbid[0].bid[0].price, - creativeId: serverResponse.seatbid[0].bid[0].id, + adId: serverResponse.seatbid[0].bid[0].adid, + creativeId: serverResponse.seatbid[0].bid[0].crid, vastXml: serverResponse.seatbid[0].bid[0].adm, width: 640, height: 480, mediaType: 'video', currency: 'USD', ttl: 100, - netRevenue: true + netRevenue: true, + adUnitCode: bidRequest.adUnitCode, + renderer: (bidRequest.mediaTypes.video.context === 'outstream') ? newRenderer(bidRequest, bidResponse) : undefined, }; expect(bidResponse).to.deep.equal(o); }); }); + + describe('when GDPR applies', function () { + beforeEach(function () { + bidderRequest = { + gdprConsent: { + consentString: 'test-gdpr-consent-string', + gdprApplies: true + } + }; + + mockConfig = { + consentManagement: { + cmpApi: 'iab', + timeout: 1111, + allowAuctionWithoutConsent: 'cancel' + } + }; + }); + + it('should send a signal to specify that GDPR applies to this request', function () { + const request = spec.buildRequests([ bidRequest ], bidderRequest); + expect(request[0].data.regs.ext.gdpr).to.equal(1); + }); + + it('should send the consent string', function () { + const request = spec.buildRequests([ bidRequest ], bidderRequest); + expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); + }); + + it('should send schain object', function () { + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.source.ext.schain.nodes[0].sid).to.equal(bidRequest.params.video.sid); + expect(data.source.ext.schain.nodes[0].rid).to.equal(data.id); + }); + }); }); diff --git a/test/spec/modules/oneplanetonlyBidAdapter_spec.js b/test/spec/modules/oneplanetonlyBidAdapter_spec.js index 4a42b471b6f..fbcec66ef51 100644 --- a/test/spec/modules/oneplanetonlyBidAdapter_spec.js +++ b/test/spec/modules/oneplanetonlyBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import {spec} from '../../../modules/oneplanetonlyBidAdapter'; -describe('OnePlanetOnlyAdapter', () => { +describe('OnePlanetOnlyAdapter', function () { let bid = { bidId: '51ef8751f9aead', bidder: 'oneplanetonly', @@ -16,31 +16,31 @@ describe('OnePlanetOnlyAdapter', () => { auctionId: '18fd8b8b0bd757' }; - describe('isBidRequestValid', () => { - it('Should return true if there are params.siteId and params.adUnitId parameters present', () => { + describe('isBidRequestValid', function () { + it('Should return true if there are params.siteId and params.adUnitId parameters present', function () { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false if at least one of parameters is not present', () => { + it('Should return false if at least one of parameters is not present', function () { delete bid.params.adUnitId; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let serverRequest = spec.buildRequests([bid]); - it('Creates a ServerRequest object with method, URL and data', () => { + it('Creates a ServerRequest object with method, URL and data', function () { expect(serverRequest).to.exist; expect(serverRequest.method).to.exist; expect(serverRequest.url).to.exist; expect(serverRequest.data).to.exist; }); - it('Returns POST method', () => { + it('Returns POST method', function () { expect(serverRequest.method).to.equal('POST'); }); - it('Returns valid URL', () => { + it('Returns valid URL', function () { expect(serverRequest.url).to.equal('//show.oneplanetonly.com/prebid?siteId=5'); }); - it('Returns valid data if array of bids is valid', () => { + it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; expect(data).to.be.an('object'); expect(data).to.have.all.keys('id', 'ver', 'prebidVer', 'transactionId', 'currency', 'timeout', 'siteId', @@ -52,14 +52,14 @@ describe('OnePlanetOnlyAdapter', () => { expect(adUnit.bidId).to.equal('51ef8751f9aead'); expect(adUnit.sizes).to.have.members(['300x250', '300x600']); }); - it('Returns empty data if no valid requests are passed', () => { + it('Returns empty data if no valid requests are passed', function () { serverRequest = spec.buildRequests([]); let data = serverRequest.data; expect(data.adUnits).to.be.an('array').that.is.empty; }); }); - describe('interpretResponse', () => { - it('Should interpret banner response', () => { + describe('interpretResponse', function () { + it('Should interpret banner response', function () { const serverResponse = { body: { bids: [{ @@ -89,7 +89,7 @@ describe('OnePlanetOnlyAdapter', () => { expect(bidObject.netRevenue).to.be.true; expect(bidObject.currency).to.equal('USD'); }); - it('Should return an empty array if invalid response is passed', () => { + it('Should return an empty array if invalid response is passed', function () { const invalid = { body: {} }; diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index dfd3254fd75..d56ad9e6dc5 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -1,7 +1,7 @@ import { spec } from 'modules/onetagBidAdapter'; import { expect } from 'chai'; -describe('onetag', () => { +describe('onetag', function () { let bid = { 'bidder': 'onetag', 'params': { @@ -15,43 +15,43 @@ describe('onetag', () => { 'transactionId': 'qwerty123' }; - describe('isBidRequestValid', () => { - it('Should return true when required params are found', () => { + describe('isBidRequestValid', function () { + it('Should return true when required params are found', function () { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false when pubId is not a string', () => { + it('Should return false when pubId is not a string', function () { bid.params.pubId = 30; expect(spec.isBidRequestValid(bid)).to.be.false; }); - it('Should return false when pubId is undefined', () => { + it('Should return false when pubId is undefined', function () { bid.params.pubId = undefined; expect(spec.isBidRequestValid(bid)).to.be.false; }); - it('Should return false when the sizes array is empty', () => { + it('Should return false when the sizes array is empty', function () { bid.sizes = []; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let serverRequest = spec.buildRequests([bid]); - it('Creates a ServerRequest object with method, URL and data', () => { + it('Creates a ServerRequest object with method, URL and data', function () { expect(serverRequest).to.exist; expect(serverRequest.method).to.exist; expect(serverRequest.url).to.exist; expect(serverRequest.data).to.exist; }); - it('Returns POST method', () => { + it('Returns POST method', function () { expect(serverRequest.method).to.equal('POST'); }); - it('Returns valid URL', () => { + it('Returns valid URL', function () { expect(serverRequest.url).to.equal('https://onetag-sys.com/prebid-request'); }); const d = serverRequest.data; try { const data = JSON.parse(d); - it('Should contains all keys', () => { + it('Should contains all keys', function () { expect(data).to.be.an('object'); expect(data).to.have.all.keys('location', 'masked', 'referrer', 'sHeight', 'sWidth', 'timeOffset', 'date', 'wHeight', 'wWidth', 'bids'); expect(data.location).to.be.a('string'); @@ -76,7 +76,7 @@ describe('onetag', () => { } catch (e) { console.log('Error while parsing'); } - it('Returns empty data if no valid requests are passed', () => { + it('Returns empty data if no valid requests are passed', function () { serverRequest = spec.buildRequests([]); let dataString = serverRequest.data; try { @@ -86,8 +86,28 @@ describe('onetag', () => { console.log('Error while parsing'); } }); + it('should send GDPR consent data', function () { + let consentString = 'consentString'; + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + let serverRequest = spec.buildRequests([bid], bidderRequest); + const payload = JSON.parse(serverRequest.data); + + expect(payload).to.exist; + expect(payload.gdprConsent).to.exist; + expect(payload.gdprConsent.consentString).to.exist.and.to.equal(consentString); + expect(payload.gdprConsent.consentRequired).to.exist.and.to.be.true; + }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const resObject = { body: { nobid: false, @@ -103,7 +123,7 @@ describe('onetag', () => { }] } }; - it('Returns an array of valid server responses if response object is valid', () => { + it('Returns an array of valid server responses if response object is valid', function () { const serverResponses = spec.interpretResponse(resObject); expect(serverResponses).to.be.an('array').that.is.not.empty; @@ -120,7 +140,7 @@ describe('onetag', () => { expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); } - it('Returns an empty array if invalid response is passed', () => { + it('Returns an empty array if invalid response is passed', function () { const serverResponses = spec.interpretResponse('invalid_response'); expect(serverResponses).to.be.an('array').that.is.empty; }); diff --git a/test/spec/modules/open8BidAdapter_spec.js b/test/spec/modules/open8BidAdapter_spec.js new file mode 100644 index 00000000000..e26811ed71c --- /dev/null +++ b/test/spec/modules/open8BidAdapter_spec.js @@ -0,0 +1,251 @@ +import { spec } from 'modules/open8BidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//as.vt.open8.com/v1/control/prebid'; + +describe('Open8Adapter', function() { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function() { + let bid = { + 'bidder': 'open8', + 'params': { + 'slotKey': 'slotkey1234' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + 'bidId': 'bidid1234', + 'bidderRequestId': 'requestid1234', + 'auctionId': 'auctionid1234', + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function() { + bid.params = { + ' slotKey': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + let bidRequests = [ + { + 'bidder': 'open8', + 'params': { + 'slotKey': 'slotkey1234' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + 'bidId': 'bidid1234', + 'bidderRequestId': 'requestid1234', + 'auctionId': 'auctionid1234', + } + ]; + + it('sends bid request to ENDPOINT via GET', function() { + const requests = spec.buildRequests(bidRequests); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].method).to.equal('GET'); + }); + }); + describe('interpretResponse', function() { + const bannerResponse = { + slotKey: 'slotkey1234', + userId: 'userid1234', + impId: 'impid1234', + media: 'TEST_MEDIA', + nurl: '//example/win', + isAdReturn: true, + syncPixels: ['//example/sync/pixel.gif'], + syncIFs: [], + ad: { + bidId: 'TEST_BID_ID', + price: 1234.56, + creativeId: 'creativeid1234', + dealId: 'TEST_DEAL_ID', + currency: 'JPY', + ds: 876, + spd: 1234, + fa: 5678, + pr: 'pr1234', + mr: 'mr1234', + nurl: '//example/win', + adType: 2, + banner: { + w: 300, + h: 250, + adm: '
    ', + imps: ['//example.com/imp'] + } + } + }; + const videoResponse = { + slotKey: 'slotkey1234', + userId: 'userid1234', + impId: 'impid1234', + media: 'TEST_MEDIA', + isAdReturn: true, + syncPixels: ['//example/sync/pixel.gif'], + syncIFs: [], + ad: { + bidId: 'TEST_BID_ID', + price: 1234.56, + creativeId: 'creativeid1234', + dealId: 'TEST_DEAL_ID', + currency: 'JPY', + ds: 876, + spd: 1234, + fa: 5678, + pr: 'pr1234', + mr: 'mr1234', + nurl: '//example/win', + adType: 1, + video: { + purl: '//playerexample.js', + vastXml: '', + w: 320, + h: 180 + }, + } + }; + + it('should get correct banner bid response', function() { + let expectedResponse = [{ + 'slotKey': 'slotkey1234', + 'userId': 'userid1234', + 'impId': 'impid1234', + 'media': 'TEST_MEDIA', + 'ds': 876, + 'spd': 1234, + 'fa': 5678, + 'pr': 'pr1234', + 'mr': 'mr1234', + 'nurl': '//example/win', + 'requestId': 'requestid1234', + 'cpm': 1234.56, + 'creativeId': 'creativeid1234', + 'dealId': 'TEST_DEAL_ID', + 'width': 300, + 'height': 250, + 'ad': "
    ", + 'mediaType': 'banner', + 'currency': 'JPY', + 'ttl': 360, + 'netRevenue': true + }]; + + let bidderRequest; + let result = spec.interpretResponse({ body: bannerResponse }, { bidderRequest }); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles video responses', function() { + let expectedResponse = [{ + 'slotKey': 'slotkey1234', + 'userId': 'userid1234', + 'impId': 'impid1234', + 'media': 'TEST_MEDIA', + 'ds': 876, + 'spd': 1234, + 'fa': 5678, + 'pr': 'pr1234', + 'mr': 'mr1234', + 'nurl': '//example/win', + 'requestId': 'requestid1234', + 'cpm': 1234.56, + 'creativeId': 'creativeid1234', + 'dealId': 'TEST_DEAL_ID', + 'width': 320, + 'height': 180, + 'vastXml': '', + 'mediaType': 'video', + 'renderer': {}, + 'adResponse': {}, + 'currency': 'JPY', + 'ttl': 360, + 'netRevenue': true + }]; + + let bidderRequest; + let result = spec.interpretResponse({ body: videoResponse }, { bidderRequest }); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function() { + let response = { + isAdReturn: false, + 'ad': {} + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, { bidderRequest }); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function() { + const imgResponse1 = { + body: { + 'isAdReturn': true, + 'ad': { /* ad body */ }, + 'syncPixels': [ + 'https://example.test/1' + ] + } + }; + + const imgResponse2 = { + body: { + 'isAdReturn': true, + 'ad': { /* ad body */ }, + 'syncPixels': [ + 'https://example.test/2' + ] + } + }; + + const ifResponse = { + body: { + 'isAdReturn': true, + 'ad': { /* ad body */ }, + 'syncIFs': [ + 'https://example.test/3' + ] + } + }; + + it('should use a sync img url from first response', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true }, [imgResponse1, imgResponse2, ifResponse]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://example.test/1' + } + ]); + }); + + it('handle ifs response', function() { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [ifResponse]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://example.test/3' + } + ]); + }); + + it('handle empty response (e.g. timeout)', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true }, []); + expect(syncs).to.deep.equal([]); + }); + + it('returns empty syncs when not enabled', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: false }, [imgResponse1]); + expect(syncs).to.deep.equal([]); + }); + }); +}); diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..88ab3c4c580 --- /dev/null +++ b/test/spec/modules/openxAnalyticsAdapter_spec.js @@ -0,0 +1,449 @@ +import { expect } from 'chai'; +import openxAdapter from 'modules/openxAnalyticsAdapter'; +import { config } from 'src/config'; +import events from 'src/events'; +import CONSTANTS from 'src/constants.json'; +import * as utils from 'src/utils'; + +const { + EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, BID_WON } +} = CONSTANTS; + +const SLOT_LOADED = 'slotOnload'; + +describe('openx analytics adapter', function() { + it('should require publisher id', function() { + sinon.spy(utils, 'logError'); + + openxAdapter.enableAnalytics(); + expect( + utils.logError.calledWith( + 'OpenX analytics adapter: publisherId is required.' + ) + ).to.be.true; + + utils.logError.restore(); + }); + + describe('sending analytics event', function() { + const auctionInit = { auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79' }; + + const bidRequestedOpenX = { + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + auctionStart: 1540944528017, + bids: [ + { + adUnitCode: 'div-1', + bidId: '2f0c647b904e25', + bidder: 'openx', + params: { unit: '540249866' }, + transactionId: 'ac66c3e6-3118-4213-a3ae-8cdbe4f72873' + } + ], + start: 1540944528021 + }; + + const bidRequestedCloseX = { + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + auctionStart: 1540944528017, + bids: [ + { + adUnitCode: 'div-1', + bidId: '43d454020e9409', + bidder: 'closex', + params: { unit: '513144370' }, + transactionId: 'ac66c3e6-3118-4213-a3ae-8cdbe4f72873' + } + ], + start: 1540944528026 + }; + + const bidResponseOpenX = { + requestId: '2f0c647b904e25', + adId: '33dddbb61d359a', + adUnitCode: 'div-1', + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + cpm: 0.5, + creativeId: 'openx-crid', + responseTimestamp: 1540944528184, + ts: '2DAABBgABAAECAAIBAAsAAgAAAJccGApKSGt6NUZxRXYyHBbinsLj' + }; + + const bidResponseCloseX = { + requestId: '43d454020e9409', + adId: '43dddbb61d359a', + adUnitCode: 'div-1', + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + cpm: 0.3, + creativeId: 'closex-crid', + responseTimestamp: 1540944528196, + ts: 'hu1QWo6iD3MHs6NG_AQAcFtyNqsj9y4S0YRbX7Kb06IrGns0BABb' + }; + + const bidTimeoutOpenX = { + 0: { + adUnitCode: 'div-1', + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + bidId: '2f0c647b904e25' + } + }; + + const bidTimeoutCloseX = { + 0: { + adUnitCode: 'div-1', + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + bidId: '43d454020e9409' + } + }; + + const bidWonOpenX = { + requestId: '2f0c647b904e25', + adId: '33dddbb61d359a', + adUnitCode: 'div-1', + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79' + }; + + const bidWonCloseX = { + requestId: '43d454020e9409', + adId: '43dddbb61d359a', + adUnitCode: 'div-1', + auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79' + }; + + function simulateAuction(events) { + let highestBid; + + events.forEach(event => { + const [eventType, args] = event; + openxAdapter.track({ eventType, args }); + if (eventType === BID_RESPONSE) { + highestBid = highestBid || args; + if (highestBid.cpm < args.cpm) { + highestBid = args; + } + } + }); + + openxAdapter.track({ + eventType: SLOT_LOADED, + args: { + slot: { + getAdUnitPath: () => { + return '/90577858/test_ad_unit'; + }, + getTargetingKeys: () => { + return []; + }, + getTargeting: sinon + .stub() + .withArgs('hb_adid') + .returns(highestBid ? [highestBid.adId] : []) + } + } + }); + } + + function getQueryData(url) { + const queryArgs = url.split('?')[1].split('&'); + return queryArgs.reduce((data, arg) => { + const [key, val] = arg.split('='); + data[key] = val; + return data; + }, {}); + } + + let xhr; + let requests; + + before(function() { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + sinon.stub(events, 'getEvents').returns([]); + openxAdapter.enableAnalytics({ + options: { + publisherId: 'test123' + } + }); + }); + + after(function() { + xhr.restore(); + events.getEvents.restore(); + openxAdapter.disableAnalytics(); + }); + + beforeEach(function() { + requests = []; + openxAdapter.reset(); + }); + + afterEach(function() {}); + + it('should not send request if no bid response', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX] + ]); + + expect(requests.length).to.equal(0); + }); + + it('should send 1 request to the right endpoint', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX] + ]); + + expect(requests.length).to.equal(1); + + const endpoint = requests[0].url.split('?')[0]; + expect(endpoint).to.equal('http://ads.openx.net/w/1.0/pban'); + }); + + describe('hb.ct, hb.rid, dddid, hb.asiid, hb.pubid', function() { + it('should always be in the query string', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX] + ]); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.include({ + 'hb.ct': String(bidRequestedOpenX.auctionStart), + 'hb.rid': auctionInit.auctionId, + dddid: bidRequestedOpenX.bids[0].transactionId, + 'hb.asiid': '/90577858/test_ad_unit', + 'hb.pubid': 'test123' + }); + }); + }); + + describe('hb.cur', function() { + it('should be in the query string if currency is set', function() { + sinon + .stub(config, 'getConfig') + .withArgs('currency.adServerCurrency') + .returns('bitcoin'); + + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX] + ]); + + config.getConfig.restore(); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.include({ + 'hb.cur': 'bitcoin' + }); + }); + + it('should not be in the query string if currency is not set', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX] + ]); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.not.have.key('hb.cur'); + }); + }); + + describe('hb.dcl, hb.dl, hb.tta, hb.ttr', function() { + it('should be in the query string if browser supports performance API', function() { + const timing = { + fetchStart: 1540944528000, + domContentLoadedEventEnd: 1540944528010, + loadEventEnd: 1540944528110 + }; + const originalPerf = window.top.performance; + window.top.performance = { timing }; + + const renderTime = 1540944528100; + sinon.stub(Date, 'now').returns(renderTime); + + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX] + ]); + + window.top.performance = originalPerf; + Date.now.restore(); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.include({ + 'hb.dcl': String(timing.domContentLoadedEventEnd - timing.fetchStart), + 'hb.dl': String(timing.loadEventEnd - timing.fetchStart), + 'hb.tta': String(bidRequestedOpenX.auctionStart - timing.fetchStart), + 'hb.ttr': String(renderTime - timing.fetchStart) + }); + }); + + it('should not be in the query string if browser does not support performance API', function() { + const originalPerf = window.top.performance; + window.top.performance = undefined; + + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX] + ]); + + window.top.performance = originalPerf; + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.not.have.keys( + 'hb.dcl', + 'hb.dl', + 'hb.tta', + 'hb.ttr' + ); + }); + }); + + describe('ts, auid', function() { + it('OpenX is in auction and has a bid response', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_REQUESTED, bidRequestedCloseX], + [BID_RESPONSE, bidResponseOpenX], + [BID_RESPONSE, bidResponseCloseX] + ]); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.include({ + ts: bidResponseOpenX.ts, + auid: bidRequestedOpenX.bids[0].params.unit + }); + }); + + it('OpenX is in auction but no bid response', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_REQUESTED, bidRequestedCloseX], + [BID_RESPONSE, bidResponseCloseX] + ]); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.include({ + auid: bidRequestedOpenX.bids[0].params.unit + }); + expect(queryData).to.not.have.key('ts'); + }); + + it('OpenX is not in auction', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedCloseX], + [BID_RESPONSE, bidResponseCloseX] + ]); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.not.have.keys('auid', 'ts'); + }); + }); + + describe('hb.exn, hb.sts, hb.ets, hb.bv, hb.crid, hb.to', function() { + it('2 bidders in auction', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_REQUESTED, bidRequestedCloseX], + [BID_RESPONSE, bidResponseOpenX], + [BID_RESPONSE, bidResponseCloseX] + ]); + + const queryData = getQueryData(requests[0].url); + const auctionStart = bidRequestedOpenX.auctionStart; + expect(queryData).to.include({ + 'hb.exn': [ + bidRequestedOpenX.bids[0].bidder, + bidRequestedCloseX.bids[0].bidder + ].join(','), + 'hb.sts': [ + bidRequestedOpenX.start - auctionStart, + bidRequestedCloseX.start - auctionStart + ].join(','), + 'hb.ets': [ + bidResponseOpenX.responseTimestamp - auctionStart, + bidResponseCloseX.responseTimestamp - auctionStart + ].join(','), + 'hb.bv': [bidResponseOpenX.cpm, bidResponseCloseX.cpm].join(','), + 'hb.crid': [ + bidResponseOpenX.creativeId, + bidResponseCloseX.creativeId + ].join(','), + 'hb.to': [false, false].join(',') + }); + }); + + it('OpenX timed out', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_REQUESTED, bidRequestedCloseX], + [BID_RESPONSE, bidResponseCloseX], + [BID_TIMEOUT, bidTimeoutOpenX] + ]); + + const queryData = getQueryData(requests[0].url); + const auctionStart = bidRequestedOpenX.auctionStart; + expect(queryData).to.include({ + 'hb.exn': [ + bidRequestedOpenX.bids[0].bidder, + bidRequestedCloseX.bids[0].bidder + ].join(','), + 'hb.sts': [ + bidRequestedOpenX.start - auctionStart, + bidRequestedCloseX.start - auctionStart + ].join(','), + 'hb.ets': [ + undefined, + bidResponseCloseX.responseTimestamp - auctionStart + ].join(','), + 'hb.bv': [0, bidResponseCloseX.cpm].join(','), + 'hb.crid': [undefined, bidResponseCloseX.creativeId].join(','), + 'hb.to': [true, false].join(',') + }); + }); + }); + + describe('hb.we, hb.g1', function() { + it('OpenX won', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX], + [BID_WON, bidWonOpenX] + ]); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.include({ + 'hb.we': '0', + 'hb.g1': 'false' + }); + }); + + it('DFP won', function() { + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_RESPONSE, bidResponseOpenX] + ]); + + const queryData = getQueryData(requests[0].url); + expect(queryData).to.include({ + 'hb.we': '-1', + 'hb.g1': 'true' + }); + }); + }); + }); +}); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 3585987e045..0002d25c37d 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -8,7 +8,7 @@ import * as utils from 'src/utils'; const URLBASE = '/w/1.0/arj'; const URLBASEVIDEO = '/v/1.0/avjp'; -describe('OpenxAdapter', () => { +describe('OpenxAdapter', function () { const adapter = newBidder(spec); /** @@ -116,107 +116,202 @@ describe('OpenxAdapter', () => { ads: { version: 0, count: 1, - pixels: 'http://testpixels.net', + pixels: 'https://testpixels.net', ad: [DEFAULT_TEST_ARJ_AD_UNIT] } }; - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - describe('when request is for a banner ad', () => { - const bannerBid = { - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: {banner: {}}, - sizes: [[300, 250], [300, 600]], - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475' - }; + describe('isBidRequestValid', function () { + describe('when request is for a banner ad', function () { + let bannerBid; + beforeEach(function () { + bannerBid = { + bidder: 'openx', + params: {}, + adUnitCode: 'adunit-code', + mediaTypes: {banner: {}}, + sizes: [[300, 250], [300, 600]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + }; + }); - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bannerBid)).to.equal(true); + it('should return false when there is no delivery domain', function () { + bannerBid.params = {'unit': '12345678'}; + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); }); - it('should return false when there is no delivery domain', () => { - let bid = Object.assign({}, bannerBid); - bid.params = {'unit': '12345678'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); + describe('when there is a delivery domain', function () { + beforeEach(function () { + bannerBid.params = {delDomain: 'test-delivery-domain'} + }); + + it('should return false when there is no ad unit id and size', function () { + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); + }); + + it('should return true if there is an adunit id ', function () { + bannerBid.params.unit = '12345678'; + expect(spec.isBidRequestValid(bannerBid)).to.equal(true); + }); + + it('should return true if there is no adunit id and sizes are defined', function () { + bannerBid.mediaTypes.banner.sizes = [720, 90]; + expect(spec.isBidRequestValid(bannerBid)).to.equal(true); + }); + + it('should return false if no sizes are defined ', function () { + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); + }); + + it('should return false if sizes empty ', function () { + bannerBid.mediaTypes.banner.sizes = []; + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); + }); }); + }); - it('should return false when there is no ad unit id ', () => { - let bid = Object.assign({}, bannerBid); - bid.params = {delDomain: 'test-del-domain'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); + describe('when request is for a multiformat ad', function () { + describe('and request config uses mediaTypes video and banner', () => { + const multiformatBid = { + bidder: 'openx', + params: { + unit: '12345678', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250]] + }, + video: { + playerSize: [300, 250] + } + }, + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' + }; + it('should return true multisize when required params found', function () { + expect(spec.isBidRequestValid(multiformatBid)).to.equal(true); + }); }); }); describe('when request is for a video ad', function () { - const videoBidWithMediaTypes = { - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - - const videoBidWithMediaType = { - 'bidder': 'openx', - 'params': { - 'unit': '12345678', - 'delDomain': 'test-del-domain' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': 'video', - 'sizes': [640, 480], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(true); - }); + describe('and request config uses mediaTypes', () => { + const videoBidWithMediaTypes = { + bidder: 'openx', + params: { + unit: '12345678', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + video: { + playerSize: [640, 480] + } + }, + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' + }; + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(true); + }); - it('should return false when required params are not passed', () => { - let videoBidWithMediaTypes = Object.assign({}, videoBidWithMediaTypes); - videoBidWithMediaTypes.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); + it('should return false when required params are not passed', function () { + let videoBidWithMediaTypes = Object.assign({}, videoBidWithMediaTypes); + videoBidWithMediaTypes.params = {}; + expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); + }); + it('should send bid request to openx url via GET, with mediaType specified as video', function () { + const request = spec.buildRequests([videoBidWithMediaTypes]); + expect(request[0].url).to.equal(`https://${videoBidWithMediaTypes.params.delDomain}${URLBASEVIDEO}`); + expect(request[0].data.ph).to.be.undefined; + expect(request[0].method).to.equal('GET'); + }); }); + describe('and request config uses both delDomain and platform', () => { + const videoBidWithDelDomainAndPlatform = { + bidder: 'openx', + params: { + unit: '12345678', + delDomain: 'test-del-domain', + platform: '1cabba9e-cafe-3665-beef-f00f00f00f00' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + video: { + playerSize: [640, 480] + } + }, + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' + }; + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(videoBidWithDelDomainAndPlatform)).to.equal(true); + }); - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(true); + it('should return false when required params are not passed', function () { + let videoBidWithMediaTypes = Object.assign({}, videoBidWithDelDomainAndPlatform); + videoBidWithMediaTypes.params = {}; + expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); + }); + it('should send bid request to openx url via GET, with mediaType specified as video', function () { + const request = spec.buildRequests([videoBidWithDelDomainAndPlatform]); + expect(request[0].url).to.equal(`https://u.openx.net${URLBASEVIDEO}`); + expect(request[0].data.ph).to.equal(videoBidWithDelDomainAndPlatform.params.platform); + expect(request[0].method).to.equal('GET'); + }); }); + describe('and request config uses mediaType', () => { + const videoBidWithMediaType = { + 'bidder': 'openx', + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + 'mediaType': 'video', + 'sizes': [640, 480], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' + }; + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(true); + }); - it('should return false when required params are not passed', () => { - let videoBidWithMediaType = Object.assign({}, videoBidWithMediaType); - delete videoBidWithMediaType.params; - videoBidWithMediaType.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(false); + it('should return false when required params are not passed', function () { + let videoBidWithMediaType = Object.assign({}, videoBidWithMediaType); + delete videoBidWithMediaType.params; + videoBidWithMediaType.params = {}; + expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(false); + }); + it('should send bid request to openx url via GET, with mediaType specified as video', function () { + const request = spec.buildRequests([videoBidWithMediaType]); + expect(request[0].url).to.equal(`https://${videoBidWithMediaType.params.delDomain}${URLBASEVIDEO}`); + expect(request[0].data.ph).to.be.undefined; + expect(request[0].method).to.equal('GET'); + }); }); }); }); - describe('buildRequests for banner ads', () => { + describe('buildRequests for banner ads', function () { const bidRequestsWithMediaType = [{ 'bidder': 'openx', 'params': { @@ -233,32 +328,202 @@ describe('OpenxAdapter', () => { const bidRequestsWithMediaTypes = [{ 'bidder': 'openx', 'params': { - 'unit': '12345678', + 'unit': '11', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': '/adunit-code/test-path', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' + }, { + 'bidder': 'openx', + 'params': { + 'unit': '22', 'delDomain': 'test-del-domain' }, 'adUnitCode': 'adunit-code', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + 'bidId': 'test-bid-id-2', + 'bidderRequestId': 'test-bid-request-2', + 'auctionId': 'test-auction-2' + }]; + const bidRequestsWithPlatform = [{ + 'bidder': 'openx', + 'params': { + 'unit': '11', + 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' + }, + 'adUnitCode': '/adunit-code/test-path', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' + }, { + 'bidder': 'openx', + 'params': { + 'unit': '11', + 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' + }, + 'adUnitCode': '/adunit-code/test-path', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' }]; - it('should send bid request to openx url via GET, with mediaType specified as banner', () => { + it('should send bid request to openx url via GET, with mediaType specified as banner', function () { const request = spec.buildRequests(bidRequestsWithMediaType); - expect(request[0].url).to.equal('//' + bidRequestsWithMediaType[0].params.delDomain + URLBASE); + expect(request[0].url).to.equal('https://' + bidRequestsWithMediaType[0].params.delDomain + URLBASE); + expect(request[0].data.ph).to.be.undefined; expect(request[0].method).to.equal('GET'); }); - it('should send bid request to openx url via GET, with mediaTypes specified with banner type', () => { + it('should send bid request to openx url via GET, with mediaTypes specified with banner type', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes); - expect(request[0].url).to.equal('//' + bidRequestsWithMediaTypes[0].params.delDomain + URLBASE); + expect(request[0].url).to.equal('https://' + bidRequestsWithMediaTypes[0].params.delDomain + URLBASE); + expect(request[0].data.ph).to.be.undefined; + expect(request[0].method).to.equal('GET'); + }); + + it('should send bid request to openx platform url via GET, if platform is present', function () { + const request = spec.buildRequests(bidRequestsWithPlatform); + expect(request[0].url).to.equal(`https://u.openx.net${URLBASE}`); + expect(request[0].data.ph).to.equal(bidRequestsWithPlatform[0].params.platform); expect(request[0].method).to.equal('GET'); }); + it('should send bid request to openx platform url via GET, if both params present', function () { + const bidRequestsWithPlatformAndDelDomain = [{ + 'bidder': 'openx', + 'params': { + 'unit': '11', + 'delDomain': 'test-del-domain', + 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' + }, + 'adUnitCode': '/adunit-code/test-path', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' + }, { + 'bidder': 'openx', + 'params': { + 'unit': '11', + 'delDomain': 'test-del-domain', + 'platform': '1cabba9e-cafe-3665-beef-f00f00f00f00' + }, + 'adUnitCode': '/adunit-code/test-path', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' + }]; + + const request = spec.buildRequests(bidRequestsWithPlatformAndDelDomain); + expect(request[0].url).to.equal(`https://u.openx.net${URLBASE}`); + expect(request[0].data.ph).to.equal(bidRequestsWithPlatform[0].params.platform); + expect(request[0].method).to.equal('GET'); + }); + + it('should send the adunit codes', function () { + const request = spec.buildRequests(bidRequestsWithMediaTypes); + expect(request[0].data.divIds).to.equal(`${encodeURIComponent(bidRequestsWithMediaTypes[0].adUnitCode)},${encodeURIComponent(bidRequestsWithMediaTypes[1].adUnitCode)}`); + }); + + it('should send ad unit ids when any are defined', function () { + const bidRequestsWithUnitIds = [{ + 'bidder': 'openx', + 'params': { + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' + }, { + 'bidder': 'openx', + 'params': { + 'unit': '22', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + 'bidId': 'test-bid-id-2', + 'bidderRequestId': 'test-bid-request-2', + 'auctionId': 'test-auction-2' + }]; + const request = spec.buildRequests(bidRequestsWithUnitIds); + expect(request[0].data.auid).to.equal(`,${bidRequestsWithUnitIds[1].params.unit}`); + }); + + it('should not send any ad unit ids when none are defined', function () { + const bidRequestsWithoutUnitIds = [{ + 'bidder': 'openx', + 'params': { + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' + }, { + 'bidder': 'openx', + 'params': { + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + 'bidId': 'test-bid-id-2', + 'bidderRequestId': 'test-bid-request-2', + 'auctionId': 'test-auction-2' + }]; + const request = spec.buildRequests(bidRequestsWithoutUnitIds); + expect(request[0].data).to.not.have.any.keys('auid'); + }); + describe('when there is a legacy request with no media type', function () { const deprecatedBidRequestsFormatWithNoMediaType = [{ 'bidder': 'openx', @@ -279,7 +544,7 @@ describe('OpenxAdapter', () => { requestData = spec.buildRequests(deprecatedBidRequestsFormatWithNoMediaType)[0].data; }); - it('should have an ad unit id', () => { + it('should have an ad unit id', function () { expect(requestData.auid).to.equal('12345678'); }); @@ -288,7 +553,7 @@ describe('OpenxAdapter', () => { }); }); - it('should send out custom params on bids that have customParams specified', () => { + it('should send out custom params on bids that have customParams specified', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], { @@ -307,14 +572,14 @@ describe('OpenxAdapter', () => { expect(dataParams.tps).to.equal(btoa('test1=testval1.&test2=testval2_,testval3')); }); - it('should send out custom floors on bids that have customFloors specified', () => { + it('should send out custom floors on bids that have customFloors specified', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], { params: { 'unit': '12345678', 'delDomain': 'test-del-domain', - 'customFloor': 1.5 + 'customFloor': 1.500001 } } ); @@ -326,7 +591,7 @@ describe('OpenxAdapter', () => { expect(dataParams.aumfs).to.equal('1500'); }); - it('should send out custom bc parameter, if override is present', () => { + it('should send out custom bc parameter, if override is present', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], { @@ -352,13 +617,13 @@ describe('OpenxAdapter', () => { expect(request[0].data.x_gdpr_f).to.equal(undefined); }); - describe('when there is a consent management framework', () => { + describe('when there is a consent management framework', function () { let bidRequests; let mockConfig; let bidderRequest; const IAB_CONSENT_FRAMEWORK_CODE = 1; - beforeEach(() => { + beforeEach(function () { bidRequests = [{ bidder: 'openx', params: { @@ -524,9 +789,386 @@ describe('OpenxAdapter', () => { }); }); }); + + it('should not send a coppa query param when there are no coppa param settings in the bid requests', function () { + const bidRequestsWithoutCoppa = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain', + coppa: false + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }, { + bidder: 'openx', + params: { + unit: '22', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + bidId: 'test-bid-id-2', + bidderRequestId: 'test-bid-request-2', + auctionId: 'test-auction-2' + }]; + const request = spec.buildRequests(bidRequestsWithoutCoppa); + expect(request[0].data).to.not.have.any.keys('tfcd'); + }); + + it('should send a coppa flag there is when there is coppa param settings in the bid requests', function () { + const bidRequestsWithCoppa = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain', + coppa: false + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }, { + bidder: 'openx', + params: { + unit: '22', + delDomain: 'test-del-domain', + coppa: true + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + bidId: 'test-bid-id-2', + bidderRequestId: 'test-bid-request-2', + auctionId: 'test-auction-2' + }]; + const request = spec.buildRequests(bidRequestsWithCoppa); + expect(request[0].data.tfcd).to.equal(1); + }); + + it('should not send a "no segmentation" flag there no DoNotTrack setting that is set to true', function () { + const bidRequestsWithoutDnt = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain', + doNotTrack: false + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }, { + bidder: 'openx', + params: { + unit: '22', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + bidId: 'test-bid-id-2', + bidderRequestId: 'test-bid-request-2', + auctionId: 'test-auction-2' + }]; + const request = spec.buildRequests(bidRequestsWithoutDnt); + expect(request[0].data).to.not.have.any.keys('ns'); + }); + + it('should send a "no segmentation" flag there is any DoNotTrack setting that is set to true', function () { + const bidRequestsWithDnt = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain', + doNotTrack: false + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }, { + bidder: 'openx', + params: { + unit: '22', + delDomain: 'test-del-domain', + doNotTrack: true + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + bidId: 'test-bid-id-2', + bidderRequestId: 'test-bid-request-2', + auctionId: 'test-auction-2' + }]; + const request = spec.buildRequests(bidRequestsWithDnt); + expect(request[0].data.ns).to.equal(1); + }); + + describe('when schain is provided', function () { + let bidRequests; + let schainConfig; + const supplyChainNodePropertyOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain']; + + beforeEach(function () { + schainConfig = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + // omitted ext + }, + { + 'asi': 'exchange2.com', + 'sid': 'abcd', + 'hp': 1, + 'rid': 'bid-request-2', + // name field missing + 'domain': 'intermediary.com' + }, + { + 'asi': 'exchange3.com', + 'sid': '4321', + 'hp': 1, + // request id + // name field missing + 'domain': 'intermediary-2.com' + } + ] + }; + + bidRequests = [{ + 'bidder': 'openx', + 'params': { + 'unit': '11', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': '/adunit-code/test-path', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1', + 'schain': schainConfig + }]; + }); + + it('should send a schain parameter with the proper delimiter symbols', function () { + const request = spec.buildRequests(bidRequests); + const dataParams = request[0].data; + const numNodes = schainConfig.nodes.length; + + // each node will have a ! to denote beginning of a new node + expect(dataParams.schain.match(/!/g).length).to.equal(numNodes); + + // 1 comma in the front for version + // 5 commas per node + expect(dataParams.schain.match(/,/g).length).to.equal(numNodes * 5 + 1); + }); + + it('should send a schain with the right version', function () { + const request = spec.buildRequests(bidRequests); + const dataParams = request[0].data; + let serializedSupplyChain = dataParams.schain.split('!'); + let version = serializedSupplyChain.shift().split(',')[0]; + + expect(version).to.equal(bidRequests[0].schain.ver); + }); + + it('should send a schain with the right complete value', function () { + const request = spec.buildRequests(bidRequests); + const dataParams = request[0].data; + let serializedSupplyChain = dataParams.schain.split('!'); + let isComplete = serializedSupplyChain.shift().split(',')[1]; + + expect(isComplete).to.equal(String(bidRequests[0].schain.complete)); + }); + + it('should send all available params in the right order', function () { + const request = spec.buildRequests(bidRequests); + const dataParams = request[0].data; + let serializedSupplyChain = dataParams.schain.split('!'); + serializedSupplyChain.shift(); + + serializedSupplyChain.forEach((serializedNode, nodeIndex) => { + let nodeProperties = serializedNode.split(','); + + nodeProperties.forEach((nodeProperty, propertyIndex) => { + let node = schainConfig.nodes[nodeIndex]; + let key = supplyChainNodePropertyOrder[propertyIndex]; + + expect(nodeProperty).to.equal(node[key] ? String(node[key]) : '', + `expected node '${nodeIndex}' property '${nodeProperty}' to key '${key}' to be the same value`) + }); + }); + }); + }); + + describe('when there are userid providers', function () { + describe('with publisher common id', function () { + it('should not send a pubcid query param when there is no crumbs.pubcid and no userId.pubcid defined in the bid requests', function () { + const request = spec.buildRequests(bidRequestsWithMediaType); + expect(request[0].data).to.not.have.any.keys('pubcid'); + }); + + it('should send a pubcid query param when crumbs.pubcid is defined in the bid requests', function () { + const bidRequestsWithPubcid = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain' + }, + crumbs: { + pubcid: 'c4a4c843-2368-4b5e-b3b1-6ee4702b9ad6' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }]; + const request = spec.buildRequests(bidRequestsWithPubcid); + expect(request[0].data.pubcid).to.equal('c4a4c843-2368-4b5e-b3b1-6ee4702b9ad6'); + }); + + it('should send a pubcid query param when userId.pubcid is defined in the bid requests', function () { + const bidRequestsWithPubcid = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain' + }, + userId: { + pubcid: 'c1a4c843-2368-4b5e-b3b1-6ee4702b9ad6' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }]; + const request = spec.buildRequests(bidRequestsWithPubcid); + expect(request[0].data.pubcid).to.equal('c1a4c843-2368-4b5e-b3b1-6ee4702b9ad6'); + }); + }); + + describe('with the trade desk unified id', function () { + it('should not send a tdid query param when there is no userId.tdid defined in the bid requests', function () { + const request = spec.buildRequests(bidRequestsWithMediaType); + expect(request[0].data).to.not.have.any.keys('ttduuid'); + }); + + it('should send a tdid query param when userId.tdid is defined in the bid requests', function () { + const bidRequestsWithTdid = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain' + }, + userId: { + tdid: '00000000-aaaa-1111-bbbb-222222222222' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }]; + const request = spec.buildRequests(bidRequestsWithTdid); + expect(request[0].data.ttduuid).to.equal('00000000-aaaa-1111-bbbb-222222222222'); + }); + }); + + describe('with the liveRamp identity link envelope', function () { + it('should not send a tdid query param when there is no userId.lre defined in the bid requests', function () { + const request = spec.buildRequests(bidRequestsWithMediaType); + expect(request[0].data).to.not.have.any.keys('lre'); + }); + + it('should send a lre query param when userId.lre is defined in the bid requests', function () { + const bidRequestsWithLiveRampEnvelope = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain' + }, + userId: { + idl_env: '00000000-aaaa-1111-bbbb-222222222222' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }]; + const request = spec.buildRequests(bidRequestsWithLiveRampEnvelope); + expect(request[0].data.lre).to.equal('00000000-aaaa-1111-bbbb-222222222222'); + }); + }); + }); }); - describe('buildRequests for video', () => { + describe('buildRequests for video', function () { const bidRequestsWithMediaTypes = [{ 'bidder': 'openx', 'mediaTypes': { @@ -561,19 +1203,19 @@ describe('OpenxAdapter', () => { 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' }]; - it('should send bid request to openx url via GET, with mediaType as video', () => { + it('should send bid request to openx url via GET, with mediaType as video', function () { const request = spec.buildRequests(bidRequestsWithMediaType); - expect(request[0].url).to.equal('//' + bidRequestsWithMediaType[0].params.delDomain + URLBASEVIDEO); + expect(request[0].url).to.equal('https://' + bidRequestsWithMediaType[0].params.delDomain + URLBASEVIDEO); expect(request[0].method).to.equal('GET'); }); - it('should send bid request to openx url via GET, with mediaTypes having video parameter', () => { + it('should send bid request to openx url via GET, with mediaTypes having video parameter', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes); - expect(request[0].url).to.equal('//' + bidRequestsWithMediaTypes[0].params.delDomain + URLBASEVIDEO); + expect(request[0].url).to.equal('https://' + bidRequestsWithMediaTypes[0].params.delDomain + URLBASEVIDEO); expect(request[0].method).to.equal('GET'); }); - it('should have the correct parameters', () => { + it('should have the correct parameters', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes); const dataParams = request[0].data; @@ -582,6 +1224,13 @@ describe('OpenxAdapter', () => { expect(dataParams.vwd).to.equal(640); }); + it('should send a bc parameter', function () { + const request = spec.buildRequests(bidRequestsWithMediaTypes); + const dataParams = request[0].data; + + expect(dataParams.bc).to.have.string('hb_pb'); + }); + describe('when using the video param', function () { let videoBidRequest; @@ -669,8 +1318,36 @@ describe('OpenxAdapter', () => { }); }); - describe('interpretResponse for banner ads', () => { - beforeEach(() => { + describe('buildRequest for multi-format ad', function () { + const multiformatBid = { + bidder: 'openx', + params: { + unit: '12345678', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250]] + }, + video: { + playerSize: [300, 250] + } + }, + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' + }; + + it('should send bid request to openx url via GET, with mediaType specified as banner', function () { + const request = spec.buildRequests([multiformatBid]); + expect(request[0].url).to.equal(`https://${multiformatBid.params.delDomain}${URLBASE}`); + }); + }); + + describe('interpretResponse for banner ads', function () { + beforeEach(function () { sinon.spy(userSync, 'registerSync'); }); @@ -684,7 +1361,7 @@ describe('OpenxAdapter', () => { width: '300', height: '250', tracking: { - impression: 'http://openx-d.openx.net/v/1.0/ri?ts=ts' + impression: 'https://openx-d.openx.net/v/1.0/ri?ts=ts' } }; @@ -719,7 +1396,7 @@ describe('OpenxAdapter', () => { bidRequest = { method: 'GET', - url: '//openx-d.openx.net/v/1.0/arj', + url: 'https://openx-d.openx.net/v/1.0/arj', data: {}, payload: {'bids': bidRequestConfigs, 'startTime': new Date()} }; @@ -766,10 +1443,18 @@ describe('OpenxAdapter', () => { expect(bid.ts).to.equal(adUnitOverride.ts); }); - it('should register a beacon', () => { + it('should return a brand ID', function () { + expect(bid.meta.brandId).to.equal(DEFAULT_TEST_ARJ_AD_UNIT.brand_id); + }); + + it('should return a brand ID', function () { + expect(bid.meta.dspid).to.equal(DEFAULT_TEST_ARJ_AD_UNIT.adv_id); + }); + + it('should register a beacon', function () { resetBoPixel(); spec.interpretResponse({body: bidResponse}, bidRequest); - sinon.assert.calledWith(userSync.registerSync, 'image', 'openx', sinon.match(new RegExp(`\/\/openx-d\.openx\.net.*\/bo\?.*ts=${adUnitOverride.ts}`))); + sinon.assert.calledWith(userSync.registerSync, 'image', 'openx', sinon.match(new RegExp(`https:\/\/openx-d\.openx\.net.*\/bo\?.*ts=${adUnitOverride.ts}`))); }); }); @@ -801,7 +1486,7 @@ describe('OpenxAdapter', () => { bidRequest = { method: 'GET', - url: '//openx-d.openx.net/v/1.0/arj', + url: 'https://openx-d.openx.net/v/1.0/arj', data: {}, payload: {'bids': bidRequestConfigs, 'startTime': new Date()} }; @@ -837,19 +1522,19 @@ describe('OpenxAdapter', () => { bidRequest = { method: 'GET', - url: '//openx-d.openx.net/v/1.0/arj', + url: 'https://openx-d.openx.net/v/1.0/arj', data: {}, payload: {'bids': bidRequestConfigs, 'startTime': new Date()} }; }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { const bidResponse = { 'ads': { 'version': 1, 'count': 1, - 'pixels': 'http://testpixels.net', + 'pixels': 'https://testpixels.net', 'ad': [] } }; @@ -908,7 +1593,7 @@ describe('OpenxAdapter', () => { }]; const bidRequest = { method: 'GET', - url: '//openx-d.openx.net/v/1.0/arj', + url: 'https://openx-d.openx.net/v/1.0/arj', data: {}, payload: {'bids': bidRequests, 'startTime': new Date()} }; @@ -951,8 +1636,8 @@ describe('OpenxAdapter', () => { }); }); - describe('interpretResponse for video ads', () => { - beforeEach(() => { + describe('interpretResponse for video ads', function () { + beforeEach(function () { sinon.spy(userSync, 'registerSync'); }); @@ -990,13 +1675,13 @@ describe('OpenxAdapter', () => { }]; const bidRequestsWithMediaTypes = { method: 'GET', - url: '//openx-d.openx.net/v/1.0/avjp', + url: 'https://openx-d.openx.net/v/1.0/avjp', data: {}, payload: {'bid': bidsWithMediaTypes[0], 'startTime': new Date()} }; const bidRequestsWithMediaType = { method: 'GET', - url: '//openx-d.openx.net/v/1.0/avjp', + url: 'https://openx-d.openx.net/v/1.0/avjp', data: {}, payload: {'bid': bidsWithMediaType[0], 'startTime': new Date()} }; @@ -1005,21 +1690,20 @@ describe('OpenxAdapter', () => { 'width': '640', 'height': '480', 'adid': '5678', - 'vastUrl': 'http://testvast.com/vastpath?colo=http://test-colo.com&ph=test-ph&ts=test-ts', - 'pixels': 'http://testpixels.net' + 'vastUrl': 'https://testvast.com/vastpath?colo=https://test-colo.com&ph=test-ph&ts=test-ts', + 'pixels': 'https://testpixels.net' }; - it('should return correct bid response with MediaTypes', () => { + it('should return correct bid response with MediaTypes', function () { const expectedResponse = [ { 'requestId': '30b31c1838de1e', - 'bidderCode': 'openx', 'cpm': 1, 'width': '640', 'height': '480', 'mediaType': 'video', 'creativeId': '5678', - 'vastUrl': 'http://testvast.com', + 'vastUrl': 'https://testvast.com', 'ttl': 300, 'netRevenue': true, 'currency': 'USD' @@ -1030,17 +1714,16 @@ describe('OpenxAdapter', () => { expect(JSON.stringify(Object.keys(result[0]).sort())).to.eql(JSON.stringify(Object.keys(expectedResponse[0]).sort())); }); - it('should return correct bid response with MediaType', () => { + it('should return correct bid response with MediaType', function () { const expectedResponse = [ { 'requestId': '30b31c1838de1e', - 'bidderCode': 'openx', 'cpm': 1, 'width': '640', 'height': '480', 'mediaType': 'video', 'creativeId': '5678', - 'vastUrl': 'http://testvast.com', + 'vastUrl': 'https://testvast.com', 'ttl': 300, 'netRevenue': true, 'currency': 'USD' @@ -1051,52 +1734,88 @@ describe('OpenxAdapter', () => { expect(JSON.stringify(Object.keys(result[0]).sort())).to.eql(JSON.stringify(Object.keys(expectedResponse[0]).sort())); }); - it('should handle nobid responses for bidRequests with MediaTypes', () => { + it('should handle nobid responses for bidRequests with MediaTypes', function () { const bidResponse = {'vastUrl': '', 'pub_rev': '', 'width': '', 'height': '', 'adid': '', 'pixels': ''}; const result = spec.interpretResponse({body: bidResponse}, bidRequestsWithMediaTypes); expect(result.length).to.equal(0); }); - it('should handle nobid responses for bidRequests with MediaType', () => { + it('should handle nobid responses for bidRequests with MediaType', function () { const bidResponse = {'vastUrl': '', 'pub_rev': '', 'width': '', 'height': '', 'adid': '', 'pixels': ''}; const result = spec.interpretResponse({body: bidResponse}, bidRequestsWithMediaType); expect(result.length).to.equal(0); }); - it('should register a beacon', () => { + it('should register a beacon', function () { resetBoPixel(); spec.interpretResponse({body: bidResponse}, bidRequestsWithMediaTypes); - sinon.assert.calledWith(userSync.registerSync, 'image', 'openx', sinon.match(/^\/\/test-colo\.com/)) + sinon.assert.calledWith(userSync.registerSync, 'image', 'openx', sinon.match(/^https:\/\/test-colo\.com/)); sinon.assert.calledWith(userSync.registerSync, 'image', 'openx', sinon.match(/ph=test-ph/)); sinon.assert.calledWith(userSync.registerSync, 'image', 'openx', sinon.match(/ts=test-ts/)); }); }); - describe('user sync', () => { - const syncUrl = 'http://testpixels.net'; + describe('user sync', function () { + const syncUrl = 'https://testpixels.net'; - it('should register the pixel iframe from banner ad response', () => { - let syncs = spec.getUserSyncs( - { iframeEnabled: true }, - [{ body: { ads: { pixels: syncUrl } } }] - ); - expect(syncs).to.deep.equal([{ type: 'iframe', url: syncUrl }]); + describe('iframe sync', function () { + it('should register the pixel iframe from banner ad response', function () { + let syncs = spec.getUserSyncs( + {iframeEnabled: true}, + [{body: {ads: {pixels: syncUrl}}}] + ); + expect(syncs).to.deep.equal([{type: 'iframe', url: syncUrl}]); + }); + + it('should register the pixel iframe from video ad response', function () { + let syncs = spec.getUserSyncs( + {iframeEnabled: true}, + [{body: {pixels: syncUrl}}] + ); + expect(syncs).to.deep.equal([{type: 'iframe', url: syncUrl}]); + }); + + it('should register the default iframe if no pixels available', function () { + let syncs = spec.getUserSyncs( + {iframeEnabled: true}, + [] + ); + expect(syncs).to.deep.equal([{type: 'iframe', url: 'https://u.openx.net/w/1.0/pd'}]); + }); }); - it('should register the pixel iframe from video ad response', () => { - let syncs = spec.getUserSyncs( - {iframeEnabled: true}, - [{body: {pixels: syncUrl}}] - ); - expect(syncs).to.deep.equal([{type: 'iframe', url: syncUrl}]); + describe('pixel sync', function () { + it('should register the image pixel from banner ad response', function () { + let syncs = spec.getUserSyncs( + {pixelEnabled: true}, + [{body: {ads: {pixels: syncUrl}}}] + ); + expect(syncs).to.deep.equal([{type: 'image', url: syncUrl}]); + }); + + it('should register the image pixel from video ad response', function () { + let syncs = spec.getUserSyncs( + {pixelEnabled: true}, + [{body: {pixels: syncUrl}}] + ); + expect(syncs).to.deep.equal([{type: 'image', url: syncUrl}]); + }); + + it('should register the default image pixel if no pixels available', function () { + let syncs = spec.getUserSyncs( + {pixelEnabled: true}, + [] + ); + expect(syncs).to.deep.equal([{type: 'image', url: 'https://u.openx.net/w/1.0/pd'}]); + }); }); - it('should register the default iframe if no pixels available', () => { + it('should prioritize iframe over image for user sync', function () { let syncs = spec.getUserSyncs( - {iframeEnabled: true}, - [] + {iframeEnabled: true, pixelEnabled: true}, + [{body: {ads: {pixels: syncUrl}}}] ); - expect(syncs).to.deep.equal([{type: 'iframe', url: '//u.openx.net/w/1.0/pd'}]); + expect(syncs).to.deep.equal([{type: 'iframe', url: syncUrl}]); }); }); @@ -1138,7 +1857,7 @@ describe('OpenxAdapter', () => { if (adUnits.length) { mockedArjResponse.ads.count = adUnits.length; - mockedArjResponse.ads.ad = adUnits.map((adUnit, index) => { + mockedArjResponse.ads.ad = adUnits.map((adUnit) => { overrideKeyCheck(adUnit, DEFAULT_TEST_ARJ_AD_UNIT, 'OxArjAdUnit'); return Object.assign(utils.deepClone(DEFAULT_TEST_ARJ_AD_UNIT), adUnit); }); @@ -1169,4 +1888,5 @@ describe('OpenxAdapter', () => { return mockedAdUnit; } -}); +}) +; diff --git a/test/spec/modules/openxoutstreamBidAdapter_spec.js b/test/spec/modules/openxoutstreamBidAdapter_spec.js new file mode 100644 index 00000000000..634df1c8c6a --- /dev/null +++ b/test/spec/modules/openxoutstreamBidAdapter_spec.js @@ -0,0 +1,243 @@ +import {expect} from 'chai'; +import {spec} from 'modules/openxoutstreamBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +describe('OpenXOutstreamAdapter', function () { + const adapter = newBidder(spec); + const URLBASE = '/v/1.0/avjp'; + const BIDDER = 'openxoutstream'; + const div = document.createElement('div'); + const PLACEMENT_ID = '1986307928000988495'; + const YM_SCRIPT = `!function(e,t){if(void 0===t._ym){var a=Math.round(5*Math.random()/3)+'';t._ym='';var m=e.createElement('script');m.type='text/javascript',m.async=!0,m.src='//static.yieldmo.com/ym.'+a+'.js',(e.getElementsByTagName('head')[0]||e.getElementsByTagName('body')[0]).appendChild(m)}else t._ym instanceof String||void 0===t._ym.chkPls||t._ym.chkPls()}(document,window);`; + const PUBLISHER_ID = '1986307525700126029'; + const CR_ID = '2052941939925262540'; + const AD_ID = '1991358644725162800'; + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + describe('when request is for a banner ad', function () { + let bannerBid; + beforeEach(function () { + bannerBid = { + bidder: BIDDER, + params: {}, + adUnitCode: 'adunit-code', + mediaTypes: {banner: {}}, + sizes: [[300, 250], [300, 600]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + }; + }); + it('should return false when there is no delivery domain', function () { + bannerBid.params = {'unit': '12345678'}; + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); + }); + + describe('when there is a delivery domain', function () { + beforeEach(function () { + bannerBid.params = {delDomain: 'test-delivery-domain'} + }); + + it('should return false if there is no adunit id and sizes are defined', function () { + bannerBid.mediaTypes.banner.sizes = [720, 90]; + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); + }); + + it('should return true if there is delivery domain and unit', function () { + bannerBid.params.unit = '12345678'; + expect(spec.isBidRequestValid(bannerBid)).to.equal(true); + }); + it('should return false if there is unit but no delivery domain', function () { + bannerBid.params = {unit: '12345678'}; + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); + }); + it('shoud return false if there is no delivery domain and no unit', function () { + bannerBid.params = {}; + expect(spec.isBidRequestValid(bannerBid)).to.equal(false); + }) + }); + }); + }); + + describe('buildRequests for banner ads', function () { + const bidRequestsWithMediaType = [{ + 'bidder': BIDDER, + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + 'mediaType': 'banner', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + + it('should send bid request to openx url via GET, with mediaType specified as banner', function () { + const request = spec.buildRequests(bidRequestsWithMediaType); + const params = bidRequestsWithMediaType[0].params; + expect(request[0].url).to.equal(`https://` + params.delDomain + URLBASE); + expect(request[0].method).to.equal('GET'); + }); + + it('should send ad unit ids, height, and width when any are defined', function () { + const bidRequestsWithUnitIds = [{ + 'bidder': BIDDER, + 'params': { + 'unit': '540141567', + 'height': '200', + 'width': '250', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + sizes: [300, 250], + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + 'bidId': 'test-bid-id-2', + 'bidderRequestId': 'test-bid-request-2', + 'auctionId': 'test-auction-2' + }, { + 'bidder': BIDDER, + 'params': { + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [300, 250], + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': 'test-bid-id-1', + 'bidderRequestId': 'test-bid-request-1', + 'auctionId': 'test-auction-1' + }]; + const request = spec.buildRequests(bidRequestsWithUnitIds); + expect(request[0].data.auid).to.equal(`${bidRequestsWithUnitIds[0].params.unit}`); + expect(request[0].data.vht).to.not.equal(`${bidRequestsWithUnitIds[0].params.height}`); + expect(request[0].data.vwd).to.not.equal(`${bidRequestsWithUnitIds[0].params.width}`); + expect(request[0].data.vht).to.equal('184'); + expect(request[0].data.vwd).to.equal('414'); + expect(request[0].data.aus).to.equal('304x184%7C412x184%7C375x184%7C414x184'); + }); + + describe('interpretResponse', function () { + let serverResponse; + let serverRequest; + + beforeEach(function () { + serverResponse = { + body: { + width: 300, + height: 250, + pub_rev: 3000, + bidderCode: 'openxoutstream', + vastUrl: 'test.vast.url', + mediaType: 'banner', + adid: '9874652394875' + }, + header: 'header?', + }; + serverRequest = { + payload: { + bid: { + bidId: '2d36ac90d654af', + }, + } + }; + }); + + it('should correctly reorder the server response', function () { + const newResponse = spec.interpretResponse(serverResponse, serverRequest); + const openHtmlTag = ''; + const closeHtmlTag = ''; + const sdkScript = createSdkScript().outerHTML; + const placementDiv = createPlacementDiv(); + placementDiv.dataset.pId = PUBLISHER_ID; + const placementDivString = placementDiv.outerHTML; + const adResponse = getTemplateAdResponse(serverResponse.body.vastUrl, PLACEMENT_ID); + const adResponseString = JSON.stringify(adResponse); + const ymAdsScript = ''; + expect(newResponse.length).to.be.equal(1); + expect(newResponse[0]).to.deep.equal({ + requestId: '2d36ac90d654af', + bidderCode: 'openxoutstream', + vastUrl: 'test.vast.url', + mediaType: 'banner', + cpm: 3, + width: 300, + height: 250, + creativeId: '9874652394875', + currency: 'USD', + netRevenue: true, + ttl: 300, + ad: openHtmlTag + placementDivString + ymAdsScript + sdkScript + closeHtmlTag + }); + }); + + it('should not add responses if the cpm is 0 or null', function () { + serverResponse.body.pub_rev = 0; + let response = spec.interpretResponse(serverResponse, serverRequest); + expect(response).to.deep.equal([]); + + serverResponse.body.pub_rev = null; + response = spec.interpretResponse(serverResponse, serverRequest); + expect(response).to.deep.equal([]) + }); + }); + }) + + function createSdkScript() { + const script = document.createElement('script'); + script.innerHTML = YM_SCRIPT; + return script; + } + function createPlacementDiv() { + div.id = `ym_${PLACEMENT_ID}`; + div.classList.add('ym'); + div.dataset.lfId = CR_ID; + return div + } + const getTemplateAdResponse = (vastUrl) => { + return { + loader: 'openxoutstream', + availability_zone: '', + data: [ + { + ads: [ + { + actions: {}, + adv_id: AD_ID, + configurables: { + cta_button_copy: 'Learn More', + vast_click_tracking: 'true', + vast_url: vastUrl, + }, + cr_id: CR_ID, + } + ], + column_count: 1, + configs: { + allowable_height: '248', + header_copy: 'You May Like', + ping: 'true', + }, + creative_format_id: 40, + css: '', + placement_id: PLACEMENT_ID, + } + ], + nc: 0, + }; + }; +}); diff --git a/test/spec/modules/optimaticBidAdapter_spec.js b/test/spec/modules/optimaticBidAdapter_spec.js index d701d981f37..3dd7ca79cc7 100644 --- a/test/spec/modules/optimaticBidAdapter_spec.js +++ b/test/spec/modules/optimaticBidAdapter_spec.js @@ -2,10 +2,10 @@ import { expect } from 'chai'; import { spec, ENDPOINT } from 'modules/optimaticBidAdapter'; import * as utils from 'src/utils'; -describe('OptimaticBidAdapter', () => { +describe('OptimaticBidAdapter', function () { let bidRequest; - beforeEach(() => { + beforeEach(function () { bidRequest = { bidder: 'optimatic', params: { @@ -20,49 +20,49 @@ describe('OptimaticBidAdapter', () => { }; }); - describe('spec.isBidRequestValid', () => { - it('should return true when the required params are passed', () => { + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed', function () { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the "bidfloor" param is missing', () => { + it('should return false when the "bidfloor" param is missing', function () { bidRequest.params = { placement: '2chy7Gc2eSQL' }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when the "placement" param is missing', () => { + it('should return false when the "placement" param is missing', function () { bidRequest.params = { bidfloor: 5.00 }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when no bid params are passed', () => { + it('should return false when no bid params are passed', function () { bidRequest.params = {}; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when a bid request is not passed', () => { + it('should return false when a bid request is not passed', function () { expect(spec.isBidRequestValid()).to.equal(false); expect(spec.isBidRequestValid({})).to.equal(false); }); }); - describe('spec.buildRequests', () => { - it('should create a POST request for every bid', () => { + describe('spec.buildRequests', function () { + it('should create a POST request for every bid', function () { const requests = spec.buildRequests([ bidRequest ]); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(ENDPOINT + bidRequest.params.placement); }); - it('should attach the bid request object', () => { + it('should attach the bid request object', function () { const requests = spec.buildRequests([ bidRequest ]); expect(requests[0].bidRequest).to.equal(bidRequest); }); - it('should attach request data', () => { + it('should attach request data', function () { const requests = spec.buildRequests([ bidRequest ]); const data = requests[0].data; const [ width, height ] = bidRequest.sizes; @@ -71,7 +71,7 @@ describe('OptimaticBidAdapter', () => { expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); }); - it('must parse bid size from a nested array', () => { + it('must parse bid size from a nested array', function () { const width = 640; const height = 480; bidRequest.sizes = [[ width, height ]]; @@ -81,7 +81,7 @@ describe('OptimaticBidAdapter', () => { expect(data.imp[0].video.h).to.equal(height); }); - it('must parse bid size from a string', () => { + it('must parse bid size from a string', function () { const width = 640; const height = 480; bidRequest.sizes = `${width}x${height}`; @@ -91,7 +91,7 @@ describe('OptimaticBidAdapter', () => { expect(data.imp[0].video.h).to.equal(height); }); - it('must handle an empty bid size', () => { + it('must handle an empty bid size', function () { bidRequest.sizes = []; const requests = spec.buildRequests([ bidRequest ]); const data = requests[0].data; @@ -100,25 +100,25 @@ describe('OptimaticBidAdapter', () => { }); }); - describe('spec.interpretResponse', () => { - it('should return no bids if the response is not valid', () => { + describe('spec.interpretResponse', function () { + it('should return no bids if the response is not valid', function () { const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response "nurl" and "adm" are missing', () => { + it('should return no bids if the response "nurl" and "adm" are missing', function () { const serverResponse = {seatbid: [{bid: [{price: 5.01}]}]}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response "price" is missing', () => { + it('should return no bids if the response "price" is missing', function () { const serverResponse = {seatbid: [{bid: [{adm: ''}]}]}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); expect(bidResponse.length).to.equal(0); }); - it('should return a valid bid response with just "adm"', () => { + it('should return a valid bid response with just "adm"', function () { const serverResponse = {seatbid: [{bid: [{id: 1, price: 5.01, adm: ''}]}], cur: 'USD'}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); let o = { @@ -137,7 +137,7 @@ describe('OptimaticBidAdapter', () => { expect(bidResponse).to.deep.equal(o); }); - it('should return a valid bid response with just "nurl"', () => { + it('should return a valid bid response with just "nurl"', function () { const serverResponse = {seatbid: [{bid: [{id: 1, price: 5.01, nurl: 'https://mg-bid-win.optimatic.com/win/134eb262-948a-463e-ad93-bc8b622d399c?wp=${AUCTION_PRICE}'}]}], cur: 'USD'}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); let o = { @@ -156,7 +156,7 @@ describe('OptimaticBidAdapter', () => { expect(bidResponse).to.deep.equal(o); }); - it('should return a valid bid response with "nurl" when both nurl and adm exist', () => { + it('should return a valid bid response with "nurl" when both nurl and adm exist', function () { const serverResponse = {seatbid: [{bid: [{id: 1, price: 5.01, adm: '', nurl: 'https://mg-bid-win.optimatic.com/win/134eb262-948a-463e-ad93-bc8b622d399c?wp=${AUCTION_PRICE}'}]}], cur: 'USD'}; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); let o = { diff --git a/test/spec/modules/optimeraBidAdapter_spec.js b/test/spec/modules/optimeraBidAdapter_spec.js index 413a52d2d7f..a0111ca9944 100644 --- a/test/spec/modules/optimeraBidAdapter_spec.js +++ b/test/spec/modules/optimeraBidAdapter_spec.js @@ -2,20 +2,20 @@ import { expect } from 'chai'; import { spec } from 'modules/optimeraBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('OptimeraAdapter', () => { +describe('OptimeraAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }) - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'optimera', 'params': { - 'clientID': '0' + 'clientID': '9999' }, 'adUnitCode': 'div-0', 'sizes': [[300, 250], [300, 600]], @@ -24,12 +24,12 @@ describe('OptimeraAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); }) - describe('buildRequests', () => { + describe('buildRequests', function () { let bid = [ { 'adUnitCode': 'div-0', @@ -42,18 +42,17 @@ describe('OptimeraAdapter', () => { } } ]; - it('buildRequests fires', () => { + it('buildRequests fires', function () { let request = spec.buildRequests(bid); expect(request).to.exist; expect(request.method).to.equal('GET'); expect(request.payload).to.exist; - expect(request.data.t).to.exist; }); }) - describe('interpretResponse', () => { + describe('interpretResponse', function () { let serverResponse = {}; - serverResponse.body = JSON.parse('{"div-0":["RB_K","728x90K"], "timestamp":["RB_K","1507565666"]}'); + serverResponse.body = JSON.parse('{"div-0":["RB_K","728x90K"], "timestamp":["RB_K","1507565666"], "device": { "de": { "div-0":["A1","728x90K"] }, "mo": { "div-0":["RB_K","728x90K"] }, "tb": { "div-0":["RB_K","728x90K"] } } }'); var bidRequest = { 'method': 'get', 'payload': [ @@ -67,10 +66,34 @@ describe('OptimeraAdapter', () => { } ] } - it('interpresResponse fires', () => { + it('interpresResponse fires', function () { let bidResponses = spec.interpretResponse(serverResponse, bidRequest); expect(bidResponses[0].dealId[0]).to.equal('RB_K'); expect(bidResponses[0].dealId[1]).to.equal('728x90K'); }); }); + + describe('interpretResponse with optional device param', function () { + let serverResponse = {}; + serverResponse.body = JSON.parse('{"div-0":["RB_K","728x90K"], "timestamp":["RB_K","1507565666"], "device": { "de": { "div-0":["A1","728x90K"] }, "mo": { "div-0":["RB_K","728x90K"] }, "tb": { "div-0":["RB_K","728x90K"] } } }'); + var bidRequest = { + 'method': 'get', + 'payload': [ + { + 'bidder': 'optimera', + 'params': { + 'clientID': '0', + 'device': 'de' + }, + 'adUnitCode': 'div-0', + 'bidId': '307440db8538ab' + } + ] + } + it('interpresResponse fires', function () { + let bidResponses = spec.interpretResponse(serverResponse, bidRequest); + expect(bidResponses[0].dealId[0]).to.equal('A1'); + expect(bidResponses[0].dealId[1]).to.equal('728x90K'); + }); + }); }); diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js new file mode 100644 index 00000000000..aeaa5f30446 --- /dev/null +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -0,0 +1,255 @@ +import {expect} from 'chai'; +import {spec} from 'modules/orbidderBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; +import openxAdapter from '../../../modules/openxAnalyticsAdapter'; +import {detectReferer} from 'src/refererDetection'; + +describe('orbidderBidAdapter', () => { + const adapter = newBidder(spec); + const defaultBidRequest = { + bidId: 'd66fa86787e0b0ca900a96eacfd5f0bb', + auctionId: 'ccc4c7cdfe11cfbd74065e6dd28413d8', + transactionId: 'd58851660c0c4461e4aa06344fc9c0c6', + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 600]], + params: { + 'accountId': 'string1', + 'placementId': 'string2' + } + }; + + const deepClone = function (val) { + return JSON.parse(JSON.stringify(val)); + }; + + const buildRequest = (buildRequest, bidderRequest) => { + if (!Array.isArray(buildRequest)) { + buildRequest = [buildRequest]; + } + + return spec.buildRequests(buildRequest, { + ...bidderRequest || {}, + refererInfo: { + referer: 'http://localhost:9876/' + } + })[0]; + }; + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(defaultBidRequest)).to.equal(true); + }); + + it('accepts optional keyValues object', () => { + const bidRequest = deepClone(defaultBidRequest); + bidRequest.params.keyValues = {'key': 'value'}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('accepts optional profile object', () => { + const bidRequest = deepClone(defaultBidRequest); + bidRequest.params.profile = {'key': 'value'}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('performs type checking', () => { + const bidRequest = deepClone(defaultBidRequest); + bidRequest.params.accountId = 1; // supposed to be a string + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('doesn\'t accept malformed keyValues', () => { + const bidRequest = deepClone(defaultBidRequest); + bidRequest.params.keyValues = 'another not usable string'; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('doesn\'t accept malformed profile', () => { + const bidRequest = deepClone(defaultBidRequest); + bidRequest.params.profile = 'another not usable string'; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when required params are not passed', () => { + const bidRequest = deepClone(defaultBidRequest); + delete bidRequest.params; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('accepts optional bidfloor', () => { + const bidRequest = deepClone(defaultBidRequest); + bidRequest.params.bidfloor = 123; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + + bidRequest.params.bidfloor = 1.23; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('doesn\'t accept malformed bidfloor', () => { + const bidRequest = deepClone(defaultBidRequest); + bidRequest.params.bidfloor = 'another not usable string'; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + const request = buildRequest(defaultBidRequest); + + it('sends bid request to endpoint via https using post', () => { + expect(request.method).to.equal('POST'); + expect(request.url.indexOf('https://')).to.equal(0); + expect(request.url).to.equal(`${spec.orbidderHost}/bid`); + }); + + it('sends correct bid parameters', () => { + // we add one, because we add referer information from bidderRequest object + expect(Object.keys(request.data).length).to.equal(Object.keys(defaultBidRequest).length + 1); + expect(request.data.pageUrl).to.equal('http://localhost:9876/'); + // expect(request.data.referrer).to.equal(''); + Object.keys(defaultBidRequest).forEach((key) => { + expect(defaultBidRequest[key]).to.equal(request.data[key]); + }); + }); + + it('handles empty gdpr object', () => { + const request = buildRequest(defaultBidRequest, { + gdprConsent: {} + }); + expect(request.data.gdprConsent.consentRequired).to.be.equal(false); + }); + + it('handles non-existent gdpr object', () => { + const request = buildRequest(defaultBidRequest, { + gdprConsent: null + }); + expect(request.data.gdprConsent).to.be.undefined; + }); + + it('handles properly filled gdpr object where gdpr applies', () => { + const consentString = 'someWeirdString'; + const request = buildRequest(defaultBidRequest, { + gdprConsent: { + gdprApplies: true, + consentString: consentString + } + }); + + const gdprConsent = request.data.gdprConsent; + expect(gdprConsent.consentRequired).to.be.equal(true); + expect(gdprConsent.consentString).to.be.equal(consentString); + }); + + it('handles properly filled gdpr object where gdpr does not apply', () => { + const consentString = 'someWeirdString'; + const request = buildRequest(defaultBidRequest, { + gdprConsent: { + gdprApplies: false, + consentString: consentString + } + }); + + const gdprConsent = request.data.gdprConsent; + expect(gdprConsent.consentRequired).to.be.equal(false); + expect(gdprConsent.consentString).to.be.equal(consentString); + }); + }); + + describe('onCallbackHandler', () => { + let ajaxStub; + const bidObj = { + adId: 'testId', + test: 1, + pageUrl: 'www.someurl.de', + referrer: 'www.somereferrer.de', + requestId: '123req456' + }; + + spec.bidParams['123req456'] = {'accountId': '123acc456'}; + + let bidObjClone = deepClone(bidObj); + bidObjClone.pageUrl = detectReferer(window)().referer; + bidObjClone.params = [{'accountId': '123acc456'}]; + + beforeEach(() => { + ajaxStub = sinon.stub(spec, 'ajaxCall'); + }); + + afterEach(() => { + ajaxStub.restore(); + }); + + it('calls orbidder\'s callback endpoint', () => { + spec.onBidWon(bidObj); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0].indexOf('https://')).to.equal(0); + expect(ajaxStub.firstCall.args[0]).to.equal(`${spec.orbidderHost}/win`); + expect(ajaxStub.firstCall.args[1]).to.equal(JSON.stringify(bidObjClone)); + }); + }); + + describe('interpretResponse', () => { + it('should get correct bid response', () => { + const serverResponse = [ + { + 'width': 300, + 'height': 250, + 'creativeId': '29681110', + 'ad': '', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'netRevenue': true, + 'currency': 'EUR' + } + ]; + + const expectedResponse = [ + { + 'requestId': '30b31c1838de1e', + 'cpm': 0.5, + 'creativeId': '29681110', + 'width': 300, + 'height': 250, + 'ttl': 60, + 'currency': 'EUR', + 'ad': '', + 'netRevenue': true + } + ]; + + const result = spec.interpretResponse({body: serverResponse}); + + expect(result.length).to.equal(expectedResponse.length); + Object.keys(expectedResponse[0]).forEach((key) => { + expect(result[0][key]).to.equal(expectedResponse[0][key]); + }); + }); + + it('handles broken server response', () => { + const serverResponse = [ + { + 'ad': '', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60 + } + ]; + const result = spec.interpretResponse({body: serverResponse}); + + expect(result.length).to.equal(0); + }); + + it('handles nobid responses', () => { + const serverResponse = []; + const result = spec.interpretResponse({body: serverResponse}); + + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/orbitsoftBidAdapter_spec.js b/test/spec/modules/orbitsoftBidAdapter_spec.js index 50145a1e72e..18ba9a6e8f3 100644 --- a/test/spec/modules/orbitsoftBidAdapter_spec.js +++ b/test/spec/modules/orbitsoftBidAdapter_spec.js @@ -2,10 +2,10 @@ import {expect} from 'chai'; import {spec} from 'modules/orbitsoftBidAdapter'; const ENDPOINT_URL = 'https://orbitsoft.com/php/ads/hb.phps'; -describe('Orbitsoft adapter', () => { - describe('implementation', () => { - describe('for requests', () => { - it('should accept valid bid', () => { +describe('Orbitsoft adapter', function () { + describe('implementation', function () { + describe('for requests', function () { + it('should accept valid bid', function () { let validBid = { bidder: 'orbitsoft', params: { @@ -18,7 +18,7 @@ describe('Orbitsoft adapter', () => { expect(isValid).to.equal(true); }); - it('should reject invalid bid', () => { + it('should reject invalid bid', function () { let invalidBid = { bidder: 'orbitsoft' }, @@ -27,8 +27,8 @@ describe('Orbitsoft adapter', () => { expect(isValid).to.equal(false); }); }); - describe('for requests', () => { - it('should accept valid bid with styles', () => { + describe('for requests', function () { + it('should accept valid bid with styles', function () { let validBid = { bidder: 'orbitsoft', params: { @@ -91,7 +91,7 @@ describe('Orbitsoft adapter', () => { expect(requestUrlParams).have.property('c6', '5B99FE'); }); - it('should accept valid bid with custom params', () => { + it('should accept valid bid with custom params', function () { let validBid = { bidder: 'orbitsoft', params: { @@ -112,7 +112,7 @@ describe('Orbitsoft adapter', () => { expect(requestUrlCustomParams).have.property('c.clickUrl', 'http://testclickurl.com'); }); - it('should reject invalid bid without requestUrl', () => { + it('should reject invalid bid without requestUrl', function () { let invalidBid = { bidder: 'orbitsoft', params: { @@ -124,7 +124,7 @@ describe('Orbitsoft adapter', () => { expect(isValid).to.equal(false); }); - it('should reject invalid bid without placementId', () => { + it('should reject invalid bid without placementId', function () { let invalidBid = { bidder: 'orbitsoft', params: { @@ -136,8 +136,8 @@ describe('Orbitsoft adapter', () => { expect(isValid).to.equal(false); }); }); - describe('bid responses', () => { - it('should return complete bid response', () => { + describe('bid responses', function () { + it('should return complete bid response', function () { let serverResponse = { body: { callback_uid: '265b29b70cc106', @@ -168,7 +168,7 @@ describe('Orbitsoft adapter', () => { expect(bids[0].adUrl).to.have.string('https://orbitsoft.com/php/ads/hb.html'); }); - it('should return empty bid response', () => { + it('should return empty bid response', function () { let bidRequests = [ { bidder: 'orbitsoft', @@ -189,7 +189,7 @@ describe('Orbitsoft adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response on incorrect size', () => { + it('should return empty bid response on incorrect size', function () { let bidRequests = [ { bidder: 'orbitsoft', @@ -212,7 +212,7 @@ describe('Orbitsoft adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response with error', () => { + it('should return empty bid response with error', function () { let bidRequests = [ { bidder: 'orbitsoft', @@ -228,7 +228,7 @@ describe('Orbitsoft adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response on empty body', () => { + it('should return empty bid response on empty body', function () { let bidRequests = [ { bidder: 'orbitsoft', diff --git a/test/spec/modules/otmBidAdapter_spec.js b/test/spec/modules/otmBidAdapter_spec.js new file mode 100644 index 00000000000..f3a98d43e57 --- /dev/null +++ b/test/spec/modules/otmBidAdapter_spec.js @@ -0,0 +1,105 @@ +import {expect} from 'chai'; +import {spec} from 'modules/otmBidAdapter'; + +describe('otmBidAdapterTests', function () { + it('validate_pub_params', function () { + expect(spec.isBidRequestValid({ + bidder: 'otm', + params: { + tid: '123', + bidfloor: 20 + } + })).to.equal(true); + }); + + it('validate_generated_params', function () { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'otm', + params: { + tid: '123', + bidfloor: 20 + }, + sizes: [[240, 400]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = request[0].data; + + expect(req_data.bidid).to.equal('bid1234'); + }); + + it('validate_best_size_select', function () { + // when: + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'otm', + params: { + tid: '123', + bidfloor: 20 + }, + sizes: [[300, 500], [300, 600], [240, 400], [300, 50]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = request[0].data; + + // then: + expect(req_data.w).to.equal(240); + expect(req_data.h).to.equal(400); + + // when: + bidRequestData = [{ + bidId: 'bid1234', + bidder: 'otm', + params: { + tid: '123', + bidfloor: 20 + }, + sizes: [[200, 240], [400, 440]] + }]; + + request = spec.buildRequests(bidRequestData); + req_data = request[0].data; + + // then: + expect(req_data.w).to.equal(200); + expect(req_data.h).to.equal(240); + }); + + it('validate_response_params', function () { + let bidRequestData = { + data: { + bidId: 'bid1234' + } + }; + + let serverResponse = { + body: [ + { + 'auctionid': '3c6f8e22-541b-485c-9214-e974d9fb1b6f', + 'cpm': 847.097, + 'ad': 'test html', + 'w': 240, + 'h': 400, + 'currency': 'RUB', + 'ttl': 300, + 'creativeid': '1_7869053', + 'bidid': '101f211def7c99', + 'transactionid': 'transaction_id_1' + } + ] + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(847.097); + expect(bid.currency).to.equal('RUB'); + expect(bid.width).to.equal(240); + expect(bid.height).to.equal(400); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('101f211def7c99'); + expect(bid.ad).to.equal('test html'); + }); +}); diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js new file mode 100644 index 00000000000..a0b51ff7a9f --- /dev/null +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -0,0 +1,992 @@ +import { expect } from 'chai'; +import { spec, getWidthAndHeightFromVideoObject, playerSizeIsNestedArray, defaultSize } from 'modules/ozoneBidAdapter'; +import { config } from 'src/config'; +import {Renderer} from '../../../src/Renderer'; +const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; +const BIDDER_CODE = 'ozone'; +/* + +NOTE - use firefox console to deep copy the objects to use here + + */ +var validBidRequests = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsMinimal = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + params: { publisherId: '9876abcd12-3', placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsNoSizes = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; + +var validBidRequestsWithBannerMediaType = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + mediaTypes: {banner: {sizes: [[300, 250], [300, 600]]}}, + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; +var validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo = [ + { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, video: {skippable: true, playback_method: ['auto_play_sound_off'], targetDiv: 'some-different-div-id-to-my-adunitcode'} } ] }, + mediaTypes: {video: {mimes: ['video/mp4'], 'context': 'outstream', 'sizes': [640, 480]}, native: {info: 'dummy data'}}, + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + } +]; + +var validBidderRequest = { + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + auctionStart: 1536838908986, + bidderCode: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + bids: [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }], + doneCbCallCount: 1, + start: 1536838908987, + timeout: 3000 +}; + +// bidder request with GDPR - change the values for testing: +// gdprConsent.gdprApplies (true/false) +// gdprConsent.vendorData.purposeConsents (make empty, make null, remove it) +// gdprConsent.vendorData.vendorConsents (remove 524, remove all, make the element null, remove it) +var bidderRequestWithFullGdpr = { + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + auctionStart: 1536838908986, + bidderCode: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + bids: [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }], + doneCbCallCount: 1, + start: 1536838908987, + timeout: 3000, + gdprConsent: { + 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + 'vendorData': { + 'metadata': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + 'gdprApplies': true, + 'hasGlobalScope': false, + 'cookieVersion': '1', + 'created': '2019-05-31T12:46:48.825', + 'lastUpdated': '2019-05-31T12:46:48.825', + 'cmpId': '28', + 'cmpVersion': '1', + 'consentLanguage': 'en', + 'consentScreen': '1', + 'vendorListVersion': 148, + 'maxVendorId': 631, + 'purposeConsents': { + '1': true, + '2': true, + '3': true, + '4': true, + '5': true + }, + 'vendorConsents': { + '468': true, + '522': true, + '524': true, /* 524 is ozone */ + '565': true, + '591': true + } + }, + 'gdprApplies': true + }, +}; + +// make sure the impid matches the request bidId +var validResponse = { + 'body': { + 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', + 'seatbid': [ + { + 'bid': [ + { + 'id': '677903815252395017', + 'impid': '2899ec066a91ff8', + 'price': 0.5, + 'adm': '', + 'adid': '98493581', + 'adomain': [ + 'http://prebid.org' + ], + 'iurl': 'https://fra1-ib.adnxs.com/cr?id=98493581', + 'cid': '9325', + 'crid': '98493581', + 'cat': [ + 'IAB3-1' + ], + 'w': 300, + 'h': 600, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 555545, + 'auction_id': 6500448734132353000, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'appnexus' + } + ], + 'ext': { + 'responsetimemillis': { + 'appnexus': 47, + 'openx': 30 + } + }, + 'timing': { + 'start': 1536848078.089177, + 'end': 1536848078.142203, + 'TimeTaken': 0.05302619934082031 + } + }, + 'headers': {} +}; +var validOutstreamResponse = { + 'body': { + 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', + 'seatbid': [ + { + 'bid': [ + { + 'id': '677903815252395017', + 'impid': '2899ec066a91ff8', + 'price': 0.5, + 'adm': '', + 'adid': '98493581', + 'adomain': [ + 'http://prebid.org' + ], + 'iurl': 'https://fra1-ib.adnxs.com/cr?id=98493581', + 'cid': '9325', + 'crid': '98493581', + 'cat': [ + 'IAB3-1' + ], + 'w': 300, + 'h': 600, + 'ext': { + 'prebid': { + 'type': 'video' + }, + 'bidder': { + 'unruly': { + 'renderer': { + 'config': { + 'targetingUUID': 'aafd3388-afaf-41f4-b271-0ac8e0325a7f', + 'siteId': 1052815, + 'featureOverrides': {} + }, + 'url': 'https://video.unrulymedia.com/native/native-loader.js#supplyMode=prebid?cb=6284685353877994', + 'id': 'unruly_inarticle' + }, + 'vast_url': 'data:text/xml;base64,PD94bWwgdmVyc2lvbj0i' + } + } + } + } + ], + 'seat': 'unruly' + } + ], + 'ext': { + 'responsetimemillis': { + 'appnexus': 47, + 'openx': 30 + } + }, + 'timing': { + 'start': 1536848078.089177, + 'end': 1536848078.142203, + 'TimeTaken': 0.05302619934082031 + } + }, + 'headers': {} +}; +var validBidResponse1adWith2Bidders = { + 'body': { + 'id': '91221f96-b931-4acc-8f05-c2a1186fa5ac', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', + 'impid': '2899ec066a91ff8', + 'price': 0.36754, + 'adm': '', + 'adid': '134928661', + 'adomain': [ + 'somecompany.com' + ], + 'iurl': 'https:\/\/ams1-ib.adnxs.com\/cr?id=134928661', + 'cid': '8825', + 'crid': '134928661', + 'cat': [ + 'IAB8-15', + 'IAB8-16', + 'IAB8-4', + 'IAB8-1', + 'IAB8-14', + 'IAB8-6', + 'IAB8-13', + 'IAB8-3', + 'IAB8-17', + 'IAB8-12', + 'IAB8-8', + 'IAB8-7', + 'IAB8-2', + 'IAB8-9', + 'IAB8', + 'IAB8-11' + ], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 14640, + 'auction_id': 1.8369641905139e+18, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'appnexus' + }, + { + 'bid': [ + { + 'id': '75665207-a1ca-49db-ba0e-a5e9c7d26f32', + 'impid': '37fff511779365a', + 'price': 1.046, + 'adm': '
    removed
    ', + 'adomain': [ + 'kx.com' + ], + 'crid': '13005', + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + } + } + } + ], + 'seat': 'openx' + } + ], + 'ext': { + 'responsetimemillis': { + 'appnexus': 91, + 'openx': 109, + 'ozappnexus': 46, + 'ozbeeswax': 2, + 'pangaea': 91 + } + } + }, + 'headers': {} +}; + +describe('ozone Adapter', function () { + describe('isBidRequestValid', function () { + // A test ad unit that will consistently return test creatives + let validBidReq = { + bidder: BIDDER_CODE, + params: { + placementId: '1310000099', + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(validBidReq)).to.equal(true); + }); + + var validBidReq2 = { + + bidder: BIDDER_CODE, + params: { + placementId: '1310000099', + publisherId: '9876abcd12-3', + siteId: '1234567890', + customData: {'gender': 'bart', 'age': 'low'}, + lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, + }, + siteId: 1234567890 + } + + it('should return true when required params found and all optional params are valid', function () { + expect(spec.isBidRequestValid(validBidReq2)).to.equal(true); + }); + + var xEmptyPlacement = { + bidder: BIDDER_CODE, + params: { + placementId: '', + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + + it('should not validate empty placementId', function () { + expect(spec.isBidRequestValid(xEmptyPlacement)).to.equal(false); + }); + + var xMissingPlacement = { + bidder: BIDDER_CODE, + params: { + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + + it('should not validate missing placementId', function () { + expect(spec.isBidRequestValid(xMissingPlacement)).to.equal(false); + }); + + var xBadPlacement = { + bidder: BIDDER_CODE, + params: { + placementId: '123X45', + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + + it('should not validate placementId with a non-numeric value', function () { + expect(spec.isBidRequestValid(xBadPlacement)).to.equal(false); + }); + + var xBadPlacementTooShort = { + bidder: BIDDER_CODE, + params: { + placementId: 123456789, /* should be exactly 10 chars */ + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + + it('should not validate placementId with a numeric value of wrong length', function () { + expect(spec.isBidRequestValid(xBadPlacementTooShort)).to.equal(false); + }); + + var xBadPlacementTooLong = { + bidder: BIDDER_CODE, + params: { + placementId: 12345678901, /* should be exactly 10 chars */ + publisherId: '9876abcd12-3', + siteId: '1234567890' + } + }; + + it('should not validate placementId with a numeric value of wrong length', function () { + expect(spec.isBidRequestValid(xBadPlacementTooLong)).to.equal(false); + }); + + var xMissingPublisher = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + siteId: '1234567890' + } + }; + + it('should not validate missing publisherId', function () { + expect(spec.isBidRequestValid(xMissingPublisher)).to.equal(false); + }); + + var xMissingSiteId = { + bidder: BIDDER_CODE, + params: { + publisherId: '9876abcd12-3', + placementId: '1234567890', + } + }; + + it('should not validate missing sitetId', function () { + expect(spec.isBidRequestValid(xMissingSiteId)).to.equal(false); + }); + + var xBadPublisherTooShort = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12a', + siteId: '1234567890' + } + }; + + it('should not validate publisherId being too short', function () { + expect(spec.isBidRequestValid(xBadPublisherTooShort)).to.equal(false); + }); + + var xBadPublisherTooLong = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12abc', + siteId: '1234567890' + } + }; + + it('should not validate publisherId being too long', function () { + expect(spec.isBidRequestValid(xBadPublisherTooLong)).to.equal(false); + }); + + var publisherNumericOk = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: 123456789012, + siteId: '1234567890' + } + }; + + it('should validate publisherId being 12 digits', function () { + expect(spec.isBidRequestValid(publisherNumericOk)).to.equal(true); + }); + + var xEmptyPublisher = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '', + siteId: '1234567890' + } + }; + + it('should not validate empty publisherId', function () { + expect(spec.isBidRequestValid(xEmptyPublisher)).to.equal(false); + }); + + var xBadSite = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12-3', + siteId: '12345Z' + } + }; + + it('should not validate bad siteId', function () { + expect(spec.isBidRequestValid(xBadSite)).to.equal(false); + }); + + var xBadSiteTooLong = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12-3', + siteId: '12345678901' + } + }; + + it('should not validate siteId too long', function () { + expect(spec.isBidRequestValid(xBadSite)).to.equal(false); + }); + + var xBadSiteTooShort = { + bidder: BIDDER_CODE, + params: { + placementId: '1234567890', + publisherId: '9876abcd12-3', + siteId: '123456789' + } + }; + + it('should not validate siteId too short', function () { + expect(spec.isBidRequestValid(xBadSite)).to.equal(false); + }); + + var allNonStrings = { + bidder: BIDDER_CODE, + params: { + placementId: 1234567890, + publisherId: '9876abcd12-3', + siteId: 1234567890 + } + }; + + it('should validate all numeric values being sent as non-string numbers', function () { + expect(spec.isBidRequestValid(allNonStrings)).to.equal(true); + }); + + var emptySiteId = { + bidder: BIDDER_CODE, + params: { + placementId: 1234567890, + publisherId: '9876abcd12-3', + siteId: '' + } + }; + + it('should not validate siteId being empty string (it is required now)', function () { + expect(spec.isBidRequestValid(emptySiteId)).to.equal(false); + }); + + var xBadCustomData = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'siteId': '1234567890', + 'customData': 'this aint gonna work' + } + }; + + it('should not validate customData not being an object', function () { + expect(spec.isBidRequestValid(xBadCustomData)).to.equal(false); + }); + + var xCustomParams = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'customParams': {'info': 'this is not allowed'}, + siteId: '1234567890' + } + }; + + it('should not validate customParams being sent', function () { + expect(spec.isBidRequestValid(xCustomParams)).to.equal(false); + }); + + var xBadCustomData = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'customData': 'this should be an object', + siteId: '1234567890' + } + }; + it('should not validate ozoneData being sent', function () { + expect(spec.isBidRequestValid(xBadCustomData)).to.equal(false); + }); + + var xBadLotame = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'lotameData': 'this should be an object', + siteId: '1234567890' + } + }; + it('should not validate lotameData being sent', function () { + expect(spec.isBidRequestValid(xBadLotame)).to.equal(false); + }); + + var xBadVideoContext = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'lotameData': {}, + siteId: '1234567890' + }, + mediaTypes: { + video: { + mimes: ['video/mp4'], + 'context': 'instream'}, + } + }; + + it('should not validate video instream being sent', function () { + expect(spec.isBidRequestValid(xBadVideoContext)).to.equal(false); + }); + + var xBadVideoContext2 = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'lotameData': {}, + siteId: '1234567890' + }, + mediaTypes: { + video: { + mimes: ['video/mp4']} + } + }; + + it('should not validate video without context attribute', function () { + expect(spec.isBidRequestValid(xBadVideoContext2)).to.equal(false); + }); + + let validVideoBidReq = { + bidder: BIDDER_CODE, + params: { + placementId: '1310000099', + publisherId: '9876abcd12-3', + siteId: '1234567890' + }, + mediaTypes: { + video: { + mimes: ['video/mp4'], + 'context': 'outstream'}, + } + }; + + it('should validate video outstream being sent', function () { + expect(spec.isBidRequestValid(validVideoBidReq)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + it('sends bid request to OZONEURI via POST', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.url).to.equal(OZONEURI); + expect(request.method).to.equal('POST'); + }); + + it('sends data as a string', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.data).to.be.a('string'); + }); + + it('sends all bid parameters', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + + it('adds all parameters inside the ext object only', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.data).to.be.a('string'); + var data = JSON.parse(request.data); + expect(data.imp[0].ext.ozone.lotameData).to.be.an('object'); + expect(data.imp[0].ext.ozone.customData).to.be.an('object'); + expect(request).not.to.have.key('lotameData'); + expect(request).not.to.have.key('customData'); + }); + + it('ignores ozoneData in & after version 2.1.1', function () { + let validBidRequestsWithOzoneData = validBidRequests; + validBidRequestsWithOzoneData[0].params.ozoneData = {'networkID': '3048', 'dfpSiteID': 'd.thesun', 'sectionID': 'homepage', 'path': '/', 'sec_id': 'null', 'sec': 'sec', 'topics': 'null', 'kw': 'null', 'aid': 'null', 'search': 'null', 'article_type': 'null', 'hide_ads': '', 'article_slug': 'null'}; + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.data).to.be.a('string'); + var data = JSON.parse(request.data); + expect(data.imp[0].ext.ozone.lotameData).to.be.an('object'); + expect(data.imp[0].ext.ozone.customData).to.be.an('object'); + expect(data.imp[0].ext.ozone.ozoneData).to.be.undefined; + expect(request).not.to.have.key('lotameData'); + expect(request).not.to.have.key('customData'); + }); + + it('has correct bidder', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + expect(request.bidderRequest.bids[0].bidder).to.equal(BIDDER_CODE); + }); + + it('handles mediaTypes element correctly', function () { + const request = spec.buildRequests(validBidRequestsWithBannerMediaType, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + + it('handles no ozone, lotame or custom data', function () { + const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + + it('handles video mediaType element correctly, with outstream video', function () { + const request = spec.buildRequests(validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + + it('should not crash when there is no sizes element at all', function () { + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); + expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + }); + + it('should be able to handle non-single requests', function () { + config.setConfig({'ozone': {'singleRequest': false}}); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); + expect(request).to.be.a('array'); + expect(request[0]).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); + // need to reset the singleRequest config flag: + config.setConfig({'ozone': {'singleRequest': true}}); + }); + + it('should add gdpr consent information to the request when ozone is true', function () { + let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; + let bidderRequest = validBidderRequest; + bidderRequest.gdprConsent = { + consentString: consentString, + gdprApplies: true, + vendorData: { + vendorConsents: {524: true}, + purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} + } + } + + const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.regs.ext.gdpr).to.equal(1); + expect(payload.regs.ext.oz_con).to.exist.and.to.equal(1); + expect(payload.regs.ext.gap).to.exist.and.to.be.an('array').and.to.eql([1, 2, 3, 4, 5]); + }); + + it('should add correct gdpr consent information to the request when user has accepted only some purpose consents', function () { + let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; + let bidderRequest = validBidderRequest; + bidderRequest.gdprConsent = { + consentString: consentString, + gdprApplies: true, + vendorData: { + vendorConsents: {524: true}, + purposeConsents: {1: true, 4: true, 5: true} + } + } + + const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.regs.ext.gdpr).to.equal(1); + expect(payload.regs.ext.oz_con).to.exist.and.to.equal(1); + expect(payload.regs.ext.gap).to.exist.and.to.be.an('array').and.to.eql([1, 4, 5]); + }); + + it('should add gdpr consent information to the request when ozone is false', function () { + let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; + let bidderRequest = validBidderRequest; + bidderRequest.gdprConsent = { + consentString: consentString, + gdprApplies: true, + vendorData: { + vendorConsents: {}, /* 524 is not present */ + purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} + } + }; + + const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.regs.ext.gdpr).to.equal(1); + expect(payload.regs.ext.oz_con).to.exist.and.to.equal(0); + expect(payload.regs.ext.gap).to.exist.and.to.be.an('array').and.to.eql([1, 2, 3, 4, 5]); + }); + + it('should set regs.ext.gdpr flag to 0 when gdprApplies is false', function () { + let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; + let bidderRequest = validBidderRequest; + bidderRequest.gdprConsent = { + consentString: consentString, + gdprApplies: false, + vendorData: { + vendorConsents: {}, /* 524 is not present */ + purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} + } + }; + + const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.regs.ext.gdpr).to.equal(0); + expect(payload.regs.ext.oz_con).to.be.undefined; + expect(payload.regs.ext.gap).to.be.undefined; + }); + }); + + describe('interpretResponse', function () { + it('should build bid array', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + const bid = result[0]; + expect(bid.cpm).to.equal(validResponse.body.seatbid[0].bid[0].cpm); + expect(bid.width).to.equal(validResponse.body.seatbid[0].bid[0].width); + expect(bid.height).to.equal(validResponse.body.seatbid[0].bid[0].height); + }); + + it('should build bid array with gdpr', function () { + var validBidderRequestWithGdpr = validBidderRequest; + validBidderRequestWithGdpr.gdprConsent = {'gdprApplies': 1, 'consentString': 'This is the gdpr consent string'}; + const request = spec.buildRequests(validBidRequests, validBidderRequestWithGdpr); + const result = spec.interpretResponse(validResponse, request); + expect(result.length).to.equal(1); + }); + + it('should fail ok if no seatbid in server response', function () { + const result = spec.interpretResponse({}, {}); + expect(result).to.be.an('array'); + expect(result).to.be.empty; + }); + + it('should fail ok if seatbid is not an array', function () { + const result = spec.interpretResponse({'body': {'seatbid': 'nothing_here'}}, {}); + expect(result).to.be.an('array'); + expect(result).to.be.empty; + }); + + it('should have video renderer', function () { + const request = spec.buildRequests(validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo, validBidderRequest); + const result = spec.interpretResponse(validOutstreamResponse, request); + const bid = result[0]; + expect(bid.renderer).to.be.an.instanceOf(Renderer); + }); + + it('should correctly parse response where there are more bidders than ad slots', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validBidResponse1adWith2Bidders, request); + expect(result.length).to.equal(2); + }); + }); + + describe('userSyncs', function () { + it('should fail gracefully if no server response', function () { + const result = spec.getUserSyncs('bad', false); + expect(result).to.be.empty; + }); + it('should fail gracefully if server response is empty', function () { + const result = spec.getUserSyncs('bad', []); + expect(result).to.be.empty; + }); + }); + + describe('video object utils', function () { + it('should find width & height from video object', function () { + let obj = {'playerSize': [640, 480], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = getWidthAndHeightFromVideoObject(obj); + expect(result.w).to.equal(640); + expect(result.h).to.equal(480); + }); + it('should find null from bad video object', function () { + let obj = {'playerSize': [], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = getWidthAndHeightFromVideoObject(obj); + expect(result).to.be.null; + }); + it('should find null from bad video object2', function () { + let obj = {'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = getWidthAndHeightFromVideoObject(obj); + expect(result).to.be.null; + }); + it('should find null from bad video object3', function () { + let obj = {'playerSize': 'should be an array', 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = getWidthAndHeightFromVideoObject(obj); + expect(result).to.be.null; + }); + it('should find that player size is nested', function () { + let obj = {'playerSize': [[640, 480]], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = getWidthAndHeightFromVideoObject(obj); + expect(result.w).to.equal(640); + expect(result.h).to.equal(480); + }); + it('should fail if player size is 2 x nested', function () { + let obj = {'playerSize': [[[640, 480]]], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = getWidthAndHeightFromVideoObject(obj); + expect(result).to.be.null; + }); + it('should find that player size is nested', function () { + let obj = {'playerSize': [[640, 480]], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = playerSizeIsNestedArray(obj); + expect(result).to.be.true; + }); + it('should find null from bad video object', function () { + let obj = {'playerSize': [], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = playerSizeIsNestedArray(obj); + expect(result).to.be.null; + }); + it('should find null from bad video object2', function () { + let obj = {'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = playerSizeIsNestedArray(obj); + expect(result).to.be.null; + }); + it('should find null from bad video object3', function () { + let obj = {'playerSize': 'should be an array', 'mimes': ['video/mp4'], 'context': 'outstream'}; + const result = playerSizeIsNestedArray(obj); + expect(result).to.be.null; + }); + }); + describe('default size', function () { + it('should should return default sizes if no obj is sent', function () { + let obj = ''; + const result = defaultSize(obj); + expect(result.defaultHeight).to.equal(250); + expect(result.defaultWidth).to.equal(300); + }); + }); +}); diff --git a/test/spec/modules/padsquadBidAdapter_spec.js b/test/spec/modules/padsquadBidAdapter_spec.js new file mode 100644 index 00000000000..aba1efea32f --- /dev/null +++ b/test/spec/modules/padsquadBidAdapter_spec.js @@ -0,0 +1,261 @@ +import {expect} from 'chai'; +import {spec} from 'modules/padsquadBidAdapter'; + +const REQUEST = { + 'bidderCode': 'padsquad', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708', + 'bidderRequestId': 'requestId', + 'bidRequest': [{ + 'bidder': 'padsquad', + 'params': { + 'unitId': 123456, + }, + 'placementCode': 'div-gpt-dummy-placement-code', + 'sizes': [ + [300, 250] + ], + 'bidId': 'bidId1', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' + }, + { + 'bidder': 'padsquad', + 'params': { + 'unitId': 123456, + }, + 'placementCode': 'div-gpt-dummy-placement-code', + 'sizes': [ + [300, 250] + ], + 'bidId': 'bidId2', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' + }], + 'start': 1487883186070, + 'auctionStart': 1487883186069, + 'timeout': 3000 +}; + +const RESPONSE = { + 'headers': null, + 'body': { + 'id': 'responseId', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'bidId1', + 'impid': 'bidId1', + 'price': 0.18, + 'adm': '', + 'adid': '144762342', + 'adomain': [ + 'http://dummydomain.com' + ], + 'iurl': 'iurl', + 'cid': '109', + 'crid': 'creativeId', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 334553, + 'auction_id': 514667951122925701, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + }, + { + 'id': 'bidId2', + 'impid': 'bidId2', + 'price': 0.1, + 'adm': '', + 'adid': '144762342', + 'adomain': [ + 'http://dummydomain.com' + ], + 'iurl': 'iurl', + 'cid': '109', + 'crid': 'creativeId', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 386046, + 'auction_id': 517067951122925501, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'seat' + } + ], + 'ext': { + 'usersync': { + 'sovrn': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlsovrn', + 'type': 'iframe' + } + ] + }, + 'appnexus': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlappnexus', + 'type': 'pixel' + } + ] + } + }, + 'responsetimemillis': { + 'appnexus': 127 + } + } + } +}; + +describe('Padsquad bid adapter', function () { + describe('isBidRequestValid', function () { + it('should accept request if only unitId is passed', function () { + let bid = { + bidder: 'padsquad', + params: { + unitId: 'unitId', + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should accept request if only networkId is passed', function () { + let bid = { + bidder: 'padsquad', + params: { + networkId: 'networkId', + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should accept request if only publisherId is passed', function () { + let bid = { + bidder: 'padsquad', + params: { + publisherId: 'publisherId', + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('reject requests without params', function () { + let bid = { + bidder: 'padsquad', + params: {} + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('creates request data', function () { + let request = spec.buildRequests(REQUEST.bidRequest, REQUEST); + + expect(request).to.exist.and.to.be.a('object'); + const payload = JSON.parse(request.data); + expect(payload.imp[0]).to.have.property('id', REQUEST.bidRequest[0].bidId); + expect(payload.imp[1]).to.have.property('id', REQUEST.bidRequest[1].bidId); + }); + + it('has gdpr data if applicable', function () { + const req = Object.assign({}, REQUEST, { + gdprConsent: { + consentString: 'consentString', + gdprApplies: true, + } + }); + let request = spec.buildRequests(REQUEST.bidRequest, req); + + const payload = JSON.parse(request.data); + expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); + expect(payload.regs.ext).to.have.property('gdpr', 1); + }); + }); + + describe('interpretResponse', function () { + it('have bids', function () { + let bids = spec.interpretResponse(RESPONSE, REQUEST); + expect(bids).to.be.an('array').that.is.not.empty; + validateBidOnIndex(0); + validateBidOnIndex(1); + + function validateBidOnIndex(index) { + expect(bids[index]).to.have.property('currency', 'USD'); + expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].impid); + expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price); + expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w); + expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h); + expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); + expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); + expect(bids[index]).to.have.property('ttl', 30); + expect(bids[index]).to.have.property('netRevenue', true); + } + }); + + it('handles empty response', function () { + const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}}); + const bids = spec.interpretResponse(EMPTY_RESP, REQUEST); + + expect(bids).to.be.empty; + }); + }); + + describe('getUserSyncs', function () { + it('handles no parameters', function () { + let opts = spec.getUserSyncs({}); + expect(opts).to.be.an('array').that.is.empty; + }); + it('returns non if sync is not allowed', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + + expect(opts).to.be.an('array').that.is.empty; + }); + + it('iframe sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [RESPONSE]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['sovrn'].syncs[0].url); + }); + + it('pixel sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [RESPONSE]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['appnexus'].syncs[0].url); + }); + + it('all sync enabled should return all results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [RESPONSE]); + + expect(opts.length).to.equal(2); + }); + }); +}); diff --git a/test/spec/modules/papyrusBidAdapter_spec.js b/test/spec/modules/papyrusBidAdapter_spec.js new file mode 100644 index 00000000000..289da56379a --- /dev/null +++ b/test/spec/modules/papyrusBidAdapter_spec.js @@ -0,0 +1,115 @@ +import { expect } from 'chai'; +import { spec } from 'modules/papyrusBidAdapter'; + +const ENDPOINT = '//prebid.papyrus.global'; +const BIDDER_CODE = 'papyrus'; + +describe('papyrus Adapter', function () { + describe('isBidRequestValid', function () { + let validBidReq = { + bidder: BIDDER_CODE, + params: { + address: '0xd7e2a771c5dcd5df7f789477356aecdaeee6c985', + placementId: 'b57e55fd18614b0591893e9fff41fbea' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(validBidReq)).to.equal(true); + }); + + let invalidBidReq = { + bidder: BIDDER_CODE, + params: { + 'placementId': '' + } + }; + + it('should not validate incorrect bid request', function () { + expect(spec.isBidRequestValid(invalidBidReq)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + bidder: BIDDER_CODE, + params: { + address: '0xd7e2a771c5dcd5df7f789477356aecdaeee6c985', + placementId: 'b57e55fd18614b0591893e9fff41fbea' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('sends valid bids count', function () { + const request = spec.buildRequests(bidRequests); + expect(request.data.length).to.equal(1); + }); + + it('sends all bid parameters', function () { + const request = spec.buildRequests(bidRequests); + expect(request.data[0]).to.have.all.keys(['address', 'placementId', 'sizes', 'bidId', 'transactionId']); + }); + + it('sedns valid sizes parameter', function () { + const request = spec.buildRequests(bidRequests); + expect(request.data[0].sizes[0]).to.equal('300x250'); + }); + }); + + describe('interpretResponse', function () { + let bidRequests = [ + { + bidder: BIDDER_CODE, + params: { + address: '0xd7e2a771c5dcd5df7f789477356aecdaeee6c985', + placementId: 'b57e55fd18614b0591893e9fff41fbea' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + let bidResponse = { + bids: [ + { + id: '1036e9746c-d186-49ae-90cb-2796d0f9b223', + adm: 'test adm', + cpm: 100, + height: 250, + width: 300 + } + ] + }; + + it('should build bid array', function () { + const request = spec.buildRequests(bidRequests); + const result = spec.interpretResponse({body: bidResponse}, request[0]); + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', function () { + const request = spec.buildRequests(bidRequests); + const result = spec.interpretResponse({body: bidResponse}, request[0]); + const bid = result[0]; + + expect(bid.cpm).to.equal(bidResponse.bids[0].cpm); + expect(bid.width).to.equal(bidResponse.bids[0].width); + expect(bid.height).to.equal(bidResponse.bids[0].height); + }); + }); +}); diff --git a/test/spec/modules/peak226BidAdapter_spec.js b/test/spec/modules/peak226BidAdapter_spec.js index f85e46c4289..8b8157225bb 100644 --- a/test/spec/modules/peak226BidAdapter_spec.js +++ b/test/spec/modules/peak226BidAdapter_spec.js @@ -4,11 +4,11 @@ import { newBidder } from 'src/adapters/bidderFactory'; const URL = 'a.ad216.com/header_bid'; -describe('PeakAdapter', () => { +describe('PeakAdapter', function () { const adapter = newBidder(spec); - describe('isBidRequestValid', () => { - it('should return true when required params found', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { const bid = { params: { uid: 123 @@ -18,7 +18,7 @@ describe('PeakAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { const bid = { params: {} }; @@ -27,7 +27,7 @@ describe('PeakAdapter', () => { }); }); - // xdescribe('buildRequests', () => { + // xdescribe('buildRequests', function () { // const bidRequests = [ // { // params: { @@ -36,7 +36,7 @@ describe('PeakAdapter', () => { // } // ]; - // it('sends bid request to URL via GET', () => { + // it('sends bid request to URL via GET', function () { // const request = spec.buildRequests(bidRequests); // expect(request.url).to.equal(`${URL}?uids=1234`); @@ -44,8 +44,8 @@ describe('PeakAdapter', () => { // }); // }); - describe('interpretResponse', () => { - it('should handle empty response', () => { + describe('interpretResponse', function () { + it('should handle empty response', function () { let bids = spec.interpretResponse( {}, { @@ -56,7 +56,7 @@ describe('PeakAdapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should handle no seatbid returned', () => { + it('should handle no seatbid returned', function () { let response = {}; let bids = spec.interpretResponse( @@ -69,7 +69,7 @@ describe('PeakAdapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should handle empty seatbid returned', () => { + it('should handle empty seatbid returned', function () { let response = { seatbid: [] }; let bids = spec.interpretResponse( @@ -82,7 +82,7 @@ describe('PeakAdapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should handle seatbid returned bids', () => { + it('should handle seatbid returned bids', function () { const bidsMap = { 1: [{ bidId: 11 }] }; const bid = { price: 0.2, diff --git a/test/spec/modules/piximediaBidAdapter_spec.js b/test/spec/modules/piximediaBidAdapter_spec.js new file mode 100644 index 00000000000..95e03734345 --- /dev/null +++ b/test/spec/modules/piximediaBidAdapter_spec.js @@ -0,0 +1,103 @@ +import { expect } from 'chai'; +import { spec } from 'modules/piximediaBidAdapter'; + +describe('piximediaAdapterTest', function() { + describe('bidRequestValidity', function() { + it('bidRequest with site ID and placement ID param', function() { + expect(spec.isBidRequestValid({ + bidder: 'piximedia', + params: { + 'siteId': 'PIXIMEDIA_PREBID10', + 'placementId': 'RG' + }, + })).to.equal(true); + }); + + it('bidRequest with no required params', function() { + expect(spec.isBidRequestValid({ + bidder: 'piximedia', + params: { + }, + })).to.equal(false); + }); + }); + + describe('bidRequest', function() { + const bidRequests = [{ + 'bidder': 'piximedia', + 'params': { + 'siteId': 'PIXIMEDIA_PREBID10', + 'placementId': 'RG' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [300, 250], + 'bidId': '51ef8751f9aead', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + it('bidRequest HTTP method', function() { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function(requestItem) { + expect(requestItem.method).to.equal('GET'); + }); + }); + + it('bidRequest data', function() { + const requests = spec.buildRequests(bidRequests); + expect(typeof requests[0].data.timestamp).to.equal('number'); + expect(requests[0].data.pbsizes).to.equal('["300x250"]'); + expect(requests[0].data.pver).to.equal('1.0'); + expect(requests[0].data.pbparams).to.equal(JSON.stringify(bidRequests[0].params)); + expect(requests[0].data.pbwidth).to.equal('300'); + expect(requests[0].data.pbheight).to.equal('250'); + expect(requests[0].data.pbbidid).to.equal('51ef8751f9aead'); + }); + }); + + describe('interpretResponse', function() { + const bidRequest = { + 'method': 'GET', + 'url': 'https://ad.piximedia.com/', + 'data': { + 'ver': 2, + 'hb': 1, + 'output': 'js', + 'pub': 267, + 'zone': 62546, + 'width': '300', + 'height': '250', + 'callback': 'json', + 'callback_uid': '51ef8751f9aead', + 'url': 'https://example.com', + 'cb': '', + } + }; + + const bidResponse = { + body: { + 'bidId': '51ef8751f9aead', + 'cpm': 4.2, + 'width': '300', + 'height': '250', + 'creative_id': '1234', + 'currency': 'EUR', + 'adm': '
    ', + }, + headers: {} + }; + + it('result is correct', function() { + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result[0].requestId).to.equal('51ef8751f9aead'); + expect(result[0].cpm).to.equal(4.2); + expect(result[0].width).to.equal('300'); + expect(result[0].height).to.equal('250'); + expect(result[0].creativeId).to.equal('1234'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].ttl).to.equal(300); + expect(result[0].ad).to.equal('
    '); + }); + }); +}); diff --git a/test/spec/modules/platformioBidAdapter_spec.js b/test/spec/modules/platformioBidAdapter_spec.js index fc8ba5bf45e..4ef2dc1bba0 100644 --- a/test/spec/modules/platformioBidAdapter_spec.js +++ b/test/spec/modules/platformioBidAdapter_spec.js @@ -1,316 +1,335 @@ -import {expect} from 'chai'; -import {spec} from 'modules/platformioBidAdapter'; -import {getTopWindowLocation} from 'src/utils'; -import {newBidder} from 'src/adapters/bidderFactory'; - -describe('Platform.io Adapter Tests', () => { - const slotConfigs = [{ - placementCode: '/DfpAccount1/slot1', - bidId: 'bid12345', - mediaType: 'banner', - params: { - pubId: '29521', - siteId: '26047', - placementId: '123', - size: '300x250', - bidFloor: '0.001', - ifa: 'IFA', - latitude: '40.712775', - longitude: '-74.005973' - } - }, { - placementCode: '/DfpAccount2/slot2', - bidId: 'bid23456', - mediaType: 'banner', - params: { - pubId: '29521', - siteId: '26047', - placementId: '1234', - size: '728x90', - bidFloor: '0.000001', - } - }]; - const nativeSlotConfig = [{ - placementCode: '/DfpAccount1/slot3', - bidId: 'bid12345', - mediaType: 'native', - nativeParams: { - title: { required: true, len: 200 }, - body: {}, - image: { wmin: 100 }, - sponsoredBy: { }, - icon: { } - }, - params: { - pubId: '29521', - placementId: '123', - siteId: '26047' - } - }]; - const videoSlotConfig = [{ - placementCode: '/DfpAccount1/slot4', - bidId: 'bid12345678', - mediaType: 'video', - video: { - skippable: true - }, - params: { - pubId: '29521', - placementId: '1234567', - siteId: '26047', - size: '640x480' - } - }]; - const appSlotConfig = [{ - placementCode: '/DfpAccount1/slot5', - bidId: 'bid12345', - params: { - pubId: '29521', - placementId: '1234', - app: { - id: '1111', - name: 'app name', - bundle: 'io.platform.apps', - storeUrl: 'http://platform.io/apps', - domain: 'platform.io' - } - } - }]; - - it('Verify build request', () => { - const request = spec.buildRequests(slotConfigs); - expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - // site object - expect(ortbRequest.site).to.not.equal(null); - expect(ortbRequest.site.publisher).to.not.equal(null); - expect(ortbRequest.site.publisher.id).to.equal('29521'); - expect(ortbRequest.site.ref).to.equal(window.top.document.referrer); - expect(ortbRequest.site.page).to.equal(getTopWindowLocation().href); - expect(ortbRequest.imp).to.have.lengthOf(2); - // device object - expect(ortbRequest.device).to.not.equal(null); - expect(ortbRequest.device.ua).to.equal(navigator.userAgent); - expect(ortbRequest.device.ifa).to.equal('IFA'); - expect(ortbRequest.device.geo.lat).to.equal('40.712775'); - expect(ortbRequest.device.geo.lon).to.equal('-74.005973'); - // slot 1 - expect(ortbRequest.imp[0].tagid).to.equal('123'); - expect(ortbRequest.imp[0].banner).to.not.equal(null); - expect(ortbRequest.imp[0].banner.w).to.equal(300); - expect(ortbRequest.imp[0].banner.h).to.equal(250); - expect(ortbRequest.imp[0].bidfloor).to.equal('0.001'); - // slot 2 - expect(ortbRequest.imp[1].tagid).to.equal('1234'); - expect(ortbRequest.imp[1].banner).to.not.equal(null); - expect(ortbRequest.imp[1].banner.w).to.equal(728); - expect(ortbRequest.imp[1].banner.h).to.equal(90); - expect(ortbRequest.imp[1].bidfloor).to.equal('0.000001'); - }); - - it('Verify parse response', () => { - const request = spec.buildRequests(slotConfigs); - const ortbRequest = JSON.parse(request.data); - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'This is an Ad' - }] - }], - cur: 'USD' - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(1); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.equal('This is an Ad'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.adId).to.equal('bid12345'); - expect(bid.creativeId).to.equal('bid12345'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('USD'); - expect(bid.ttl).to.equal(360); - }); - - it('Verify full passback', () => { - const request = spec.buildRequests(slotConfigs); - const bids = spec.interpretResponse({ body: null }, request) - expect(bids).to.have.lengthOf(0); - }); - - it('Verify Native request', () => { - const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - // native impression - expect(ortbRequest.imp[0].tagid).to.equal('123'); - const nativePart = ortbRequest.imp[0]['native']; - expect(nativePart).to.not.equal(null); - expect(nativePart.ver).to.equal('1.1'); - expect(nativePart.request).to.not.equal(null); - // native request assets - const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); - expect(nativeRequest).to.not.equal(null); - expect(nativeRequest.assets).to.have.lengthOf(5); - expect(nativeRequest.assets[0].id).to.equal(1); - expect(nativeRequest.assets[1].id).to.equal(2); - expect(nativeRequest.assets[2].id).to.equal(3); - expect(nativeRequest.assets[3].id).to.equal(4); - expect(nativeRequest.assets[4].id).to.equal(5); - expect(nativeRequest.assets[0].required).to.equal(1); - expect(nativeRequest.assets[0].title).to.not.equal(null); - expect(nativeRequest.assets[0].title.len).to.equal(200); - expect(nativeRequest.assets[1].title).to.be.undefined; - expect(nativeRequest.assets[1].data).to.not.equal(null); - expect(nativeRequest.assets[1].data.type).to.equal(2); - expect(nativeRequest.assets[1].data.len).to.equal(200); - expect(nativeRequest.assets[2].required).to.equal(0); - expect(nativeRequest.assets[3].img).to.not.equal(null); - expect(nativeRequest.assets[3].img.wmin).to.equal(50); - expect(nativeRequest.assets[3].img.hmin).to.equal(50); - expect(nativeRequest.assets[3].img.type).to.equal(1); - expect(nativeRequest.assets[4].img).to.not.equal(null); - expect(nativeRequest.assets[4].img.wmin).to.equal(100); - expect(nativeRequest.assets[4].img.hmin).to.equal(150); - expect(nativeRequest.assets[4].img.type).to.equal(3); - }); - - it('Verify Native response', () => { - const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - const nativeResponse = { - 'native': { - assets: [ - { id: 1, title: { text: 'Ad Title' } }, - { id: 2, data: { value: 'Test description' } }, - { id: 3, data: { value: 'Brand' } }, - { id: 4, img: { url: 'https://s3.amazonaws.com/adx1public/creatives_icon.png', w: 100, h: 100 } }, - { id: 5, img: { url: 'https://s3.amazonaws.com/adx1public/creatives_image.png', w: 300, h: 300 } } - ], - link: { url: 'http://brand.com/' } - } - }; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - nurl: 'http://rtb.adx1.com/log', - adm: JSON.stringify(nativeResponse) - }] - }], - cur: 'USD', - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - // verify bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.adId).to.equal('bid12345'); - expect(bid.ad).to.be.undefined; - expect(bid.mediaType).to.equal('native'); - const nativeBid = bid['native']; - expect(nativeBid).to.not.equal(null); - expect(nativeBid.title).to.equal('Ad Title'); - expect(nativeBid.sponsoredBy).to.equal('Brand'); - expect(nativeBid.icon.url).to.equal('https://s3.amazonaws.com/adx1public/creatives_icon.png'); - expect(nativeBid.image.url).to.equal('https://s3.amazonaws.com/adx1public/creatives_image.png'); - expect(nativeBid.image.width).to.equal(300); - expect(nativeBid.image.height).to.equal(300); - expect(nativeBid.icon.width).to.equal(100); - expect(nativeBid.icon.height).to.equal(100); - expect(nativeBid.clickUrl).to.equal(encodeURIComponent('http://brand.com/')); - expect(nativeBid.impressionTrackers).to.have.lengthOf(1); - expect(nativeBid.impressionTrackers[0]).to.equal('http://rtb.adx1.com/log'); - }); - - it('Verify Video request', () => { - const request = spec.buildRequests(videoSlotConfig); - expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const videoRequest = JSON.parse(request.data); - // site object - expect(videoRequest.site).to.not.equal(null); - expect(videoRequest.site.publisher.id).to.equal('29521'); - expect(videoRequest.site.ref).to.equal(window.top.document.referrer); - expect(videoRequest.site.page).to.equal(getTopWindowLocation().href); - // device object - expect(videoRequest.device).to.not.equal(null); - expect(videoRequest.device.ua).to.equal(navigator.userAgent); - // slot 1 - expect(videoRequest.imp[0].tagid).to.equal('1234567'); - expect(videoRequest.imp[0].video).to.not.equal(null); - expect(videoRequest.imp[0].video.w).to.equal(640); - expect(videoRequest.imp[0].video.h).to.equal(480); - expect(videoRequest.imp[0].banner).to.equal(null); - expect(videoRequest.imp[0].native).to.equal(null); - }); - - it('Verify parse video response', () => { - const request = spec.buildRequests(videoSlotConfig); - const videoRequest = JSON.parse(request.data); - const videoResponse = { - seatbid: [{ - bid: [{ - impid: videoRequest.imp[0].id, - price: 1.90, - adm: 'http://vid.example.com/9876', - crid: '510511_754567308' - }] - }], - cur: 'USD' - }; - const bids = spec.interpretResponse({ body: videoResponse }, request); - expect(bids).to.have.lengthOf(1); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.90); - expect(bid.vastUrl).to.equal('http://vid.example.com/9876'); - expect(bid.crid).to.equal('510511_754567308'); - expect(bid.width).to.equal(640); - expect(bid.height).to.equal(480); - expect(bid.adId).to.equal('bid12345678'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('USD'); - expect(bid.ttl).to.equal(360); - }); - - it('Verifies bidder code', () => { - expect(spec.code).to.equal('platformio'); - }); - - it('Verifies supported media types', () => { - expect(spec.supportedMediaTypes).to.have.lengthOf(3); - expect(spec.supportedMediaTypes[0]).to.equal('banner'); - expect(spec.supportedMediaTypes[1]).to.equal('native'); - expect(spec.supportedMediaTypes[2]).to.equal('video'); - }); - - it('Verifies if bid request valid', () => { - expect(spec.isBidRequestValid(slotConfigs[0])).to.equal(true); - expect(spec.isBidRequestValid(slotConfigs[1])).to.equal(true); - expect(spec.isBidRequestValid(nativeSlotConfig[0])).to.equal(true); - expect(spec.isBidRequestValid(videoSlotConfig[0])).to.equal(true); - }); - - it('Verify app requests', () => { - const request = spec.buildRequests(appSlotConfig); - const ortbRequest = JSON.parse(request.data); - expect(ortbRequest.site).to.equal(null); - expect(ortbRequest.app).to.not.be.null; - expect(ortbRequest.app.publisher).to.not.equal(null); - expect(ortbRequest.app.publisher.id).to.equal('29521'); - expect(ortbRequest.app.id).to.equal('1111'); - expect(ortbRequest.app.name).to.equal('app name'); - expect(ortbRequest.app.bundle).to.equal('io.platform.apps'); - expect(ortbRequest.app.storeurl).to.equal('http://platform.io/apps'); - expect(ortbRequest.app.domain).to.equal('platform.io'); - }); -}); +import {expect} from 'chai'; +import {spec} from 'modules/platformioBidAdapter'; +import {getTopWindowLocation} from 'src/utils'; +import {newBidder} from 'src/adapters/bidderFactory'; + +describe('Platform.io Adapter Tests', function () { + const slotConfigs = [{ + placementCode: '/DfpAccount1/slot1', + bidId: 'bid12345', + mediaType: 'banner', + params: { + pubId: '29521', + siteId: '26047', + placementId: '123', + size: '300x250', + bidFloor: '0.001', + ifa: 'IFA', + latitude: '40.712775', + longitude: '-74.005973' + } + }, { + placementCode: '/DfpAccount2/slot2', + bidId: 'bid23456', + mediaType: 'banner', + params: { + pubId: '29521', + siteId: '26047', + placementId: '1234', + size: '728x90', + bidFloor: '0.000001', + } + }]; + const nativeSlotConfig = [{ + placementCode: '/DfpAccount1/slot3', + bidId: 'bid12345', + mediaType: 'native', + nativeParams: { + title: { required: true, len: 200 }, + body: {}, + image: { wmin: 100 }, + sponsoredBy: { }, + icon: { } + }, + params: { + pubId: '29521', + placementId: '123', + siteId: '26047' + } + }]; + const videoSlotConfig = [{ + placementCode: '/DfpAccount1/slot4', + bidId: 'bid12345678', + mediaType: 'video', + video: { + skippable: true + }, + params: { + pubId: '29521', + placementId: '1234567', + siteId: '26047', + size: '640x480' + } + }]; + const appSlotConfig = [{ + placementCode: '/DfpAccount1/slot5', + bidId: 'bid12345', + params: { + pubId: '29521', + placementId: '1234', + app: { + id: '1111', + name: 'app name', + bundle: 'io.platform.apps', + storeUrl: 'http://platform.io/apps', + domain: 'platform.io' + } + } + }]; + + it('Verify build request', function () { + const request = spec.buildRequests(slotConfigs); + expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + // site object + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.publisher).to.not.equal(null); + expect(ortbRequest.site.publisher.id).to.equal('29521'); + expect(ortbRequest.site.ref).to.equal(window.top.document.referrer); + expect(ortbRequest.site.page).to.equal(getTopWindowLocation().href); + expect(ortbRequest.imp).to.have.lengthOf(2); + // device object + expect(ortbRequest.device).to.not.equal(null); + expect(ortbRequest.device.ua).to.equal(navigator.userAgent); + expect(ortbRequest.device.ifa).to.equal('IFA'); + expect(ortbRequest.device.geo.lat).to.equal('40.712775'); + expect(ortbRequest.device.geo.lon).to.equal('-74.005973'); + // slot 1 + expect(ortbRequest.imp[0].tagid).to.equal('123'); + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); + expect(ortbRequest.imp[0].bidfloor).to.equal('0.001'); + // slot 2 + expect(ortbRequest.imp[1].tagid).to.equal('1234'); + expect(ortbRequest.imp[1].banner).to.not.equal(null); + expect(ortbRequest.imp[1].banner.w).to.equal(728); + expect(ortbRequest.imp[1].banner.h).to.equal(90); + expect(ortbRequest.imp[1].bidfloor).to.equal('0.000001'); + }); + + it('Verify parse response', function () { + const request = spec.buildRequests(slotConfigs); + const ortbRequest = JSON.parse(request.data); + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'This is an Ad' + }] + }], + cur: 'USD' + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + expect(bids).to.have.lengthOf(1); + // verify first bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.equal('This is an Ad'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.adId).to.equal('bid12345'); + expect(bid.creativeId).to.equal('bid12345'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(360); + }); + + it('Verify full passback', function () { + const request = spec.buildRequests(slotConfigs); + const bids = spec.interpretResponse({ body: null }, request) + expect(bids).to.have.lengthOf(0); + }); + + it('Verify Native request', function () { + const request = spec.buildRequests(nativeSlotConfig); + expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + // native impression + expect(ortbRequest.imp[0].tagid).to.equal('123'); + const nativePart = ortbRequest.imp[0]['native']; + expect(nativePart).to.not.equal(null); + expect(nativePart.ver).to.equal('1.1'); + expect(nativePart.request).to.not.equal(null); + // native request assets + const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); + expect(nativeRequest).to.not.equal(null); + expect(nativeRequest.assets).to.have.lengthOf(5); + expect(nativeRequest.assets[0].id).to.equal(1); + expect(nativeRequest.assets[1].id).to.equal(2); + expect(nativeRequest.assets[2].id).to.equal(3); + expect(nativeRequest.assets[3].id).to.equal(4); + expect(nativeRequest.assets[4].id).to.equal(5); + expect(nativeRequest.assets[0].required).to.equal(1); + expect(nativeRequest.assets[0].title).to.not.equal(null); + expect(nativeRequest.assets[0].title.len).to.equal(200); + expect(nativeRequest.assets[1].title).to.be.undefined; + expect(nativeRequest.assets[1].data).to.not.equal(null); + expect(nativeRequest.assets[1].data.type).to.equal(2); + expect(nativeRequest.assets[1].data.len).to.equal(200); + expect(nativeRequest.assets[2].required).to.equal(0); + expect(nativeRequest.assets[3].img).to.not.equal(null); + expect(nativeRequest.assets[3].img.wmin).to.equal(50); + expect(nativeRequest.assets[3].img.hmin).to.equal(50); + expect(nativeRequest.assets[3].img.type).to.equal(1); + expect(nativeRequest.assets[4].img).to.not.equal(null); + expect(nativeRequest.assets[4].img.wmin).to.equal(100); + expect(nativeRequest.assets[4].img.hmin).to.equal(150); + expect(nativeRequest.assets[4].img.type).to.equal(3); + }); + + it('Verify Native response', function () { + const request = spec.buildRequests(nativeSlotConfig); + expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + const nativeResponse = { + 'native': { + assets: [ + { id: 1, title: { text: 'Ad Title' } }, + { id: 2, data: { value: 'Test description' } }, + { id: 3, data: { value: 'Brand' } }, + { id: 4, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_icon.png', w: 100, h: 100 } }, + { id: 5, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_image.png', w: 300, h: 300 } } + ], + link: { url: 'http://brand.com/' } + } + }; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + nurl: 'http://rtb.adx1.com/log', + adm: JSON.stringify(nativeResponse) + }] + }], + cur: 'USD', + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + // verify bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.adId).to.equal('bid12345'); + expect(bid.ad).to.be.undefined; + expect(bid.mediaType).to.equal('native'); + const nativeBid = bid['native']; + expect(nativeBid).to.not.equal(null); + expect(nativeBid.title).to.equal('Ad Title'); + expect(nativeBid.sponsoredBy).to.equal('Brand'); + expect(nativeBid.icon.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_icon.png'); + expect(nativeBid.image.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_image.png'); + expect(nativeBid.image.width).to.equal(300); + expect(nativeBid.image.height).to.equal(300); + expect(nativeBid.icon.width).to.equal(100); + expect(nativeBid.icon.height).to.equal(100); + expect(nativeBid.clickUrl).to.equal(encodeURIComponent('http://brand.com/')); + expect(nativeBid.impressionTrackers).to.have.lengthOf(1); + expect(nativeBid.impressionTrackers[0]).to.equal('http://rtb.adx1.com/log'); + }); + + it('Verify Video request', function () { + const request = spec.buildRequests(videoSlotConfig); + expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); + expect(request.method).to.equal('POST'); + const videoRequest = JSON.parse(request.data); + // site object + expect(videoRequest.site).to.not.equal(null); + expect(videoRequest.site.publisher.id).to.equal('29521'); + expect(videoRequest.site.ref).to.equal(window.top.document.referrer); + expect(videoRequest.site.page).to.equal(getTopWindowLocation().href); + // device object + expect(videoRequest.device).to.not.equal(null); + expect(videoRequest.device.ua).to.equal(navigator.userAgent); + // slot 1 + expect(videoRequest.imp[0].tagid).to.equal('1234567'); + expect(videoRequest.imp[0].video).to.not.equal(null); + expect(videoRequest.imp[0].video.w).to.equal(640); + expect(videoRequest.imp[0].video.h).to.equal(480); + expect(videoRequest.imp[0].banner).to.equal(null); + expect(videoRequest.imp[0].native).to.equal(null); + }); + + it('Verify parse video response', function () { + const request = spec.buildRequests(videoSlotConfig); + const videoRequest = JSON.parse(request.data); + const videoResponse = { + seatbid: [{ + bid: [{ + impid: videoRequest.imp[0].id, + price: 1.90, + adm: 'http://vid.example.com/9876', + crid: '510511_754567308' + }] + }], + cur: 'USD' + }; + const bids = spec.interpretResponse({ body: videoResponse }, request); + expect(bids).to.have.lengthOf(1); + // verify first bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.90); + expect(bid.vastUrl).to.equal('http://vid.example.com/9876'); + expect(bid.crid).to.equal('510511_754567308'); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(480); + expect(bid.adId).to.equal('bid12345678'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(360); + }); + + it('Verifies bidder code', function () { + expect(spec.code).to.equal('platformio'); + }); + + it('Verifies supported media types', function () { + expect(spec.supportedMediaTypes).to.have.lengthOf(3); + expect(spec.supportedMediaTypes[0]).to.equal('banner'); + expect(spec.supportedMediaTypes[1]).to.equal('native'); + expect(spec.supportedMediaTypes[2]).to.equal('video'); + }); + + it('Verifies if bid request valid', function () { + expect(spec.isBidRequestValid(slotConfigs[0])).to.equal(true); + expect(spec.isBidRequestValid(slotConfigs[1])).to.equal(true); + expect(spec.isBidRequestValid(nativeSlotConfig[0])).to.equal(true); + expect(spec.isBidRequestValid(videoSlotConfig[0])).to.equal(true); + }); + + it('Verify app requests', function () { + const request = spec.buildRequests(appSlotConfig); + const ortbRequest = JSON.parse(request.data); + expect(ortbRequest.site).to.equal(null); + expect(ortbRequest.app).to.not.be.null; + expect(ortbRequest.app.publisher).to.not.equal(null); + expect(ortbRequest.app.publisher.id).to.equal('29521'); + expect(ortbRequest.app.id).to.equal('1111'); + expect(ortbRequest.app.name).to.equal('app name'); + expect(ortbRequest.app.bundle).to.equal('io.platform.apps'); + expect(ortbRequest.app.storeurl).to.equal('http://platform.io/apps'); + expect(ortbRequest.app.domain).to.equal('platform.io'); + }); + + it('Verify GDPR', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'serialized_gpdr_data' + } + }; + const request = spec.buildRequests(slotConfigs, bidderRequest); + expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + expect(ortbRequest.user).to.not.equal(null); + expect(ortbRequest.user.ext).to.not.equal(null); + expect(ortbRequest.user.ext.consent).to.equal('serialized_gpdr_data'); + expect(ortbRequest.regs).to.not.equal(null); + expect(ortbRequest.regs.ext).to.not.equal(null); + expect(ortbRequest.regs.ext.gdpr).to.equal(1); + }); +}); diff --git a/test/spec/modules/playgroundxyzBidAdapter_spec.js b/test/spec/modules/playgroundxyzBidAdapter_spec.js new file mode 100644 index 00000000000..a90564003f4 --- /dev/null +++ b/test/spec/modules/playgroundxyzBidAdapter_spec.js @@ -0,0 +1,213 @@ +import { expect } from 'chai'; +import { spec } from 'modules/playgroundxyzBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { deepClone } from 'src/utils'; + +const URL = 'https://ads.playground.xyz/host-config/prebid?v=2'; +const GDPR_CONSENT = 'XYZ-CONSENT'; + +describe('playgroundxyzBidAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'playgroundxyz', + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [320, 50]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'playgroundxyz', + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('sends bid request to ENDPOINT via POST', function () { + let bidRequest = Object.assign([], bidRequests); + + const request = spec.buildRequests(bidRequest); + const data = JSON.parse(request.data); + const banner = data.imp[0].banner; + + expect(Object.keys(data.imp[0].ext)).to.have.members(['appnexus', 'pxyz']); + expect([banner.w, banner.h]).to.deep.equal([300, 250]); + expect(banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(request.url).to.equal(URL); + expect(request.method).to.equal('POST'); + }); + }) + + describe('interpretResponse', function () { + let response = { + 'id': 'bidd_id', + 'seatbid': [ { + 'bid': [ + { + 'id': '4434762738980910431', + 'impid': '221f2bdc1fbc31', + 'price': 1, + 'adid': '91673066', + 'adm': '', + 'adomain': [ 'pg.xyz' ], + 'iurl': 'http://pgxyz.com/cr?id=91673066', + 'cid': 'c_id', + 'crid': 'c_rid', + 'h': 50, + 'w': 320, + 'ext': { + 'appnexus': { + 'brand_id': 1, + 'auction_id': 1087655594852566000, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + ], + 'seat': '4321' + }], + 'bidid': '6894227111893743356', + 'cur': 'AUD' + }; + + let bidderRequest = { + 'bidderCode': 'playgroundxyz' + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '221f2bdc1fbc31', + 'cpm': 1, + 'creativeId': 91673066, + 'width': 300, + 'height': 50, + 'ad': '', + 'mediaType': 'banner', + 'currency': 'AUD', + 'ttl': 300, + 'netRevenue': true + } + ]; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid response', function () { + const response = undefined; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'playgroundxyz', + 'params': { + 'publisherId': 'PUB_FAKE' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '321db112312as', + 'bidderRequestId': '23edabce2731sd6', + 'auctionId': '12as040790a475' + } + ]; + + it('should not populate GDPR', function () { + let bidRequest = Object.assign([], bidRequests); + const request = spec.buildRequests(bidRequest); + let data = JSON.parse(request.data); + expect(data).to.not.have.property('user'); + expect(data).to.not.have.property('regs'); + }); + + it('should populate GDPR and consent string when consetString is presented but not gdpApplies', function () { + let bidRequest = Object.assign([], bidRequests); + const request = spec.buildRequests(bidRequest, {gdprConsent: {consentString: GDPR_CONSENT}}); + let data = JSON.parse(request.data); + expect(data.regs.ext.gdpr).to.equal(0); + expect(data.user.ext.consent).to.equal('XYZ-CONSENT'); + }); + + it('should populate GDPR and consent string when gdpr is set to true', function () { + let bidRequest = Object.assign([], bidRequests); + const request = spec.buildRequests(bidRequest, {gdprConsent: {gdprApplies: true, consentString: GDPR_CONSENT}}); + let data = JSON.parse(request.data); + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.user.ext.consent).to.equal('XYZ-CONSENT'); + }); + + it('should populate GDPR and consent string when gdpr is set to false', function () { + let bidRequest = Object.assign([], bidRequests); + const request = spec.buildRequests(bidRequest, {gdprConsent: {gdprApplies: false, consentString: GDPR_CONSENT}}); + let data = JSON.parse(request.data); + expect(data.regs.ext.gdpr).to.equal(0); + expect(data.user.ext.consent).to.equal('XYZ-CONSENT'); + }); + }); + + describe('getUserSyncs', function () { + const syncUrl = '//ib.adnxs.com/getuidnb?https://ads.playground.xyz/usersync?partner=appnexus&uid=$UID'; + + describe('when iframeEnabled is true', function () { + const syncOptions = { + 'iframeEnabled': true + } + it('should return one image type user sync pixel', function () { + let result = spec.getUserSyncs(syncOptions); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image') + expect(result[0].url).to.equal(syncUrl); + }); + }); + + describe('when iframeEnabled is false', function () { + const syncOptions = { + 'iframeEnabled': false + } + it('should return one image type user sync pixel', function () { + let result = spec.getUserSyncs(syncOptions); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image') + expect(result[0].url).to.equal(syncUrl); + }); + }); + }) +}); diff --git a/test/spec/modules/polluxBidAdapter_spec.js b/test/spec/modules/polluxBidAdapter_spec.js index ea550fecd71..ad30771e15b 100644 --- a/test/spec/modules/polluxBidAdapter_spec.js +++ b/test/spec/modules/polluxBidAdapter_spec.js @@ -24,7 +24,7 @@ describe('POLLUX Bid Adapter tests', function () { params: {zone: '276'} }]; - it('TEST: verify buildRequests no valid bid requests', () => { + it('TEST: verify buildRequests no valid bid requests', function () { let request = spec.buildRequests(false); expect(request).to.not.equal(null); expect(request).to.not.have.property('method'); @@ -47,7 +47,7 @@ describe('POLLUX Bid Adapter tests', function () { expect(request).to.not.have.property('data'); }); - it('TEST: verify buildRequests single bid', () => { + it('TEST: verify buildRequests single bid', function () { const request = spec.buildRequests(setup_single_bid); expect(request.method).to.equal('POST'); const requested_bids = JSON.parse(request.data); @@ -70,7 +70,7 @@ describe('POLLUX Bid Adapter tests', function () { expect(requested_bids[0].zones).to.equal('1806,276'); }); - it('TEST: verify buildRequests multi bid', () => { + it('TEST: verify buildRequests multi bid', function () { const request = spec.buildRequests(setup_multi_bid); expect(request.method).to.equal('POST'); const requested_bids = JSON.parse(request.data); @@ -102,7 +102,7 @@ describe('POLLUX Bid Adapter tests', function () { expect(requested_bids[1].zones).to.equal('276'); }); - it('TEST: verify interpretResponse empty', () => { + it('TEST: verify interpretResponse empty', function () { let bids = spec.interpretResponse(false, {}); expect(bids).to.not.equal(null); expect(bids).to.have.lengthOf(0); @@ -117,7 +117,7 @@ describe('POLLUX Bid Adapter tests', function () { expect(bids).to.have.lengthOf(0); }); - it('TEST: verify interpretResponse ad_type url', () => { + it('TEST: verify interpretResponse ad_type url', function () { const serverResponse = { body: [ { @@ -147,7 +147,7 @@ describe('POLLUX Bid Adapter tests', function () { expect(bids[0]).to.not.have.property('ad'); }); - it('TEST: verify interpretResponse ad_type html', () => { + it('TEST: verify interpretResponse ad_type html', function () { const serverResponse = { body: [ { @@ -176,7 +176,7 @@ describe('POLLUX Bid Adapter tests', function () { expect(bids[0].ad).to.equal('

    I am an ad

    '); }); - it('TEST: verify url and query params', () => { + it('TEST: verify url and query params', function () { const URL = require('url-parse'); const querystringify = require('querystringify'); const request = spec.buildRequests(setup_single_bid); @@ -188,7 +188,7 @@ describe('POLLUX Bid Adapter tests', function () { expect(parsedQuery).to.have.property('domain').and.to.have.length.above(1); }); - it('TEST: verify isBidRequestValid', () => { + it('TEST: verify isBidRequestValid', function () { expect(spec.isBidRequestValid({})).to.equal(false); expect(spec.isBidRequestValid({params: {}})).to.equal(false); expect(spec.isBidRequestValid(setup_single_bid[0])).to.equal(true); @@ -196,11 +196,11 @@ describe('POLLUX Bid Adapter tests', function () { expect(spec.isBidRequestValid(setup_multi_bid[1])).to.equal(true); }); - it('TEST: verify bidder code', () => { + it('TEST: verify bidder code', function () { expect(spec.code).to.equal('pollux'); }); - it('TEST: verify bidder aliases', () => { + it('TEST: verify bidder aliases', function () { expect(spec.aliases).to.have.lengthOf(1); expect(spec.aliases[0]).to.equal('plx'); }); diff --git a/test/spec/modules/polymorphBidAdapter_spec.js b/test/spec/modules/polymorphBidAdapter_spec.js new file mode 100644 index 00000000000..6fd4bd90288 --- /dev/null +++ b/test/spec/modules/polymorphBidAdapter_spec.js @@ -0,0 +1,219 @@ +import { expect } from 'chai'; +import { polymorphAdapterSpec } from 'modules/polymorphBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'polymorph'; +const ENDPOINT_URL = '//api.adsnative.com/v1/ad-template.json'; +const PLACEMENT_ID = 'ping'; +const NETWORK_KEY = 'abcd1234'; +const WIDGET_ID = 'xyz'; +const CATEGORIES = 'IAB1,IAB2'; + +const spec = newBidder(polymorphAdapterSpec).getSpec(); + +const bidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': { + 'placementId': PLACEMENT_ID + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', +}, +{ + 'bidder': BIDDER_CODE, + 'params': { + 'placementId': PLACEMENT_ID, + 'defaultWidth': 300, + 'defaultHeight': 600, + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[700, 250], [300, 600]], + 'bidId': '30b31c1838de1d', + 'bidderRequestId': '22edbae2733bf7', + 'auctionId': '1d1a030790a476', +}, +{ + 'bidder': BIDDER_CODE, + 'params': { + 'network_key': NETWORK_KEY, + 'widget_id': WIDGET_ID, + 'cat': CATEGORIES + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[700, 250], [300, 600]], + 'bidId': '30b31c1838de1f', + 'bidderRequestId': '22edbae2733bf7', + 'auctionId': '1d1a030790a476', +}]; + +describe('Polymorph adapter test', function () { + describe('.code', function () { + it('should return a bidder code of polymorph', function () { + expect(spec.code).to.eql(BIDDER_CODE); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + }); + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bidRequests[2])).to.equal(true); + }); + + it('should return false if req has no placementId', function () { + const invalidBidRequest = { + bidder: BIDDER_CODE, + params: { + someKey: 'abc123' + } + }; + expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); + }); + + it('should return false if req has wrong bidder code', function () { + const invalidBidRequest = { + bidder: 'something', + params: { + someKey: 'abc123' + } + }; + expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); + }); + }); + + describe('buildRequests', function () { + it('payload test', function () { + const requests = spec.buildRequests(bidRequests); + var payload1 = {}; + requests[0].data.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) { + payload1[decodeURIComponent(key)] = decodeURIComponent(value); + }); + expect(payload1.ref).to.not.be.undefined; + expect(payload1.url).to.not.be.undefined; + expect(payload1.hb).to.equal('1'); + expect(payload1.hb_source).to.equal('prebid'); + expect(payload1.zid).to.equal(PLACEMENT_ID); + expect(payload1.sizes).to.equal('300,250,300,600'); + expect(payload1.bid_id).to.equal('30b31c1838de1e'); + + var payload2 = {}; + requests[1].data.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) { + payload2[decodeURIComponent(key)] = decodeURIComponent(value); + }); + expect(payload2.ref).to.not.be.undefined; + expect(payload2.url).to.not.be.undefined; + expect(payload2.hb).to.equal('1'); + expect(payload2.hb_source).to.equal('prebid'); + expect(payload2.zid).to.equal(PLACEMENT_ID); + expect(payload2.sizes).to.equal('700,250,300,600'); + expect(payload2.bid_id).to.equal('30b31c1838de1d'); + + var payload3 = {}; + requests[2].data.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) { + payload3[decodeURIComponent(key)] = decodeURIComponent(value); + }); + expect(payload3.ref).to.not.be.undefined; + expect(payload3.url).to.not.be.undefined; + expect(payload3.hb).to.equal('1'); + expect(payload3.hb_source).to.equal('prebid'); + expect(payload3.network_key).to.equal(NETWORK_KEY); + expect(payload3.widget_id).to.equal(WIDGET_ID); + expect(payload3.cat).to.equal(CATEGORIES); + expect(payload3.sizes).to.equal('700,250,300,600'); + expect(payload3.bid_id).to.equal('30b31c1838de1f'); + }); + + it('sends bid request to ENDPOINT via GET', function () { + const requests = spec.buildRequests(bidRequests); + expect(requests[0].url).to.equal(ENDPOINT_URL); + expect(requests[0].method).to.equal('GET'); + }); + }); + + describe('interpretResponse', function () { + const response = { + body: { + 'status': 'OK', + 'crid': '5ISP4995', + 'ecpm': 10, + 'ad': { + 'html': '
    ', + 'height': 250, + 'width': 300 + } + } + }; + + const response2 = { + body: { + 'status': 'OK', + 'ecpm': 10, + 'html': '', + 'ads': [{ + 'crid': '5ISP4995', + 'ad': { + 'html': '
    ' + } + }, + { + 'crid': '5ISP4996', + 'ad': { + 'html': '
    ' + } + }] + } + }; + + it('should get correct bid response', function () { + const body = response.body; + const expectedResponse = [{ + requestId: bidRequests[0].bidId, + cpm: body.ecpm, + width: body.ad.width, + height: body.ad.height, + ad: body.ad.html, + ttl: 3600, + creativeId: body.crid, + netRevenue: false, + currency: 'USD', + mediaType: 'banner' + }]; + + let result = spec.interpretResponse(response, { 'bidderRequest': bidRequests[0] }); + expect(result).to.deep.equal(expectedResponse); + }); + + it('widget use case', function () { + const body = response2.body; + const expectedResponse = [ + { + requestId: bidRequests[1].bidId, + cpm: body.ecpm, + width: 300, + height: 600, + ad: body.html, + ttl: 3600, + creativeId: body.ads[0].crid, + netRevenue: false, + currency: 'USD', + mediaType: 'banner' + } + ]; + + let result = spec.interpretResponse(response2, { 'bidderRequest': bidRequests[1] }); + expect(result).to.deep.equal(expectedResponse); + }); + + it('handles nobid responses', function () { + let response = []; + + let result = spec.interpretResponse(response, { 'bidderRequest': bidRequests[0] }); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 9c25e0439b3..c823e5aa370 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1,12 +1,13 @@ import { expect } from 'chai'; -import { PrebidServer as Adapter, resetSyncedStatus } from 'modules/prebidServerBidAdapter'; -import adapterManager from 'src/adaptermanager'; +import { PrebidServer as Adapter, resetSyncedStatus } from 'modules/prebidServerBidAdapter/index.js'; +import adapterManager from 'src/adapterManager'; import * as utils from 'src/utils'; -import cookie from 'src/cookie'; import { userSync } from 'src/userSync'; import { ajax } from 'src/ajax'; import { config } from 'src/config'; import { requestBidsHook } from 'modules/consentManagement'; +import events from 'src/events'; +import CONSTANTS from 'src/constants'; let CONFIG = { accountId: '1', @@ -32,6 +33,19 @@ const REQUEST = { 'mediaTypes': { 'banner': { 'sizes': [[ 300, 250 ], [ 300, 300 ]] + }, + 'native': { + 'title': { + 'required': true, + 'len': 800 + }, + 'image': { + 'required': true, + 'sizes': [989, 742], + }, + 'sponsoredBy': { + 'required': true + } } }, 'transactionId': '4ef956ad-fd83-406d-bd35-e4bb786ab86c', @@ -79,34 +93,70 @@ const VIDEO_REQUEST = { ] }; -const BID_REQUESTS = [ - { - 'bidderCode': 'appnexus', - 'auctionId': '173afb6d132ba3', - 'bidderRequestId': '3d1063078dfcc8', - 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': '10433394', - 'member': 123 +const OUTSTREAM_VIDEO_REQUEST = { + 'account_id': '1', + 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', + 'max_bids': 1, + 'timeout_millis': 1000, + 'secure': 0, + 'url': '', + 'prebid_version': '1.4.0-pre', + 'ad_units': [ + { + 'code': 'div-gpt-ad-1460505748561-0', + 'sizes': [640, 480], + 'mediaTypes': { + 'video': { + playerSize: [[ 640, 480 ]], + context: 'outstream', + mimes: ['video/mp4'] }, - 'bid_id': '123', - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '4ef956ad-fd83-406d-bd35-e4bb786ab86c', - 'sizes': [300, 250], - 'bidId': '259fb43aaa06c1', - 'bidderRequestId': '3d1063078dfcc8', - 'auctionId': '173afb6d132ba3' + banner: { sizes: [[300, 250]] } + }, + 'transactionId': '4ef956ad-fd83-406d-bd35-e4bb786ab86c', + 'bids': [ + { + 'bid_id': '123', + 'bidder': 'appnexus', + 'params': { 'placementId': '12349520' } + } + ] + }, + { + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: 13232385, + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + } + } + } + ], + renderer: { + url: 'http://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + render: function (bid) { + ANOutstreamVideo.renderAd({ + targetId: bid.adUnitCode, + adResponse: bid.adResponse, + }); + } } - ], - 'auctionStart': 1510852447530, - 'timeout': 5000, - 'src': 's2s', - 'doneCbCallCount': 0 - } -]; + } + ] +}; + +let BID_REQUESTS; const RESPONSE = { 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', @@ -271,7 +321,7 @@ const RESPONSE_OPENRTB = { 'bid': [ { 'id': '8750901685062148', - 'impid': '123', + 'impid': 'div-gpt-ad-1460505748561-0', 'price': 0.5, 'adm': '', 'adid': '29681110', @@ -310,7 +360,7 @@ const RESPONSE_OPENRTB_VIDEO = { bid: [ { id: '1987250005171537465', - impid: '/19968336/header-bid-tag-0', + impid: 'div-gpt-ad-1460505748561-0', price: 10, adm: 'adnxs', adid: '81877115', @@ -345,6 +395,93 @@ const RESPONSE_OPENRTB_VIDEO = { }, }; +const RESPONSE_OPENRTB_NATIVE = { + 'id': 'c7dcf14f', + 'seatbid': [ + { + 'bid': [ + { + 'id': '6451317310275562039', + 'impid': 'div-gpt-ad-1460505748561-0', + 'price': 10, + 'adm': { + 'ver': '1.2', + 'assets': [ + { + 'id': 1, + 'img': { + 'url': 'https://vcdn.adnxs.com/p/creative-image/f8/7f/0f/13/f87f0f13-230c-4f05-8087-db9216e393de.jpg', + 'w': 989, + 'h': 742, + 'ext': { + 'appnexus': { + 'prevent_crop': 0 + } + } + } + }, + { + 'id': 0, + 'title': { + 'text': 'This is a Prebid Native Creative' + } + }, + { + 'id': 2, + 'data': { + 'value': 'Prebid.org' + } + } + ], + 'link': { + 'url': 'https://lax1-ib.adnxs.com/click?AAAAAAAAJEAAAAAAAAAkQAAAAAAAACRAAAAAAAAAJEAAAAAAAAAkQGdce2vBWudAJZpFu1er1zA7ZzddAAAAAOLoyQBtJAAAbSQAAAIAAAC8pM8FnPgWAAAAAABVU0QAVVNEAAEAAQBNXQAAAAABAgMCAAAAALsAuhVqdgAAAAA./cpcpm=AAAAAAAAAAA=/bcr=AAAAAAAA8D8=/pp=${AUCTION_PRICE}/cnd=%213Q5HCQj8-LwKELzJvi4YnPFbIAQoADEAAAAAAAAkQDoJTEFYMTo0MDc3QKcPSQAAAAAAAPA_UQAAAAAAAAAAWQAAAAAAAAAAYQAAAAAAAAAAaQAAAAAAAAAAcQAAAAAAAAAA/cca=OTMyNSNMQVgxOjQwNzc=/bn=84305/test=1/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html' + }, + 'eventtrackers': [ + { + 'event': 1, + 'method': 1, + 'url': 'https://lax1-ib.adnxs.com/it?an_audit=0&test=1&referrer=http%3A%2F%2Flocalhost%3A9999%2FintegrationExamples%2Fgpt%2Fdemo_native.html&e=wqT_3QKCCKACBAAAAwDWAAUBCLvO3ekFEOe47duW2NbzQBiltJba--rq6zAqNgkAAAECCCRAEQEHEAAAJEAZEQkAIREJACkRCQAxEQmoMOLRpwY47UhA7UhIAlC8yb4uWJzxW2AAaM26dXjRkgWAAQGKAQNVU0SSAQEG8FKYAQGgAQGoAQGwAQC4AQLAAQPIAQLQAQnYAQDgAQHwAQCKAjt1ZignYScsIDI1Mjk4ODUsIDE1NjM5MTE5OTUpO3VmKCdyJywgOTc0OTQyMDQsIC4eAPQ0AZICnQIhb2pkaWlnajgtTHdLRUx6SnZpNFlBQ0NjOFZzd0FEZ0FRQVJJN1VoUTR0R25CbGdBWVAwQmFBQndBSGdBZ0FFQWlBRUFrQUVCbUFFQm9BRUJxQUVEc0FFQXVRSHpyV3FrQUFBa1FNRUI4NjFxcEFBQUpFREpBVVZpYmxDaFpRQkEyUUVBQUFBQUFBRHdQLUFCQVBVQkFBQUFBUGdCQUpnQ0FLQUNBTFVDQUFBQUFMMENBQUFBQU1BQ0FNZ0NBT0FDQU9nQ0FQZ0NBSUFEQVpBREFKZ0RBYWdEX1BpOENyb0RDVXhCV0RFNk5EQTNOLUFEcHctUUJBQ1lCQUhCQkFBQUFBQUFBQUFBeVFRQUFBQUFBQUFBQU5nRUFBLi6aAoUBITNRNUhDUWo4LUx3S0VMeiUhJG5QRmJJQVFvQUQRvVhBa1FEb0pURUZZTVRvME1EYzNRS2NQUxFUDFBBX1URDAxBQUFXHQwAWR0MAGEdDABjHQz0FwHYAgDgAq2YSOoCPmh0dHA6Ly9sb2NhbGhvc3Q6OTk5OS9pbnRlZ3JhdGlvbkV4YW1wbGVzL2dwdC9kZW1vX25hdGl2ZS5odG1sgAMAiAMBkAMAmAMUoAMBqgMAwAPgqAHIAwDYAwDgAwDoAwD4AwOABACSBAkvb3BlbnJ0YjKYBACiBA0xNzMuMjQ0LjM2LjQwqATtoySyBAwIABAAGAAgADAAOAC4BADABADIBADSBA45MzI1I0xBWDE6NDA3N9oEAggB4AQA8AS8yb4uiAUBmAUAoAX___________8BqgUkZTU5YzNlYjYtNmRkNi00MmQ5LWExMWEtM2FhMTFjOTc5MGUwwAUAyQUAAAAAAADwP9IFCQkAaVh0ANgFAeAFAfAFmfQh-gUECAAQAJAGAZgGALgGAMEGCSQk8D_IBgDaBhYKEAkQGQEBwTTgBgzyBgIIAIAHAYgHAA..&s=11ababa390e9f7983de260493fc5b91ec5b1b3d4&pp=${AUCTION_PRICE}' + } + ] + }, + 'adid': '97494204', + 'adomain': [ + 'http://prebid.org' + ], + 'iurl': 'https://lax1-ib.adnxs.com/cr?id=97494204', + 'cid': '9325', + 'crid': '97494204', + 'cat': [ + 'IAB3-1' + ], + 'ext': { + 'prebid': { + 'targeting': { + 'hb_bidder': 'appnexus', + 'hb_pb': '10.00' + }, + 'type': 'native', + 'video': { + 'duration': 0, + 'primary_category': '' + } + }, + 'bidder': { + 'appnexus': { + 'brand_id': 555545, + 'auction_id': 4676806524825984103, + 'bidder_id': 2, + 'bid_ad_type': 3 + } + } + } + } + ], + 'seat': 'appnexus' + } + ] +}; + const RESPONSE_UNSUPPORTED_BIDDER = { 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', 'status': 'OK', @@ -354,23 +491,57 @@ const RESPONSE_UNSUPPORTED_BIDDER = { }] }; -describe('S2S Adapter', () => { +describe('S2S Adapter', function () { let adapter, addBidResponse = sinon.spy(), done = sinon.spy(); - beforeEach(() => adapter = new Adapter()); + beforeEach(function () { + adapter = new Adapter(); + BID_REQUESTS = [ + { + 'bidderCode': 'appnexus', + 'auctionId': '173afb6d132ba3', + 'bidderRequestId': '3d1063078dfcc8', + 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', + 'bids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': '10433394', + 'member': 123, + 'keywords': { + 'foo': ['bar', 'baz'], + 'fizz': ['buzz'] + } + }, + 'bid_id': '123', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '4ef956ad-fd83-406d-bd35-e4bb786ab86c', + 'sizes': [300, 250], + 'bidId': '123', + 'bidderRequestId': '3d1063078dfcc8', + 'auctionId': '173afb6d132ba3' + } + ], + 'auctionStart': 1510852447530, + 'timeout': 5000, + 'src': 's2s', + 'doneCbCallCount': 0 + } + ]; + }); - afterEach(() => { + afterEach(function () { addBidResponse.resetHistory(); done.resetHistory(); }); - describe('request function', () => { + describe('request function', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = request => requests.push(request); @@ -378,13 +549,27 @@ describe('S2S Adapter', () => { resetSyncedStatus(); }); - afterEach(() => xhr.restore()); + afterEach(function () { + xhr.restore(); + }); + + it('should not add outstrean without renderer', function() { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + + config.setConfig({s2sConfig: ortb2Config}); + adapter.callBids(OUTSTREAM_VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + + const requestBid = JSON.parse(requests[0].requestBody); + expect(requestBid.imp[0].banner).to.exist; + expect(requestBid.imp[0].video).to.not.exist; + }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); - it('exists converts types', () => { + it('exists converts types', function () { config.setConfig({s2sConfig: CONFIG}); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); @@ -393,100 +578,106 @@ describe('S2S Adapter', () => { expect(requestBid.ad_units[0].bids[0].params.member).to.exist.and.to.be.a('string'); }); - it('adds gdpr consent information to ortb2 request depending on presence of module', () => { - let ortb2Config = utils.deepClone(CONFIG); - ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + describe('gdpr tests', function () { + afterEach(function () { + config.resetConfig(); + $$PREBID_GLOBAL$$.requestBids.removeAll(); + }); - let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: ortb2Config }; - config.setConfig(consentConfig); + it('adds gdpr consent information to ortb2 request depending on presence of module', function () { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' - let gdprBidRequest = utils.deepClone(BID_REQUESTS); - gdprBidRequest[0].gdprConsent = { - consentString: 'abc123', - gdprApplies: true - }; + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: ortb2Config }; + config.setConfig(consentConfig); - adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); - let requestBid = JSON.parse(requests[0].requestBody); + let gdprBidRequest = utils.deepClone(BID_REQUESTS); + gdprBidRequest[0].gdprConsent = { + consentString: 'abc123', + gdprApplies: true + }; - expect(requestBid.regs.ext.gdpr).is.equal(1); - expect(requestBid.user.ext.consent).is.equal('abc123'); + adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(requests[0].requestBody); - config.resetConfig(); - config.setConfig({s2sConfig: CONFIG}); + expect(requestBid.regs.ext.gdpr).is.equal(1); + expect(requestBid.user.ext.consent).is.equal('abc123'); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - requestBid = JSON.parse(requests[1].requestBody); + config.resetConfig(); + config.setConfig({s2sConfig: CONFIG}); - expect(requestBid.regs).to.not.exist; - expect(requestBid.user).to.not.exist; + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + requestBid = JSON.parse(requests[1].requestBody); - config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook); - }); + expect(requestBid.regs).to.not.exist; + expect(requestBid.user).to.not.exist; + }); - it('check gdpr info gets added into cookie_sync request: have consent data', () => { - let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + it('check gdpr info gets added into cookie_sync request: have consent data', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; - let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; - config.setConfig(consentConfig); + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); - let gdprBidRequest = utils.deepClone(BID_REQUESTS); + let gdprBidRequest = utils.deepClone(BID_REQUESTS); - gdprBidRequest[0].gdprConsent = { - consentString: 'abc123def', - gdprApplies: true - }; + gdprBidRequest[0].gdprConsent = { + consentString: 'abc123def', + gdprApplies: true + }; - adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); - let requestBid = JSON.parse(requests[0].requestBody); + adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.gdpr).is.equal(1); - expect(requestBid.gdpr_consent).is.equal('abc123def'); - }); + expect(requestBid.gdpr).is.equal(1); + expect(requestBid.gdpr_consent).is.equal('abc123def'); + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + }); - it('check gdpr info gets added into cookie_sync request: have consent data but gdprApplies is false', () => { - let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + it('check gdpr info gets added into cookie_sync request: have consent data but gdprApplies is false', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; - let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; - config.setConfig(consentConfig); + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); - let gdprBidRequest = utils.deepClone(BID_REQUESTS); - gdprBidRequest[0].gdprConsent = { - consentString: 'xyz789abcc', - gdprApplies: false - }; + let gdprBidRequest = utils.deepClone(BID_REQUESTS); + gdprBidRequest[0].gdprConsent = { + consentString: 'xyz789abcc', + gdprApplies: false + }; - adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); - let requestBid = JSON.parse(requests[0].requestBody); + adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.gdpr).is.equal(0); - expect(requestBid.gdpr_consent).is.undefined; - }); + expect(requestBid.gdpr).is.equal(0); + expect(requestBid.gdpr_consent).is.undefined; + }); - it('checks gdpr info gets added to cookie_sync request: consent data unknown', () => { - let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + it('checks gdpr info gets added to cookie_sync request: consent data unknown', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; - let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; - config.setConfig(consentConfig); + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); - let gdprBidRequest = utils.deepClone(BID_REQUESTS); - gdprBidRequest[0].gdprConsent = { - consentString: undefined, - gdprApplies: undefined - }; + let gdprBidRequest = utils.deepClone(BID_REQUESTS); + gdprBidRequest[0].gdprConsent = { + consentString: undefined, + gdprApplies: undefined + }; - adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); - let requestBid = JSON.parse(requests[0].requestBody); + adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.gdpr).is.undefined; - expect(requestBid.gdpr_consent).is.undefined; + expect(requestBid.gdpr).is.undefined; + expect(requestBid.gdpr_consent).is.undefined; + }); }); - it('sets invalid cacheMarkup value to 0', () => { + it('sets invalid cacheMarkup value to 0', function () { const s2sConfig = Object.assign({}, CONFIG, { cacheMarkup: 999 }); @@ -496,42 +687,41 @@ describe('S2S Adapter', () => { expect(requestBid).to.have.property('cache_markup', 0); }); - it('adds digitrust id is present and user is not optout', () => { + it('adds digitrust id is present and user is not optout', function () { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + + let consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + let digiTrustObj = { - success: true, - identity: { - privacy: { - optout: false - }, - id: 'testId', - keyv: 'testKeyV' - } + privacy: { + optout: false + }, + id: 'testId', + keyv: 'testKeyV' }; - window.DigiTrust = { - getUser: () => digiTrustObj - }; + let digiTrustBidRequest = utils.deepClone(BID_REQUESTS); + digiTrustBidRequest[0].bids[0].userId = { digitrustid: { data: digiTrustObj } }; - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(REQUEST, digiTrustBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.digiTrust).to.deep.equal({ - id: digiTrustObj.identity.id, - keyv: digiTrustObj.identity.keyv, - pref: 0 + expect(requestBid.user.ext.digitrust).to.deep.equal({ + id: digiTrustObj.id, + keyv: digiTrustObj.keyv }); - digiTrustObj.identity.privacy.optout = true; + digiTrustObj.privacy.optout = true; - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(REQUEST, digiTrustBidRequest, addBidResponse, done, ajax); requestBid = JSON.parse(requests[1].requestBody); - expect(requestBid.digiTrust).to.not.exist; - - delete window.DigiTrust; + expect(requestBid.user && request.user.ext && requestBid.user.ext.digitrust).to.not.exist; }); - it('adds device and app objects to request', () => { + it('adds device and app objects to request', function () { const _config = { s2sConfig: CONFIG, device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, app: { bundle: 'com.test.app' }, @@ -542,6 +732,8 @@ describe('S2S Adapter', () => { const requestBid = JSON.parse(requests[0].requestBody); expect(requestBid.device).to.deep.equal({ ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC', + w: window.innerWidth, + h: window.innerHeight }); expect(requestBid.app).to.deep.equal({ bundle: 'com.test.app', @@ -549,7 +741,7 @@ describe('S2S Adapter', () => { }); }); - it('adds device and app objects to request for ORTB', () => { + it('adds device and app objects to request for OpenRTB', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); @@ -558,13 +750,15 @@ describe('S2S Adapter', () => { s2sConfig: s2sConfig, device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, app: { bundle: 'com.test.app' }, - } + }; config.setConfig(_config); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); expect(requestBid.device).to.deep.equal({ ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC', + w: window.innerWidth, + h: window.innerHeight }); expect(requestBid.app).to.deep.equal({ bundle: 'com.test.app', @@ -572,7 +766,78 @@ describe('S2S Adapter', () => { }); }); - it('adds site if app is not present', () => { + it('adds device.w and device.h even if the config lacks a device object', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + }); + + const _config = { + s2sConfig: s2sConfig, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + expect(requestBid.device).to.deep.equal({ + w: window.innerWidth, + h: window.innerHeight + }); + expect(requestBid.app).to.deep.equal({ + bundle: 'com.test.app', + publisher: {'id': '1'} + }); + }); + + it('adds native request for OpenRTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + }); + + const _config = { + s2sConfig: s2sConfig + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.imp[0].native).to.deep.equal({ + request: JSON.stringify({ + 'context': 1, + 'plcmttype': 1, + 'eventtrackers': [{ + event: 1, + methods: [1] + }], + 'assets': [ + { + 'required': 1, + 'title': { + 'len': 800 + } + }, + { + 'required': 1, + 'img': { + 'type': 3, + 'w': 989, + 'h': 742 + } + }, + { + 'required': 1, + 'data': { + 'type': 1 + } + } + ] + }), + ver: '1.2' + }); + }); + + it('adds site if app is not present', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); @@ -589,11 +854,11 @@ describe('S2S Adapter', () => { expect(requestBid.site.page).to.exist.and.to.be.a('string'); }); - it('adds appnexus aliases to request', () => { + it('adds appnexus aliases to request', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({s2sConfig: s2sConfig}); const aliasBidder = { bidder: 'brealtime', @@ -611,16 +876,20 @@ describe('S2S Adapter', () => { prebid: { aliases: { brealtime: 'appnexus' + }, + targeting: { + includebidderkeys: false, + includewinners: true } } }); }); - it('adds dynamic aliases to request', () => { + it('adds dynamic aliases to request', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({s2sConfig: s2sConfig}); const alias = 'foobar'; const aliasBidder = { @@ -641,19 +910,27 @@ describe('S2S Adapter', () => { prebid: { aliases: { [alias]: 'appnexus' + }, + targeting: { + includebidderkeys: false, + includewinners: true } } }); }); - it('converts appnexus params to expected format for PBS', () => { + it('converts appnexus params to expected format for PBS', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({s2sConfig: s2sConfig}); const myRequest = utils.deepClone(REQUEST); myRequest.ad_units[0].bids[0].params.usePaymentRule = true; + myRequest.ad_units[0].bids[0].params.keywords = { + foo: ['bar', 'baz'], + fizz: ['buzz'] + }; adapter.callBids(myRequest, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); @@ -662,37 +939,355 @@ describe('S2S Adapter', () => { expect(requestBid.imp[0].ext.appnexus.placement_id).to.exist.and.to.equal(10433394); expect(requestBid.imp[0].ext.appnexus.use_pmt_rule).to.exist.and.to.be.true; expect(requestBid.imp[0].ext.appnexus.member).to.exist; + expect(requestBid.imp[0].ext.appnexus.keywords).to.exist.and.to.deep.equal([{ + key: 'foo', + value: ['bar', 'baz'] + }, { + key: 'fizz', + value: ['buzz'] + }]); + + config.resetConfig(); + const oldS2sConfig = Object.assign({}, CONFIG); + config.setConfig({s2sConfig: oldS2sConfig}); + + const myRequest2 = utils.deepClone(REQUEST); + myRequest2.ad_units[0].bids[0].params.keywords = { + foo: ['bar', 'baz'], + fizz: ['buzz'] + }; + + adapter.callBids(myRequest2, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid2 = JSON.parse(requests[1].requestBody); + + expect(requestBid2.ad_units[0].bids[0].params.keywords).to.exist.and.to.deep.equal([{ + key: 'foo', + value: ['bar', 'baz'] + }, { + key: 'fizz', + value: ['buzz'] + }]); + }); + + it('adds limit to the cookie_sync request if userSyncLimit is greater than 0', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.userSyncLimit = 1; + + config.setConfig({ s2sConfig: cookieSyncConfig }); + + let bidRequest = utils.deepClone(BID_REQUESTS); + adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + expect(requestBid.limit).is.equal(1); }); + + it('does not add limit to cooke_sync request if userSyncLimit is missing or 0', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + config.setConfig({ s2sConfig: cookieSyncConfig }); + + let bidRequest = utils.deepClone(BID_REQUESTS); + adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + expect(requestBid.limit).is.undefined; + + cookieSyncConfig.userSyncLimit = 0; + config.resetConfig(); + config.setConfig({ s2sConfig: cookieSyncConfig }); + + bidRequest = utils.deepClone(BID_REQUESTS); + adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); + requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + expect(requestBid.limit).is.undefined; + }); + + it('adds s2sConfig adapterOptions to request for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + adapterOptions: { + appnexus: { + key: 'value' + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + expect(requestBid.imp[0].ext.appnexus).to.haveOwnProperty('key'); + expect(requestBid.imp[0].ext.appnexus.key).to.be.equal('value') + }); + + it('when userId is defined on bids, it\'s properties should be copied to user.ext.tpid properties', function () { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + + let consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + + let userIdBidRequest = utils.deepClone(BID_REQUESTS); + userIdBidRequest[0].bids[0].userId = { + tdid: 'abc123', + pubcid: '1234' + }; + + adapter.callBids(REQUEST, userIdBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(requests[0].requestBody); + expect(typeof requestBid.user.ext.eids).is.equal('object'); + expect(Array.isArray(requestBid.user.ext.eids)).to.be.true; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')[0].uids[0].id).is.equal('abc123'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcommon')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcommon')[0].uids[0].id).is.equal('1234'); + }); + + it('when config \'currency.adServerCurrency\' value is an array: ORTB has property \'cur\' value set to a single item array', function () { + let s2sConfig = utils.deepClone(CONFIG); + s2sConfig.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + config.setConfig({ + currency: {adServerCurrency: ['USD', 'GB', 'UK', 'AU']}, + s2sConfig: s2sConfig + }); + + const bidRequests = utils.deepClone(BID_REQUESTS); + adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); + + const parsedRequestBody = JSON.parse(requests[0].requestBody); + expect(parsedRequestBody.cur).to.deep.equal(['USD']); + }); + + it('when config \'currency.adServerCurrency\' value is a string: ORTB has property \'cur\' value set to a single item array', function () { + let s2sConfig = utils.deepClone(CONFIG); + s2sConfig.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + config.setConfig({ + currency: {adServerCurrency: 'NZ'}, + s2sConfig: s2sConfig + }); + + const bidRequests = utils.deepClone(BID_REQUESTS); + adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); + + const parsedRequestBody = JSON.parse(requests[1].requestBody); + expect(parsedRequestBody.cur).to.deep.equal(['NZ']); + }); + + it('when config \'currency.adServerCurrency\' is unset: ORTB should not define a \'cur\' property', function () { + let s2sConfig = utils.deepClone(CONFIG); + s2sConfig.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + config.setConfig({s2sConfig: s2sConfig}); + + const bidRequests = utils.deepClone(BID_REQUESTS); + adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); + + const parsedRequestBody = JSON.parse(requests[0].requestBody); + expect(typeof parsedRequestBody.cur).to.equal('undefined'); + }); + + it('always add ext.prebid.targeting.includebidderkeys: false for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + adapterOptions: { + appnexus: { + key: 'value' + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includebidderkeys'); + expect(requestBid.ext.prebid.targeting.includebidderkeys).to.equal(false); + }); + + it('always add ext.prebid.targeting.includewinners: true for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + adapterOptions: { + appnexus: { + key: 'value' + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includewinners'); + expect(requestBid.ext.prebid.targeting.includewinners).to.equal(true); + }); + + it('adds s2sConfig video.ext.prebid to request for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + extPrebid: { + foo: 'bar' + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid).to.haveOwnProperty('ext'); + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.equal({ + foo: 'bar', + targeting: { + includewinners: true, + includebidderkeys: false + } + }); + }); + + it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + extPrebid: { + targeting: { + includewinners: false, + includebidderkeys: true + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid).to.haveOwnProperty('ext'); + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.equal({ + targeting: { + includewinners: false, + includebidderkeys: true + } + }); + }); + + it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + extPrebid: { + cache: { + vastxml: 'vastxml-set-though-extPrebid.cache.vastXml' + }, + targeting: { + includewinners: false, + includebidderkeys: false + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid).to.haveOwnProperty('ext'); + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.equal({ + cache: { + vastxml: 'vastxml-set-though-extPrebid.cache.vastXml' + }, + targeting: { + includewinners: false, + includebidderkeys: false + } + }); + }); + + it('passes schain object in request', function() { + const bidRequests = utils.deepClone(BID_REQUESTS); + const schainObject = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + } + ] + }; + bidRequests[0].bids[0].schain = schainObject; + adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + expect(parsedRequestBody.source.ext.schain).to.deep.equal(schainObject); + }) }); - describe('response handler', () => { + describe('response handler', function () { let server; let logWarnSpy; - beforeEach(() => { + beforeEach(function () { server = sinon.fakeServer.create(); sinon.stub(utils, 'triggerPixel'); sinon.stub(utils, 'insertUserSyncIframe'); sinon.stub(utils, 'logError'); - sinon.stub(cookie, 'cookieSet'); - sinon.stub(utils, 'getBidRequest').returns({ - bidId: '123' - }); + sinon.stub(events, 'emit'); logWarnSpy = sinon.spy(utils, 'logWarn'); }); - afterEach(() => { + afterEach(function () { server.restore(); - utils.getBidRequest.restore(); utils.triggerPixel.restore(); utils.insertUserSyncIframe.restore(); utils.logError.restore(); - cookie.cookieSet.restore(); + events.emit.restore(); logWarnSpy.restore(); }); // TODO: test dependent on pbjs_api_spec. Needs to be isolated - it('registers bids', () => { + it('registers bids and calls BIDDER_DONE', function () { server.respondWith(JSON.stringify(RESPONSE)); config.setConfig({s2sConfig: CONFIG}); @@ -700,18 +1295,22 @@ describe('S2S Adapter', () => { server.respond(); sinon.assert.calledOnce(addBidResponse); + sinon.assert.calledOnce(events.emit); + const event = events.emit.firstCall.args; + expect(event[0]).to.equal(CONSTANTS.EVENTS.BIDDER_DONE); + expect(event[1].bids[0]).to.have.property('serverResponseTimeMs', 52); + const response = addBidResponse.firstCall.args[1]; expect(response).to.have.property('statusMessage', 'Bid available'); expect(response).to.have.property('cpm', 0.5); - expect(response).to.have.property('adId', '123'); + expect(response).to.have.property('requestId', '123'); expect(response).to.not.have.property('videoCacheKey'); expect(response).to.have.property('cache_id', '7654321'); expect(response).to.have.property('cache_url', 'http://www.test.com/cache?uuid=7654321'); expect(response).to.not.have.property('vastUrl'); - expect(response).to.have.property('serverResponseTimeMs', 52); }); - it('registers video bids', () => { + it('registers video bids', function () { server.respondWith(JSON.stringify(VIDEO_RESPONSE)); config.setConfig({s2sConfig: CONFIG}); @@ -722,14 +1321,14 @@ describe('S2S Adapter', () => { const response = addBidResponse.firstCall.args[1]; expect(response).to.have.property('statusMessage', 'Bid available'); expect(response).to.have.property('cpm', 0.5); - expect(response).to.have.property('adId', '123'); + expect(response).to.have.property('requestId', '123'); expect(response).to.have.property('videoCacheKey', 'video_cache_id'); expect(response).to.have.property('cache_id', 'video_cache_id'); expect(response).to.have.property('cache_url', 'video_cache_url'); expect(response).to.have.property('vastUrl', 'video_cache_url'); }); - it('does not call addBidResponse and calls done when ad unit not set', () => { + it('does not call addBidResponse and calls done when ad unit not set', function () { server.respondWith(JSON.stringify(RESPONSE_NO_BID_NO_UNIT)); config.setConfig({s2sConfig: CONFIG}); @@ -740,7 +1339,7 @@ describe('S2S Adapter', () => { sinon.assert.calledOnce(done); }); - it('does not call addBidResponse and calls done when server requests cookie sync', () => { + it('does not call addBidResponse and calls done when server requests cookie sync', function () { server.respondWith(JSON.stringify(RESPONSE_NO_COOKIE)); config.setConfig({s2sConfig: CONFIG}); @@ -751,7 +1350,7 @@ describe('S2S Adapter', () => { sinon.assert.calledOnce(done); }); - it('does not call addBidResponse and calls done when ad unit is set', () => { + it('does not call addBidResponse and calls done when ad unit is set', function () { server.respondWith(JSON.stringify(RESPONSE_NO_BID_UNIT_SET)); config.setConfig({s2sConfig: CONFIG}); @@ -762,7 +1361,7 @@ describe('S2S Adapter', () => { sinon.assert.calledOnce(done); }); - it('registers successful bids and calls done when there are less bids than requests', () => { + it('registers successful bids and calls done when there are less bids than requests', function () { server.respondWith(JSON.stringify(RESPONSE)); config.setConfig({s2sConfig: CONFIG}); @@ -774,13 +1373,13 @@ describe('S2S Adapter', () => { expect(addBidResponse.firstCall.args[0]).to.equal('div-gpt-ad-1460505748561-0'); - expect(addBidResponse.firstCall.args[1]).to.have.property('adId', '123'); + expect(addBidResponse.firstCall.args[1]).to.have.property('requestId', '123'); expect(addBidResponse.firstCall.args[1]) .to.have.property('statusMessage', 'Bid available'); }); - it('should have dealId in bidObject', () => { + it('should have dealId in bidObject', function () { server.respondWith(JSON.stringify(RESPONSE)); config.setConfig({s2sConfig: CONFIG}); @@ -790,7 +1389,7 @@ describe('S2S Adapter', () => { expect(response).to.have.property('dealId', 'test-dealid'); }); - it('should pass through default adserverTargeting if present in bidObject', () => { + it('should pass through default adserverTargeting if present in bidObject', function () { server.respondWith(JSON.stringify(RESPONSE)); config.setConfig({s2sConfig: CONFIG}); @@ -800,7 +1399,7 @@ describe('S2S Adapter', () => { expect(response).to.have.property('adserverTargeting').that.deep.equals({'foo': 'bar'}); }); - it('registers client user syncs when client bid adapter is present', () => { + it('registers client user syncs when client bid adapter is present', function () { let rubiconAdapter = { registerSyncs: sinon.spy() }; @@ -817,7 +1416,7 @@ describe('S2S Adapter', () => { adapterManager.getBidAdapter.restore(); }); - it('registers client user syncs when using OpenRTB endpoint', () => { + it('registers client user syncs when using OpenRTB endpoint', function () { let rubiconAdapter = { registerSyncs: sinon.spy() }; @@ -837,7 +1436,7 @@ describe('S2S Adapter', () => { adapterManager.getBidAdapter.restore(); }); - it('registers bid responses when server requests cookie sync', () => { + it('registers bid responses when server requests cookie sync', function () { server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); config.setConfig({s2sConfig: CONFIG}); @@ -853,34 +1452,10 @@ describe('S2S Adapter', () => { expect(response).to.have.property('source', 's2s'); const bid_request_passed = addBidResponse.firstCall.args[1]; - expect(bid_request_passed).to.have.property('adId', '123'); - }); - - it('does not call cookieSet cookie sync when no_cookie response && not opted in', () => { - server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); - - let myConfig = Object.assign({}, CONFIG); - - config.setConfig({s2sConfig: myConfig}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - server.respond(); - sinon.assert.notCalled(cookie.cookieSet); + expect(bid_request_passed).to.have.property('requestId', '123'); }); - it('calls cookieSet cookie sync when no_cookie response && opted in', () => { - server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); - let myConfig = Object.assign({ - cookieSet: true, - cookieSetUrl: 'https://acdn.adnxs.com/cookieset/cs.js' - }, CONFIG); - - config.setConfig({s2sConfig: myConfig}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - server.respond(); - sinon.assert.calledOnce(cookie.cookieSet); - }); - - it('handles OpenRTB responses', () => { + it('handles OpenRTB responses and call BIDDER_DONE', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); @@ -890,16 +1465,20 @@ describe('S2S Adapter', () => { adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); + sinon.assert.calledOnce(events.emit); + const event = events.emit.firstCall.args; + expect(event[0]).to.equal(CONSTANTS.EVENTS.BIDDER_DONE); + expect(event[1].bids[0]).to.have.property('serverResponseTimeMs', 8); + sinon.assert.calledOnce(addBidResponse); const response = addBidResponse.firstCall.args[1]; expect(response).to.have.property('statusMessage', 'Bid available'); expect(response).to.have.property('bidderCode', 'appnexus'); - expect(response).to.have.property('adId', '123'); + expect(response).to.have.property('requestId', '123'); expect(response).to.have.property('cpm', 0.5); - expect(response).to.have.property('serverResponseTimeMs', 8); }); - it('handles OpenRTB video responses', () => { + it('handles OpenRTB video responses', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' }); @@ -915,12 +1494,117 @@ describe('S2S Adapter', () => { expect(response).to.have.property('vastXml', RESPONSE_OPENRTB_VIDEO.seatbid[0].bid[0].adm); expect(response).to.have.property('mediaType', 'video'); expect(response).to.have.property('bidderCode', 'appnexus'); - expect(response).to.have.property('adId', '123'); + expect(response).to.have.property('requestId', '123'); expect(response).to.have.property('cpm', 10); - expect(response).to.have.property('serverResponseTimeMs', 81); }); - it('should log warning for unsupported bidder', () => { + it('handles response cache from ext.prebid.cache.vastXml', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); + cacheResponse.seatbid.forEach(item => { + item.bid[0].ext.prebid.cache = { + vastXml: { + cacheId: 'abcd1234', + url: 'https://prebid-cache.net/cache?uuid=abcd1234' + } + } + }); + server.respondWith(JSON.stringify(cacheResponse)); + adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + + sinon.assert.calledOnce(addBidResponse); + const response = addBidResponse.firstCall.args[1]; + + expect(response).to.have.property('statusMessage', 'Bid available'); + expect(response).to.have.property('videoCacheKey', 'abcd1234'); + expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=abcd1234'); + }); + + it('add adserverTargeting object to bids when ext.prebid.targeting is defined', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); + const targetingTestData = { + hb_cache_path: '/cache', + hb_cache_host: 'prebid-cache.testurl.com' + }; + + cacheResponse.seatbid.forEach(item => { + item.bid[0].ext.prebid.targeting = targetingTestData + }); + server.respondWith(JSON.stringify(cacheResponse)); + adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + + sinon.assert.calledOnce(addBidResponse); + const response = addBidResponse.firstCall.args[1]; + + expect(response).to.have.property('adserverTargeting'); + expect(response.adserverTargeting).to.deep.equal({ + 'hb_cache_path': '/cache', + 'hb_cache_host': 'prebid-cache.testurl.com' + }); + }); + + it('handles response cache from ext.prebid.targeting', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); + cacheResponse.seatbid.forEach(item => { + item.bid[0].ext.prebid.targeting = { + hb_uuid: 'a5ad3993', + hb_cache_host: 'prebid-cache.net', + hb_cache_path: '/cache' + } + }); + server.respondWith(JSON.stringify(cacheResponse)); + adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + + sinon.assert.calledOnce(addBidResponse); + const response = addBidResponse.firstCall.args[1]; + + expect(response).to.have.property('statusMessage', 'Bid available'); + expect(response).to.have.property('videoCacheKey', 'a5ad3993'); + expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=a5ad3993'); + }); + + it('handles OpenRTB native responses', function () { + sinon.stub(utils, 'getBidRequest').returns({ + adUnitCode: 'div-gpt-ad-1460505748561-0', + bidder: 'appnexus', + bidId: '123' + }); + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + + server.respondWith(JSON.stringify(RESPONSE_OPENRTB_NATIVE)); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + + sinon.assert.calledOnce(addBidResponse); + const response = addBidResponse.firstCall.args[1]; + expect(response).to.have.property('statusMessage', 'Bid available'); + expect(response).to.have.property('adm').deep.equal(RESPONSE_OPENRTB_NATIVE.seatbid[0].bid[0].adm); + expect(response).to.have.property('mediaType', 'native'); + expect(response).to.have.property('bidderCode', 'appnexus'); + expect(response).to.have.property('requestId', '123'); + expect(response).to.have.property('cpm', 10); + + utils.getBidRequest.restore(); + }); + + it('should log warning for unsupported bidder', function () { server.respondWith(JSON.stringify(RESPONSE_UNSUPPORTED_BIDDER)); const s2sConfig = Object.assign({}, CONFIG, { @@ -940,18 +1624,18 @@ describe('S2S Adapter', () => { }); }); - describe('s2sConfig', () => { + describe('s2sConfig', function () { let logErrorSpy; - beforeEach(() => { + beforeEach(function () { logErrorSpy = sinon.spy(utils, 'logError'); }); - afterEach(() => { + afterEach(function () { utils.logError.restore(); }); - it('should log an error when accountId is missing', () => { + it('should log an error when accountId is missing', function () { const options = { enabled: true, bidders: ['appnexus'], @@ -964,7 +1648,7 @@ describe('S2S Adapter', () => { sinon.assert.calledOnce(logErrorSpy); }); - it('should log an error when bidders is missing', () => { + it('should log an error when bidders is missing', function () { const options = { accountId: '1', enabled: true, @@ -977,7 +1661,7 @@ describe('S2S Adapter', () => { sinon.assert.calledOnce(logErrorSpy); }); - it('should log an error when endpoint is missing', () => { + it('should log an error when endpoint is missing', function () { const options = { accountId: '1', bidders: ['appnexus'], @@ -990,7 +1674,7 @@ describe('S2S Adapter', () => { sinon.assert.calledOnce(logErrorSpy); }); - it('should log an error when using an unknown vendor', () => { + it('should log an error when using an unknown vendor', function () { const options = { accountId: '1', bidders: ['appnexus'], @@ -1001,7 +1685,7 @@ describe('S2S Adapter', () => { sinon.assert.calledOnce(logErrorSpy); }); - it('should configure the s2sConfig object with appnexus vendor defaults unless specified by user', () => { + it('should configure the s2sConfig object with appnexus vendor defaults unless specified by user', function () { const options = { accountId: '123', bidders: ['appnexus'], @@ -1016,15 +1700,13 @@ describe('S2S Adapter', () => { expect(vendorConfig).to.have.property('accountId', '123'); expect(vendorConfig).to.have.property('adapter', 'prebidServer'); expect(vendorConfig.bidders).to.deep.equal(['appnexus']); - expect(vendorConfig.cookieSet).to.be.false; - expect(vendorConfig.cookieSetUrl).to.be.undefined; expect(vendorConfig.enabled).to.be.true; expect(vendorConfig).to.have.property('endpoint', '//prebid.adnxs.com/pbs/v1/openrtb2/auction'); expect(vendorConfig).to.have.property('syncEndpoint', '//prebid.adnxs.com/pbs/v1/cookie_sync'); expect(vendorConfig).to.have.property('timeout', 750); }); - it('should configure the s2sConfig object with rubicon vendor defaults unless specified by user', () => { + it('should configure the s2sConfig object with rubicon vendor defaults unless specified by user', function () { const options = { accountId: 'abc', bidders: ['rubicon'], @@ -1039,12 +1721,74 @@ describe('S2S Adapter', () => { expect(vendorConfig).to.have.property('accountId', 'abc'); expect(vendorConfig).to.have.property('adapter', 'prebidServer'); expect(vendorConfig.bidders).to.deep.equal(['rubicon']); - expect(vendorConfig.cookieSet).to.be.false; - expect(vendorConfig.cookieSetUrl).to.be.undefined; expect(vendorConfig.enabled).to.be.true; - expect(vendorConfig).to.have.property('endpoint', '//prebid-server.rubiconproject.com/auction'); + expect(vendorConfig).to.have.property('endpoint', '//prebid-server.rubiconproject.com/openrtb2/auction'); expect(vendorConfig).to.have.property('syncEndpoint', '//prebid-server.rubiconproject.com/cookie_sync'); expect(vendorConfig).to.have.property('timeout', 750); }); + + it('should return proper defaults', function () { + expect(config.getConfig('s2sConfig')).to.deep.equal({ + 'accountId': 'abc', + 'adapter': 'prebidServer', + 'bidders': ['rubicon'], + 'defaultVendor': 'rubicon', + 'enabled': true, + 'endpoint': '//prebid-server.rubiconproject.com/openrtb2/auction', + 'syncEndpoint': '//prebid-server.rubiconproject.com/cookie_sync', + 'timeout': 750 + }) + }); + + it('should return default adapterOptions if not set', function () { + config.setConfig({ + s2sConfig: { + accountId: 'abc', + bidders: ['rubicon'], + defaultVendor: 'rubicon', + timeout: 750 + } + }); + expect(config.getConfig('s2sConfig')).to.deep.equal({ + enabled: true, + timeout: 750, + adapter: 'prebidServer', + accountId: 'abc', + bidders: ['rubicon'], + defaultVendor: 'rubicon', + endpoint: '//prebid-server.rubiconproject.com/openrtb2/auction', + syncEndpoint: '//prebid-server.rubiconproject.com/cookie_sync' + }) + }); + + it('should set adapterOptions', function () { + config.setConfig({ + s2sConfig: { + adapterOptions: { + rubicon: { + singleRequest: true, + foo: 'bar' + } + } + } + }); + expect(config.getConfig('s2sConfig').adapterOptions).to.deep.equal({ + rubicon: { + singleRequest: true, + foo: 'bar' + } + }) + }); + + it('should set syncUrlModifier', function () { + config.setConfig({ + s2sConfig: { + syncUrlModifier: { + appnexus: () => {} + } + } + }); + expect(typeof config.getConfig('s2sConfig').syncUrlModifier.appnexus).to.equal('function') + }); }); }); diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..cd414a70236 --- /dev/null +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -0,0 +1,117 @@ +import prebidmanagerAnalytics from 'modules/prebidmanagerAnalyticsAdapter'; +import {expect} from 'chai'; +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('Prebid Manager Analytics Adapter', function () { + let xhr; + let requests; + + let bidWonEvent = { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'adId': '1ebb82ec35375e', + 'mediaType': 'banner', + 'cpm': 0.5, + 'requestId': '1582271863760569973', + 'creative_id': '96846035', + 'creativeId': '96846035', + 'ttl': 60, + 'currency': 'USD', + 'netRevenue': true, + 'auctionId': '9c7b70b9-b6ab-4439-9e71-b7b382797c18', + 'responseTimestamp': 1537521629657, + 'requestTimestamp': 1537521629331, + 'bidder': 'appnexus', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'timeToRespond': 326, + 'size': '300x250', + 'status': 'rendered', + 'eventType': 'bidWon', + 'ad': 'some ad', + 'adUrl': 'ad url' + }; + + before(function () { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + }); + + after(function () { + xhr.restore(); + }); + + describe('Prebid Manager Analytic tests', function () { + beforeEach(function () { + requests = []; + sinon.stub(events, 'getEvents').returns([]); + }); + + afterEach(function () { + prebidmanagerAnalytics.disableAnalytics(); + events.getEvents.restore(); + }); + + it('support custom endpoint', function () { + let custom_url = 'custom url'; + prebidmanagerAnalytics.enableAnalytics({ + provider: 'prebidmanager', + options: { + url: custom_url, + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + expect(prebidmanagerAnalytics.getOptions().url).to.equal(custom_url); + }); + + it('bid won event', function() { + xhr.onCreate = request => requests.push(request); + let bundleId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; + prebidmanagerAnalytics.enableAnalytics({ + provider: 'prebidmanager', + options: { + bundleId: bundleId + } + }); + + events.emit(constants.EVENTS.BID_WON, bidWonEvent); + prebidmanagerAnalytics.flush(); + + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal('https://endpoint.prebidmanager.com/endpoint'); + expect(requests[0].requestBody.substring(0, 2)).to.equal('1:'); + + const pmEvents = JSON.parse(requests[0].requestBody.substring(2)); + expect(pmEvents.pageViewId).to.exist; + expect(pmEvents.bundleId).to.equal(bundleId); + expect(pmEvents.ver).to.equal(1); + expect(pmEvents.events.length).to.equal(2); + expect(pmEvents.events[0].eventType).to.equal('pageView'); + expect(pmEvents.events[1].eventType).to.equal('bidWon'); + expect(pmEvents.events[1].ad).to.be.undefined; + expect(pmEvents.events[1].adUrl).to.be.undefined; + }); + + it('track event without errors', function () { + sinon.spy(prebidmanagerAnalytics, 'track'); + + prebidmanagerAnalytics.enableAnalytics({ + provider: 'prebidmanager', + options: { + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + events.emit(constants.EVENTS.AUCTION_INIT, {}); + events.emit(constants.EVENTS.BID_REQUESTED, {}); + events.emit(constants.EVENTS.BID_RESPONSE, {}); + events.emit(constants.EVENTS.BID_WON, {}); + events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.BID_TIMEOUT, {}); + + sinon.assert.callCount(prebidmanagerAnalytics.track, 6); + }); + }); +}); diff --git a/test/spec/modules/projectLimeLightBidAdapter_spec.js b/test/spec/modules/projectLimeLightBidAdapter_spec.js new file mode 100644 index 00000000000..434b3fbccb5 --- /dev/null +++ b/test/spec/modules/projectLimeLightBidAdapter_spec.js @@ -0,0 +1,170 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/projectLimeLightBidAdapter'; + +describe('ProjectLimeLightAdapter', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'project-limelight', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('//ads.project-limelight.com/hb'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'secure', 'adUnits'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.secure).to.be.a('boolean'); + let adUnits = data['adUnits']; + for (let i = 0; i < adUnits.length; i++) { + let adUnit = adUnits[i]; + expect(adUnit).to.have.all.keys('id', 'bidId', 'type', 'sizes', 'transactionId'); + expect(adUnit.id).to.be.a('number'); + expect(adUnit.bidId).to.be.a('string'); + expect(adUnit.type).to.be.a('string'); + expect(adUnit.transactionId).to.be.a('string'); + expect(adUnit.sizes).to.be.an('array'); + } + }); + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.adUnits).to.be.an('array').that.is.empty; + }); + }); + describe('interpretBannerResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

    Hello ad

    ', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + describe('interpretVideoResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'video', + cpm: 0.3, + width: 320, + height: 50, + vastXml: '', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.vastXml).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + describe('isBidRequestValid', function() { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'project-limelight', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function() { + let bidFailed = { + bidder: 'project-limelight', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + expect(spec.isBidRequestValid(bidFailed)).to.equal(false); + }); + }); +}); diff --git a/test/spec/modules/pubCommonId_spec.js b/test/spec/modules/pubCommonId_spec.js index 50ca4616a4b..fd2bff5bac6 100644 --- a/test/spec/modules/pubCommonId_spec.js +++ b/test/spec/modules/pubCommonId_spec.js @@ -5,22 +5,44 @@ import { setConfig, isPubcidEnabled, getExpInterval, - initPubcid } from 'modules/pubCommonId'; + initPubcid, + setStorageItem, + getStorageItem, + removeStorageItem, + getPubcidConfig } from 'modules/pubCommonId'; import { getAdUnits } from 'test/fixtures/fixtures'; import * as auctionModule from 'src/auction'; import { registerBidder } from 'src/adapters/bidderFactory'; import * as utils from 'src/utils'; +let events = require('src/events'); +let constants = require('src/constants.json'); + var assert = require('chai').assert; var expect = require('chai').expect; -const COOKIE_NAME = '_pubcid'; +const ID_NAME = '_pubcid'; +const EXP = '_exp'; const TIMEOUT = 2000; +const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89a-f][0-9a-f]{3}-[0-9a-f]{12}$/; + +function cleanUp() { + window.document.cookie = ID_NAME + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + localStorage.removeItem(ID_NAME); + localStorage.removeItem(ID_NAME + EXP); +} + describe('Publisher Common ID', function () { + afterEach(function () { + $$PREBID_GLOBAL$$.requestBids.removeAll(); + }); describe('Decorate adUnits', function () { - before(function() { - window.document.cookie = COOKIE_NAME + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + beforeEach(function() { + cleanUp(); + }); + afterEach(function() { + cleanUp(); }); it('Check same cookie', function () { @@ -28,20 +50,26 @@ describe('Publisher Common ID', function () { let adUnits2 = getAdUnits(); let innerAdUnits1; let innerAdUnits2; - let pubcid = getCookie(COOKIE_NAME); + let pubcid; - expect(pubcid).to.be.null; // there should be no cookie initially + expect(getCookie(ID_NAME)).to.be.null; // there should be no cookie initially + expect(localStorage.getItem(ID_NAME)).to.be.null; // there should be no local storage item either - requestBidHook({adUnits: adUnits1}, (config) => { innerAdUnits1 = config.adUnits }); - pubcid = getCookie(COOKIE_NAME); // cookies is created after requestbidHook + requestBidHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); + pubcid = localStorage.getItem(ID_NAME); // local storage item is created after requestbidHook innerAdUnits1.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); expect(bid.crumbs.pubcid).to.equal(pubcid); }); }); - requestBidHook({adUnits: adUnits2}, (config) => { innerAdUnits2 = config.adUnits }); + + // verify cookie is null + expect(getCookie(ID_NAME)).to.be.null; + + // verify same pubcid is preserved + requestBidHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); assert.deepEqual(innerAdUnits1, innerAdUnits2); }); @@ -53,27 +81,30 @@ describe('Publisher Common ID', function () { let pubcid1; let pubcid2; - requestBidHook({adUnits: adUnits1}, (config) => { innerAdUnits1 = config.adUnits }); - pubcid1 = getCookie(COOKIE_NAME); // get first cookie - setCookie(COOKIE_NAME, '', -1); // erase cookie + requestBidHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); + pubcid1 = localStorage.getItem(ID_NAME); // get first pubcid + removeStorageItem(ID_NAME); // remove storage + + expect(pubcid1).to.not.be.null; innerAdUnits1.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); expect(bid.crumbs.pubcid).to.equal(pubcid1); }); }); - requestBidHook({adUnits: adUnits2}, (config) => { innerAdUnits2 = config.adUnits }); - pubcid2 = getCookie(COOKIE_NAME); // get second cookie + requestBidHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); + pubcid2 = localStorage.getItem(ID_NAME); // get second pubcid innerAdUnits2.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); expect(bid.crumbs.pubcid).to.equal(pubcid2); }); }); + expect(pubcid2).to.not.be.null; expect(pubcid1).to.not.equal(pubcid2); }); @@ -82,28 +113,91 @@ describe('Publisher Common ID', function () { let innerAdUnits; let pubcid = utils.generateUUID(); - setCookie(COOKIE_NAME, pubcid, 600); - requestBidHook({adUnits}, (config) => { innerAdUnits = config.adUnits }); + setCookie(ID_NAME, pubcid, 600); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); expect(bid.crumbs.pubcid).to.equal(pubcid); }); }); }); + + it('Replicate cookie to storage', function() { + let adUnits = getAdUnits(); + let innerAdUnits; + let pubcid = utils.generateUUID(); + + setCookie(ID_NAME, pubcid, 600); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getStorageItem(ID_NAME)).to.equal(pubcid); + }); + + it('Does not replicate storage to cookie', function() { + let adUnits = getAdUnits(); + let innerAdUnits; + let pubcid = utils.generateUUID(); + + setStorageItem(ID_NAME, pubcid, 600); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getCookie(ID_NAME)).to.be.null; + }); + + it('Cookie only', function() { + setConfig({type: 'cookie'}); + let adUnits = getAdUnits(); + let innerAdUnits; + + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getCookie(ID_NAME)).to.match(uuidPattern); + expect(getStorageItem(ID_NAME)).to.be.null; + }); + + it('Storage only', function() { + setConfig({type: 'html5'}); + let adUnits = getAdUnits(); + let innerAdUnits; + + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getCookie(ID_NAME)).to.be.null; + expect(getStorageItem(ID_NAME)).to.match(uuidPattern); + }); + + it('Bad id recovery', function() { + let adUnits = getAdUnits(); + let innerAdUnits; + + setStorageItem(ID_NAME, 'undefined', 600); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getStorageItem(ID_NAME)).to.match(uuidPattern); + }); }); describe('Configuration', function () { + beforeEach(() => { + setConfig(); + cleanUp(); + }); + afterEach(() => { + setConfig(); + cleanUp(); + }); + it('empty config', function () { // this should work as usual setConfig({}); let adUnits = getAdUnits(); let innerAdUnits; - requestBidHook({adUnits}, (config) => { innerAdUnits = config.adUnits }); - let pubcid = getCookie(COOKIE_NAME); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + let pubcid = localStorage.getItem(ID_NAME); innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); expect(bid.crumbs.pubcid).to.equal(pubcid); }); }); @@ -111,35 +205,50 @@ describe('Publisher Common ID', function () { it('disable', function () { setConfig({enable: false}); - setCookie(COOKIE_NAME, '', -1); // erase cookie let adUnits = getAdUnits(); let unmodified = getAdUnits(); let innerAdUnits; expect(isPubcidEnabled()).to.be.false; - requestBidHook({adUnits}, (config) => { innerAdUnits = config.adUnits }); - expect(getCookie(COOKIE_NAME)).to.be.null; + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + expect(getCookie(ID_NAME)).to.be.null; assert.deepEqual(innerAdUnits, unmodified); setConfig({enable: true}); // reset - requestBidHook({adUnits}, (config) => { innerAdUnits = config.adUnits }); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); }); }); }); it('change expiration time', function () { setConfig({expInterval: 100}); - setCookie(COOKIE_NAME, '', -1); // erase cookie expect(getExpInterval()).to.equal(100); let adUnits = getAdUnits(); let innerAdUnits; - requestBidHook({adUnits}, (config) => { innerAdUnits = config.adUnits }); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); innerAdUnits.every((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); }); - }) + }); + }); + + it('disable auto create', function() { + setConfig({ + create: false + }); + + const config = getPubcidConfig(); + expect(config.create).to.be.false; + expect(config.typeEnabled).to.equal('html5'); + + let adUnits = getAdUnits(); + let innerAdUnits; + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + const pubcid = localStorage.getItem(ID_NAME); + expect(pubcid).to.be.null; }); }); @@ -156,7 +265,7 @@ describe('Publisher Common ID', function () { getUserSyncs: () => {} }; - beforeEach(() => { + beforeEach(function () { adUnits = [{ code: 'adUnit-code', mediaTypes: { @@ -176,7 +285,7 @@ describe('Publisher Common ID', function () { registerBidder(sampleSpec); }); - afterEach(() => { + afterEach(function () { auctionModule.newAuction.restore(); }); @@ -184,9 +293,78 @@ describe('Publisher Common ID', function () { $$PREBID_GLOBAL$$.requestBids({adUnits}); adUnits.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.property('crumbs.pubcid'); + expect(bid).to.have.deep.nested.property('crumbs.pubcid'); }); }); }); }); + + describe('Storage item functions', () => { + beforeEach(() => { cleanUp(); }); + afterEach(() => { cleanUp(); }); + + it('Test set', () => { + const key = ID_NAME; + const val = 'test-set-value'; + // Set item in localStorage + const now = Date.now(); + setStorageItem(key, val, 100); + // Check both item and expiry time are stored + const expVal = localStorage.getItem(key + EXP); + const storedVal = localStorage.getItem(key); + // Verify expiry + expect(expVal).to.not.be.null; + const expDate = new Date(expVal); + expect((expDate.getTime() - now) / 1000).to.be.closeTo(100 * 60, 5); + // Verify value + expect(storedVal).to.equal(val); + }); + + it('Test get and remove', () => { + const key = ID_NAME; + const val = 'test-get-remove'; + setStorageItem(key, val, 10); + expect(getStorageItem(key)).to.equal(val); + removeStorageItem(key); + expect(getStorageItem(key)).to.be.null; + }); + + it('Test expiry', () => { + const key = ID_NAME; + const val = 'test-expiry'; + setStorageItem(key, val, -1); + expect(localStorage.getItem(key)).to.equal(val); + expect(getStorageItem(key)).to.be.null; + expect(localStorage.getItem(key)).to.be.null; + }); + }); + + describe('event callback', () => { + beforeEach(() => { + setConfig(); + cleanUp(); + sinon.stub(events, 'getEvents').returns([]); + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(() => { + setConfig(); + cleanUp(); + events.getEvents.restore(); + utils.triggerPixel.restore(); + }); + it('auction end trigger', () => { + setConfig({ + pixelUrl: '/any/url' + }); + + let adUnits = getAdUnits(); + let innerAdUnits; + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(utils.triggerPixel.called).to.be.false; + events.emit(constants.EVENTS.AUCTION_END, {}); + expect(utils.triggerPixel.called).to.be.true; + expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/url'); + }); + }); }); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 09acbbe95a0..ababf2dcf5f 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1,16 +1,58 @@ import {expect} from 'chai'; import {spec} from 'modules/pubmaticBidAdapter'; import * as utils from 'src/utils'; +import {config} from 'src/config'; const constants = require('src/constants.json'); -describe('PubMatic adapter', () => { +describe('PubMatic adapter', function () { let bidRequests; + let videoBidRequests; + let multipleMediaRequests; let bidResponses; + let nativeBidRequests; + let nativeBidRequestsWithAllParams; + let nativeBidRequestsWithoutAsset; + let nativeBidRequestsWithRequiredParam; + let nativeBidResponse; + let validnativeBidImpression; + let validnativeBidImpressionWithRequiredParam; + let nativeBidImpressionWithoutRequiredParams; + let validnativeBidImpressionWithAllParams; + let bannerAndVideoBidRequests; + let bannerAndNativeBidRequests; + let videoAndNativeBidRequests; + let bannerVideoAndNativeBidRequests; + let bannerBidResponse; + let videoBidResponse; + let schainConfig; + + beforeEach(function () { + schainConfig = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + } + ] + }; - beforeEach(() => { bidRequests = [ { bidder: 'pubmatic', + mediaTypes: { + banner: { + sizes: [[728, 90], [160, 600]] + } + }, params: { publisherId: '301', adSlot: '/15671365/DMDemo@300x250:0', @@ -23,13 +65,466 @@ describe('PubMatic adapter', () => { lon: '23.7', wiid: '1234567890', profId: '100', - verId: '200' + verId: '200', + currency: 'AUD', + dctr: 'key1:val1,val2|key2:val1' }, placementCode: '/19968336/header-bid-tag-1', sizes: [[300, 250], [300, 600]], bidId: '23acc48ad47af5', requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', + schain: schainConfig + } + ]; + + videoBidRequests = + [ + { + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bidder: 'pubmatic', + bidId: '22bddb28db77d', + params: { + publisherId: '5890', + adSlot: 'Div1@0x0', // ad_id or tagid + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30, + startdelay: 5, + playbackmethod: [1, 3], + api: [1, 2], + protocols: [2, 3], + battr: [13, 14], + linearity: 1, + placement: 2, + minbitrate: 10, + maxbitrate: 10 + } + } + } + ]; + + multipleMediaRequests = [ + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200' + } + }, + { + code: 'div-instream', + mediaTypes: { + video: { + context: 'instream', + playerSize: [300, 250] + }, + }, + bidder: 'pubmatic', + params: { + publisherId: '5890', + adSlot: 'Div1@640x480', // ad_id or tagid + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30, + startdelay: 15, + playbackmethod: [1, 3], + api: [1, 2], + protocols: [2, 3], + w: 640, + h: 480, + battr: [13, 14], + linearity: 1, + placement: 2, + minbitrate: 100, + maxbitrate: 4096 + } + } + } + ]; + + nativeBidRequests = [{ + code: '/19968336/prebid_native_example_1', + sizes: [ + [300, 250] + ], + mediaTypes: { + native: { + title: { + required: true, + length: 80 + }, + image: { + required: true, + sizes: [300, 250] + }, + sponsoredBy: { + required: true + } + } + }, + nativeParams: { + title: { required: true, length: 80 }, + image: { required: true, sizes: [300, 250] }, + sponsoredBy: { required: true } + }, + bidder: 'pubmatic', + params: { + publisherId: '5670', + adSlot: '/43743431/NativeAutomationPrebid@1x1', + }, + bidId: '2a5571261281d4', + requestId: 'B68287E1-DC39-4B38-9790-FE4F179739D6', + bidderRequestId: '1c56ad30b9b8ca8', + }]; + + nativeBidRequestsWithAllParams = [{ + code: '/19968336/prebid_native_example_1', + sizes: [ + [300, 250] + ], + mediaTypes: { + native: { + title: {required: true, len: 80, ext: {'title1': 'title2'}}, + icon: {required: true, sizes: [50, 50], ext: {'icon1': 'icon2'}}, + image: {required: true, sizes: [728, 90], ext: {'image1': 'image2'}, 'mimes': ['image/png', 'image/gif']}, + sponsoredBy: {required: true, len: 10, ext: {'sponsor1': 'sponsor2'}}, + body: {required: true, len: 10, ext: {'body1': 'body2'}}, + rating: {required: true, len: 10, ext: {'rating1': 'rating2'}}, + likes: {required: true, len: 10, ext: {'likes1': 'likes2'}}, + downloads: {required: true, len: 10, ext: {'downloads1': 'downloads2'}}, + price: {required: true, len: 10, ext: {'price1': 'price2'}}, + saleprice: {required: true, len: 10, ext: {'saleprice1': 'saleprice2'}}, + phone: {required: true, len: 10, ext: {'phone1': 'phone2'}}, + address: {required: true, len: 10, ext: {'address1': 'address2'}}, + desc2: {required: true, len: 10, ext: {'desc21': 'desc22'}}, + displayurl: {required: true, len: 10, ext: {'displayurl1': 'displayurl2'}} + } + }, + nativeParams: { + title: {required: true, len: 80, ext: {'title1': 'title2'}}, + icon: {required: true, sizes: [50, 50], ext: {'icon1': 'icon2'}}, + image: {required: true, sizes: [728, 90], ext: {'image1': 'image2'}, 'mimes': ['image/png', 'image/gif']}, + sponsoredBy: {required: true, len: 10, ext: {'sponsor1': 'sponsor2'}}, + body: {required: true, len: 10, ext: {'body1': 'body2'}}, + rating: {required: true, len: 10, ext: {'rating1': 'rating2'}}, + likes: {required: true, len: 10, ext: {'likes1': 'likes2'}}, + downloads: {required: true, len: 10, ext: {'downloads1': 'downloads2'}}, + price: {required: true, len: 10, ext: {'price1': 'price2'}}, + saleprice: {required: true, len: 10, ext: {'saleprice1': 'saleprice2'}}, + phone: {required: true, len: 10, ext: {'phone1': 'phone2'}}, + address: {required: true, len: 10, ext: {'address1': 'address2'}}, + desc2: {required: true, len: 10, ext: {'desc21': 'desc22'}}, + displayurl: {required: true, len: 10, ext: {'displayurl1': 'displayurl2'}} + }, + bidder: 'pubmatic', + params: { + publisherId: '5670', + adSlot: '/43743431/NativeAutomationPrebid@1x1', + }, + bidId: '2a5571261281d4', + requestId: 'B68287E1-DC39-4B38-9790-FE4F179739D6', + bidderRequestId: '1c56ad30b9b8ca8', + }]; + + nativeBidRequestsWithoutAsset = [{ + code: '/19968336/prebid_native_example_1', + sizes: [ + [300, 250] + ], + mediaTypes: { + native: { + type: 'image' + } + }, + nativeParams: { + title: { required: true }, + image: { required: true }, + sponsoredBy: { required: true }, + clickUrl: { required: true } + }, + bidder: 'pubmatic', + params: { + publisherId: '5670', + adSlot: '/43743431/NativeAutomationPrebid@1x1', + } + }]; + + nativeBidRequestsWithRequiredParam = [{ + code: '/19968336/prebid_native_example_1', + sizes: [ + [300, 250] + ], + mediaTypes: { + native: { + title: { + required: false, + length: 80 + }, + image: { + required: false, + sizes: [300, 250] + }, + sponsoredBy: { + required: true + } + } + }, + nativeParams: { + title: { required: false, length: 80 }, + image: { required: false, sizes: [300, 250] }, + sponsoredBy: { required: true } + }, + bidder: 'pubmatic', + params: { + publisherId: '5670', + adSlot: '/43743431/NativeAutomationPrebid@1x1', + } + }]; + + bannerAndVideoBidRequests = [ + { + code: 'div-banner-video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + }, + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'AUD', + dctr: 'key1:val1,val2|key2:val1', + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30, + startdelay: 15, + playbackmethod: [1, 3], + api: [1, 2], + protocols: [2, 3], + w: 640, + h: 480, + battr: [13, 14], + linearity: 1, + placement: 2, + minbitrate: 100, + maxbitrate: 4096 + } + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[728, 90]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + + bannerAndNativeBidRequests = [ + { + code: 'div-banner-native', + mediaTypes: { + native: { + title: { + required: true, + length: 80 + }, + image: { + required: true, + sizes: [300, 250] + }, + sponsoredBy: { + required: true + } + }, + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + nativeParams: { + title: { required: true, length: 80 }, + image: { required: true, sizes: [300, 250] }, + sponsoredBy: { required: true } + }, + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'AUD', + dctr: 'key1:val1,val2|key2:val1' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[728, 90]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + + videoAndNativeBidRequests = [ + { + code: 'div-video-native', + mediaTypes: { + native: { + title: { + required: true, + length: 80 + }, + image: { + required: true, + sizes: [300, 250] + }, + sponsoredBy: { + required: true + } + }, + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + nativeParams: { + title: { required: true, length: 80 }, + image: { required: true, sizes: [300, 250] }, + sponsoredBy: { required: true } + }, + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30, + startdelay: 15, + playbackmethod: [1, 3], + api: [1, 2], + protocols: [2, 3], + w: 640, + h: 480, + battr: [13, 14], + linearity: 1, + placement: 2, + minbitrate: 100, + maxbitrate: 4096 + } + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[728, 90]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + + bannerVideoAndNativeBidRequests = [ + { + code: 'div-video-native', + mediaTypes: { + native: { + title: { + required: true, + length: 80 + }, + image: { + required: true, + sizes: [300, 250] + }, + sponsoredBy: { + required: true + } + }, + video: { + playerSize: [640, 480], + context: 'instream' + }, + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + nativeParams: { + title: { required: true, length: 80 }, + image: { required: true, sizes: [300, 250] }, + sponsoredBy: { required: true } + }, + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30, + startdelay: 15, + playbackmethod: [1, 3], + api: [1, 2], + protocols: [2, 3], + w: 640, + h: 480, + battr: [13, 14], + linearity: 1, + placement: 2, + minbitrate: 100, + maxbitrate: 4096 + } + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[728, 90]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' } ]; @@ -43,10 +538,13 @@ describe('PubMatic adapter', () => { 'impid': '22bddb28db77d', 'price': 1.3, 'adm': 'image3.pubmatic.com Layer based creative', + 'adomain': ['blackrock.com'], 'h': 250, 'w': 300, 'ext': { - 'deal_channel': 6 + 'deal_channel': 6, + 'advid': 976, + 'dspid': 123 } }] }, { @@ -55,20 +553,107 @@ describe('PubMatic adapter', () => { 'impid': '22bddb28db77e', 'price': 1.7, 'adm': 'image3.pubmatic.com Layer based creative', + 'adomain': ['hivehome.com'], + 'h': 250, + 'w': 300, + 'ext': { + 'deal_channel': 5, + 'advid': 832, + 'dspid': 422 + } + }] + }] + } + }; + + nativeBidResponse = { + 'body': { + 'id': '1544691825939', + 'seatbid': [{ + 'bid': [{ + 'id': 'B68287E1-DC39-4B38-9790-FE4F179739D6', + 'impid': '2a5571261281d4', + 'price': 0.01, + 'adm': "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Native Test Title\"}},{\"id\":2,\"img\":{\"h\":627,\"url\":\"http://stagingpub.net/native_ads/PM-Native-Ad-1200x627.png\",\"w\":1200}},{\"data\":{\"value\":\"Sponsored By PubMatic\"},\"id\":4}],\"imptrackers\":[\"http://imptracker.com/main/9bde02d0-6017-11e4-9df7-005056967c35\",\"http://172.16.4.213/AdServer/AdDisplayTrackerServlet?operId=1&pubId=5890&siteId=5892&adId=6016&adType=12&adServerId=243&kefact=0.010000&kaxefact=0.010000&kadNetFrequecy=0&kadwidth=0&kadheight=0&kadsizeid=7&kltstamp=1544692761&indirectAdId=0&adServerOptimizerId=2&ranreq=0.1&kpbmtpfact=1.000000&dcId=1&tldId=0&passback=0&svr=MADS1107&ekefact=GSQSXOLKDgBAvRnoiNj0LxtpAnNEO30u1ZI5sITloOsP7gzh&ekaxefact=GSQSXAXLDgD0fOZLCjgbnVJiyS3D65dqDkxfs2ArpC3iugXw&ekpbmtpfact=GSQSXCDLDgB5mcooOvXtCKmx7TnNDJDY2YuHFOL3o9ceoU4H&crID=campaign111&lpu=advertiserdomain.com&ucrid=273354366805642829&campaignId=16981&creativeId=0&pctr=0.000000&wDSPByrId=511&wDspId=6&wbId=0&wrId=0&wAdvID=1&isRTB=1&rtbId=C09BB577-B8C1-4C3E-A0FF-73F6F631C80A&imprId=B68287E1-DC39-4B38-9790-FE4F179739D6&oid=B68287E1-DC39-4B38-9790-FE4F179739D6&pageURL=http%3A%2F%2Ftest.com%2FTestPages%2Fnativead.html\"],\"jstracker\":\" ', 'h': 250, 'w': 300, 'ext': { - 'deal_channel': 5 + 'deal_channel': 6 } }] }] } }; + + videoBidResponse = { + 'body': { + 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', + 'seatbid': [{ + 'bid': [{ + 'id': '74858439-49D7-4169-BA5D-44A046315B2F', + 'impid': '22bddb28db77d', + 'price': 1.3, + 'adm': 'Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://dsptracker.com/{PSPM}00:00:04https://www.pubmatic.com', + 'h': 250, + 'w': 300, + 'ext': { + 'deal_channel': 6 + } + }] + }] + } + } }); - describe('implementation', () => { - describe('Bid validations', () => { - it('valid bid case', () => { + describe('implementation', function () { + describe('Bid validations', function () { + it('valid bid case', function () { let validBid = { bidder: 'pubmatic', params: { @@ -80,7 +665,7 @@ describe('PubMatic adapter', () => { expect(isValid).to.equal(true); }); - it('invalid bid case: publisherId not passed', () => { + it('invalid bid case: publisherId not passed', function () { let validBid = { bidder: 'pubmatic', params: { @@ -91,7 +676,7 @@ describe('PubMatic adapter', () => { expect(isValid).to.equal(false); }); - it('invalid bid case: publisherId is not string', () => { + it('invalid bid case: publisherId is not string', function () { let validBid = { bidder: 'pubmatic', params: { @@ -103,38 +688,38 @@ describe('PubMatic adapter', () => { expect(isValid).to.equal(false); }); - it('invalid bid case: adSlot not passed', () => { - let validBid = { - bidder: 'pubmatic', - params: { - publisherId: '301' - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - - it('invalid bid case: adSlot is not string', () => { + it('valid bid case: adSlot is not passed', function () { let validBid = { bidder: 'pubmatic', params: { - publisherId: '301', - adSlot: 15671365 + publisherId: '301' } }, isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); + expect(isValid).to.equal(true); }); }); - describe('Request formation', () => { - it('Endpoint checking', () => { + describe('Request formation', function () { + it('buildRequests function should not modify original bidRequests object', function () { + let originalBidRequests = utils.deepClone(bidRequests); + let request = spec.buildRequests(bidRequests); + expect(bidRequests).to.deep.equal(originalBidRequests); + }); + + it('buildRequests function should not modify original nativebidRequests object', function () { + let originalBidRequests = utils.deepClone(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests); + expect(nativeBidRequests).to.deep.equal(originalBidRequests); + }); + + it('Endpoint checking', function () { let request = spec.buildRequests(bidRequests); - expect(request.url).to.equal('//hbopenbid.pubmatic.com/translator?source=prebid-client'); + expect(request.url).to.equal('https://hbopenbid.pubmatic.com/translator?source=prebid-client'); expect(request.method).to.equal('POST'); }); - it('Request params check', () => { + it('Request params check', function () { let request = spec.buildRequests(bidRequests); let data = JSON.parse(request.data); expect(data.at).to.equal(1); // auction type @@ -142,13 +727,15 @@ describe('PubMatic adapter', () => { expect(data.site.domain).to.be.a('string'); // domain should be set expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id + expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.site.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude - expect(data.ext.wrapper.wv).to.equal(constants.REPO_AND_VERSION); // Wrapper Version + expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID @@ -160,9 +747,206 @@ describe('PubMatic adapter', () => { expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); }); - it('Request params check with GDPR Consent', () => { + it('Request params check: without adSlot', function () { + delete bidRequests[0].params.adSlot; + let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); + expect(data.at).to.equal(1); // auction type + expect(data.cur[0]).to.equal('USD'); // currency + expect(data.site.domain).to.be.a('string'); // domain should be set + expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL + expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id + expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.site.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); + expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB + expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender + expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID + expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID + expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID + + expect(data.imp[0].id).to.equal(bidRequests[0].bidId); // Prebid bid id is passed as id + expect(data.imp[0].bidfloor).to.equal(parseFloat(bidRequests[0].params.kadfloor)); // kadfloor + expect(data.imp[0].tagid).to.deep.equal(undefined); // tagid + expect(data.imp[0].banner.w).to.equal(728); // width + expect(data.imp[0].banner.h).to.equal(90); // height + expect(data.imp[0].banner.format).to.deep.equal([{w: 160, h: 600}]); + expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + }); + + it('Request params multi size format object check', function () { + let bidRequests = [ + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'AUD' + }, + placementCode: '/19968336/header-bid-tag-1', + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + /* case 1 - size passed in adslot */ + let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); + + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + + /* case 2 - size passed in adslot as well as in sizes array */ + bidRequests[0].sizes = [[300, 600], [300, 250]]; + bidRequests[0].mediaTypes = { + banner: { + sizes: [[300, 600], [300, 250]] + } + }; + request = spec.buildRequests(bidRequests); + data = JSON.parse(request.data); + + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + + /* case 3 - size passed in sizes but not in adslot */ + bidRequests[0].params.adSlot = '/15671365/DMDemo'; + bidRequests[0].sizes = [[300, 250], [300, 600]]; + bidRequests[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + } + }; + request = spec.buildRequests(bidRequests); + data = JSON.parse(request.data); + + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + expect(data.imp[0].banner.format).exist.and.to.be.an('array'); + expect(data.imp[0].banner.format[0]).exist.and.to.be.an('object'); + expect(data.imp[0].banner.format[0].w).to.equal(300); // width + expect(data.imp[0].banner.format[0].h).to.equal(600); // height + }); + + it('Request params currency check', function () { + let multipleBidRequests = [ + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'AUD' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + }, + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'GBP' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + + /* case 1 - + currency specified in both adunits + output: imp[0] and imp[1] both use currency specified in bidRequests[0].params.currency + + */ + let request = spec.buildRequests(multipleBidRequests); + let data = JSON.parse(request.data); + + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + expect(data.imp[1].bidfloorcur).to.equal(bidRequests[0].params.currency); + + /* case 2 - + currency specified in only 1st adunit + output: imp[0] and imp[1] both use currency specified in bidRequests[0].params.currency + + */ + delete multipleBidRequests[1].params.currency; + request = spec.buildRequests(multipleBidRequests); + data = JSON.parse(request.data); + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + expect(data.imp[1].bidfloorcur).to.equal(bidRequests[0].params.currency); + + /* case 3 - + currency specified in only 1st adunit + output: imp[0] and imp[1] both use default currency - USD + + */ + delete multipleBidRequests[0].params.currency; + request = spec.buildRequests(multipleBidRequests); + data = JSON.parse(request.data); + expect(data.imp[0].bidfloorcur).to.equal('USD'); + expect(data.imp[1].bidfloorcur).to.equal('USD'); + + /* case 4 - + currency not specified in 1st adunit but specified in 2nd adunit + output: imp[0] and imp[1] both use default currency - USD + + */ + multipleBidRequests[1].params.currency = 'AUD'; + request = spec.buildRequests(multipleBidRequests); + data = JSON.parse(request.data); + expect(data.imp[0].bidfloorcur).to.equal('USD'); + expect(data.imp[1].bidfloorcur).to.equal('USD'); + }); + + it('Request params check with GDPR Consent', function () { let bidRequest = { gdprConsent: { consentString: 'kjfdniwjnifwenrif3', @@ -184,7 +968,7 @@ describe('PubMatic adapter', () => { expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude - expect(data.ext.wrapper.wv).to.equal(constants.REPO_AND_VERSION); // Wrapper Version + expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID @@ -198,16 +982,1225 @@ describe('PubMatic adapter', () => { expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid }); - it('invalid adslot', () => { - bidRequests[0].params.adSlot = '/15671365/DMDemo'; - let request = spec.buildRequests(bidRequests); - expect(request).to.equal(undefined); - }); + it('Request should have digitrust params', function() { + window.DigiTrust = { + getUser: function () { + } + }; + var bidRequest = {}; + let sandbox = sinon.sandbox.create(); + sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => + ({ + success: true, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + }) + ); + + let request = spec.buildRequests(bidRequests, bidRequest); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'digitru.st', + 'uids': [{ + 'id': 'testId', + 'atype': 1, + 'ext': { + 'keyv': 4 + } + }] + }]); + sandbox.restore(); + delete window.DigiTrust; + }); + + it('Request should not have digitrust params when DigiTrust not loaded', function() { + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + + it('Request should not have digitrust params due to optout', function() { + window.DigiTrust = { + getUser: function () { + } + }; + let sandbox = sinon.sandbox.create(); + sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => + ({ + success: true, + identity: { + privacy: {optout: true}, + id: 'testId', + keyv: 4 + } + }) + ); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + sandbox.restore(); + delete window.DigiTrust; + }); + + it('Request should not have digitrust params due to failure', function() { + window.DigiTrust = { + getUser: function () { + } + }; + let sandbox = sinon.sandbox.create(); + sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => + ({ + success: false, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + }) + ); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + sandbox.restore(); + delete window.DigiTrust; + }); + + describe('DigiTrustId from config', function() { + var origGetConfig; + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + window.DigiTrust = { + getUser: sandbox.spy() + }; + }); + + afterEach(() => { + sandbox.restore(); + delete window.DigiTrust; + }); + + it('Request should have digiTrustId config params', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + digiTrustId: { + success: true, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'digitru.st', + 'uids': [{ + 'id': 'testId', + 'atype': 1, + 'ext': { + 'keyv': 4 + } + }] + }]); + // should not have called DigiTrust.getUser() + expect(window.DigiTrust.getUser.notCalled).to.equal(true); + }); + + it('Request should not have digiTrustId config params due to optout', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + digiTrustId: { + success: true, + identity: { + privacy: {optout: true}, + id: 'testId', + keyv: 4 + } + } + } + return config[key]; + }); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + // should not have called DigiTrust.getUser() + expect(window.DigiTrust.getUser.notCalled).to.equal(true); + }); + + it('Request should not have digiTrustId config params due to failure', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + digiTrustId: { + success: false, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + } + } + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + // should not have called DigiTrust.getUser() + expect(window.DigiTrust.getUser.notCalled).to.equal(true); + }); + + it('Request should not have digiTrustId config params if they do not exist', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = {}; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + // should have called DigiTrust.getUser() once + expect(window.DigiTrust.getUser.calledOnce).to.equal(true); + }); + }); + + describe('AdsrvrOrgId from config', function() { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('Request should have adsrvrOrgId config params', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should NOT have adsrvrOrgId config params if id in adsrvrOrgId is NOT string', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': 1, + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + + it('Request should NOT have adsrvrOrgId config params if adsrvrOrgId is NOT object', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: null + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + + it('Request should NOT have adsrvrOrgId config params if id in adsrvrOrgId is NOT set', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + }); + + describe('AdsrvrOrgId from userId module', function() { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('Request should have AdsrvrOrgId config params', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID_FROM_USER_ID_MODULE', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should have adsrvrOrgId from UserId Module if config and userId module both have TTD ID', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': 'TTD_ID_FROM_CONFIG', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + bidRequests[0].userId = {}; + bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID_FROM_USER_ID_MODULE', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should NOT have adsrvrOrgId params if userId is NOT object', function() { + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + + it('Request should NOT have adsrvrOrgId params if userId.tdid is NOT string', function() { + bidRequests[0].userId = { + tdid: 1234 + }; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + }); + + describe('AdsrvrOrgId and Digitrust', function() { + // here we are considering cases only of accepting DigiTrustId from config + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + window.DigiTrust = { + getUser: sandbox.spy() + }; + }); + + afterEach(() => { + sandbox.restore(); + delete window.DigiTrust; + }); + + it('Request should have id of both AdsrvrOrgId and Digitrust if both have returned valid ids', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'digitru.st', + 'uids': [{ + 'id': 'testId', + 'atype': 1, + 'ext': { + 'keyv': 4 + } + }] + }, { + 'source': 'adserver.org', + 'uids': [{ + 'id': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should have id of only AdsrvrOrgId and NOT Digitrust if only AdsrvrOrgId have returned valid id', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: true}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should have id of only Digitrust and NOT AdsrvrOrgId if only Digitrust have returned valid id', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'digitru.st', + 'uids': [{ + 'id': 'testId', + 'atype': 1, + 'ext': { + 'keyv': 4 + } + }] + }]); + }); + + it('Request should NOT have id of Digitrust and NOT AdsrvrOrgId if only both have NOT returned valid ids', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: true}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + }); + + describe('UserIds from request', function() { + describe('pubcommon Id', function() { + it('send the pubcommon id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.pubcid = 'pub_common_user_id'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'pubcommon', + 'uids': [{ + 'id': 'pub_common_user_id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.pubcid = 1; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.pubcid = []; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.pubcid = null; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.pubcid = {}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('Digitrust Id', function() { + it('send the digitrust id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.digitrustid = {data: {id: 'digitrust_user_id'}}; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'digitru.st', + 'uids': [{ + 'id': 'digitrust_user_id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.digitrustid = {data: {id: 1}}; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.digitrustid = {data: {id: []}}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.digitrustid = {data: {id: null}}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.digitrustid = {data: {id: {}}}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('ID5 Id', function() { + it('send the id5 id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.id5id = 'id5-user-id'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'id5-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.id5id = 1; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.id5id = []; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.id5id = null; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.id5id = {}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('CriteoRTUS Id', function() { + it('send the criteo id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.criteortus = {pubmatic: {userid: 'criteo-rtus-user-id'}}; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'criteortus', + 'uids': [{ + 'id': 'criteo-rtus-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.criteortus = {appnexus: {userid: 'criteo-rtus-user-id'}}; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.criteortus = {pubmatic: {userid: 1}}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.criteortus = {pubmatic: {userid: []}}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.criteortus = {pubmatic: {userid: null}}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.criteortus = {pubmatic: {userid: {}}}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('IdentityLink Id', function() { + it('send the identity-link id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.idl_env = 'identity-link-user-id'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'liveramp.com', + 'uids': [{ + 'id': 'identity-link-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.idl_env = 1; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.idl_env = []; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.idl_env = null; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.idl_env = {}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + }) + + it('Request params check for video ad', function () { + let request = spec.buildRequests(videoBidRequests); + let data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist; + expect(data.imp[0].tagid).to.equal('Div1'); + expect(data.imp[0].video.ext['video_skippable']).to.equal(videoBidRequests[0].params.video.skippable ? 1 : 0); + expect(data.imp[0]['video']['mimes']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['mimes'][0]).to.equal(videoBidRequests[0].params.video['mimes'][0]); + expect(data.imp[0]['video']['mimes'][1]).to.equal(videoBidRequests[0].params.video['mimes'][1]); + expect(data.imp[0]['video']['minduration']).to.equal(videoBidRequests[0].params.video['minduration']); + expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); + expect(data.imp[0]['video']['startdelay']).to.equal(videoBidRequests[0].params.video['startdelay']); + + expect(data.imp[0]['video']['playbackmethod']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['playbackmethod'][0]).to.equal(videoBidRequests[0].params.video['playbackmethod'][0]); + expect(data.imp[0]['video']['playbackmethod'][1]).to.equal(videoBidRequests[0].params.video['playbackmethod'][1]); + + expect(data.imp[0]['video']['api']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['api'][0]).to.equal(videoBidRequests[0].params.video['api'][0]); + expect(data.imp[0]['video']['api'][1]).to.equal(videoBidRequests[0].params.video['api'][1]); + + expect(data.imp[0]['video']['protocols']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['protocols'][0]).to.equal(videoBidRequests[0].params.video['protocols'][0]); + expect(data.imp[0]['video']['protocols'][1]).to.equal(videoBidRequests[0].params.video['protocols'][1]); + + expect(data.imp[0]['video']['battr']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['battr'][0]).to.equal(videoBidRequests[0].params.video['battr'][0]); + expect(data.imp[0]['video']['battr'][1]).to.equal(videoBidRequests[0].params.video['battr'][1]); + + expect(data.imp[0]['video']['linearity']).to.equal(videoBidRequests[0].params.video['linearity']); + expect(data.imp[0]['video']['placement']).to.equal(videoBidRequests[0].params.video['placement']); + expect(data.imp[0]['video']['minbitrate']).to.equal(videoBidRequests[0].params.video['minbitrate']); + expect(data.imp[0]['video']['maxbitrate']).to.equal(videoBidRequests[0].params.video['maxbitrate']); + + expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); + }); + + it('Request params check for 1 banner and 1 video ad', function () { + let request = spec.buildRequests(multipleMediaRequests); + let data = JSON.parse(request.data); + + expect(data.imp).to.be.an('array') + expect(data.imp).with.length.above(1); + + expect(data.at).to.equal(1); // auction type + expect(data.cur[0]).to.equal('USD'); // currency + expect(data.site.domain).to.be.a('string'); // domain should be set + expect(data.site.page).to.equal(multipleMediaRequests[0].params.kadpageurl); // forced pageURL + expect(data.site.publisher.id).to.equal(multipleMediaRequests[0].params.publisherId); // publisher Id + expect(data.user.yob).to.equal(parseInt(multipleMediaRequests[0].params.yob)); // YOB + expect(data.user.gender).to.equal(multipleMediaRequests[0].params.gender); // Gender + expect(data.device.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude + expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version + expect(data.ext.wrapper.transactionId).to.equal(multipleMediaRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.wiid).to.equal(multipleMediaRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID + expect(data.ext.wrapper.profile).to.equal(parseInt(multipleMediaRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID + expect(data.ext.wrapper.version).to.equal(parseInt(multipleMediaRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID + + // banner imp object check + expect(data.imp[0].id).to.equal(multipleMediaRequests[0].bidId); // Prebid bid id is passed as id + expect(data.imp[0].bidfloor).to.equal(parseFloat(multipleMediaRequests[0].params.kadfloor)); // kadfloor + expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + expect(data.imp[0].ext.pmZoneId).to.equal(multipleMediaRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + + // video imp object check + expect(data.imp[1].video).to.exist; + expect(data.imp[1].tagid).to.equal('Div1'); + expect(data.imp[1].video.ext['video_skippable']).to.equal(multipleMediaRequests[1].params.video.skippable ? 1 : 0); + expect(data.imp[1]['video']['mimes']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['mimes'][0]).to.equal(multipleMediaRequests[1].params.video['mimes'][0]); + expect(data.imp[1]['video']['mimes'][1]).to.equal(multipleMediaRequests[1].params.video['mimes'][1]); + expect(data.imp[1]['video']['minduration']).to.equal(multipleMediaRequests[1].params.video['minduration']); + expect(data.imp[1]['video']['maxduration']).to.equal(multipleMediaRequests[1].params.video['maxduration']); + expect(data.imp[1]['video']['startdelay']).to.equal(multipleMediaRequests[1].params.video['startdelay']); + + expect(data.imp[1]['video']['playbackmethod']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['playbackmethod'][0]).to.equal(multipleMediaRequests[1].params.video['playbackmethod'][0]); + expect(data.imp[1]['video']['playbackmethod'][1]).to.equal(multipleMediaRequests[1].params.video['playbackmethod'][1]); + + expect(data.imp[1]['video']['api']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['api'][0]).to.equal(multipleMediaRequests[1].params.video['api'][0]); + expect(data.imp[1]['video']['api'][1]).to.equal(multipleMediaRequests[1].params.video['api'][1]); + + expect(data.imp[1]['video']['protocols']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['protocols'][0]).to.equal(multipleMediaRequests[1].params.video['protocols'][0]); + expect(data.imp[1]['video']['protocols'][1]).to.equal(multipleMediaRequests[1].params.video['protocols'][1]); + + expect(data.imp[1]['video']['battr']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['battr'][0]).to.equal(multipleMediaRequests[1].params.video['battr'][0]); + expect(data.imp[1]['video']['battr'][1]).to.equal(multipleMediaRequests[1].params.video['battr'][1]); + + expect(data.imp[1]['video']['linearity']).to.equal(multipleMediaRequests[1].params.video['linearity']); + expect(data.imp[1]['video']['placement']).to.equal(multipleMediaRequests[1].params.video['placement']); + expect(data.imp[1]['video']['minbitrate']).to.equal(multipleMediaRequests[1].params.video['minbitrate']); + expect(data.imp[1]['video']['maxbitrate']).to.equal(multipleMediaRequests[1].params.video['maxbitrate']); + + expect(data.imp[1]['video']['w']).to.equal(multipleMediaRequests[1].mediaTypes.video.playerSize[0]); + expect(data.imp[1]['video']['h']).to.equal(multipleMediaRequests[1].mediaTypes.video.playerSize[1]); + }); + + it('Request params should have valid native bid request for all valid params', function () { + let request = spec.buildRequests(nativeBidRequests); + let data = JSON.parse(request.data); + expect(data.imp[0].native).to.exist; + expect(data.imp[0].native['request']).to.exist; + expect(data.imp[0].tagid).to.equal('/43743431/NativeAutomationPrebid'); + expect(data.imp[0]['native']['request']).to.exist.and.to.be.an('string'); + expect(data.imp[0]['native']['request']).to.exist.and.to.equal(validnativeBidImpression.native.request); + }); + + it('Request params should not have valid native bid request for non native request', function () { + let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); + expect(data.imp[0].native).to.not.exist; + }); + + it('Request params should have valid native bid request with valid required param values for all valid params', function () { + let request = spec.buildRequests(nativeBidRequestsWithRequiredParam); + let data = JSON.parse(request.data); + expect(data.imp[0].native).to.exist; + expect(data.imp[0].native['request']).to.exist; + expect(data.imp[0].tagid).to.equal('/43743431/NativeAutomationPrebid'); + expect(data.imp[0]['native']['request']).to.exist.and.to.be.an('string'); + expect(data.imp[0]['native']['request']).to.exist.and.to.equal(validnativeBidImpressionWithRequiredParam.native.request); + }); + + it('should not have valid native request if assets are not defined with minimum required params and only native is the slot', function () { + let request = spec.buildRequests(nativeBidRequestsWithoutAsset); + expect(request).to.deep.equal(undefined); + }); + + it('Request params should have valid native bid request for all native params', function () { + let request = spec.buildRequests(nativeBidRequestsWithAllParams); + let data = JSON.parse(request.data); + expect(data.imp[0].native).to.exist; + expect(data.imp[0].native['request']).to.exist; + expect(data.imp[0].tagid).to.equal('/43743431/NativeAutomationPrebid'); + expect(data.imp[0]['native']['request']).to.exist.and.to.be.an('string'); + expect(data.imp[0]['native']['request']).to.exist.and.to.equal(validnativeBidImpressionWithAllParams.native.request); + }); + + it('Request params - should handle banner and video format in single adunit', function() { + let request = spec.buildRequests(bannerAndVideoBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(300); + expect(data.banner.h).to.equal(250); + expect(data.banner.format).to.exist; + expect(data.banner.format.length).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes.length); + + // Case: when size is not present in adslo + bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; + request = spec.buildRequests(bannerAndVideoBidRequests); + data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes[0][0]); + expect(data.banner.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes[0][1]); + expect(data.banner.format).to.exist; + expect(data.banner.format.length).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes.length - 1); + + expect(data.video).to.exist; + expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); + }); + + it('Request params - banner and video req in single adslot - should ignore banner imp if banner size is set to fluid and send video imp object', function () { + /* Adslot configured for banner and video. + banner size is set to [['fluid'], [300, 250]] + adslot specifies a size as 300x250 + => banner imp object should have primary w and h set to 300 and 250. fluid is ignored + */ + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; + + let request = spec.buildRequests(bannerAndVideoBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(300); + expect(data.banner.h).to.equal(250); + expect(data.banner.format).to.exist; + expect(data.banner.format[0].w).to.equal(160); + expect(data.banner.format[0].h).to.equal(600); + + /* Adslot configured for banner and video. + banner size is set to [['fluid'], [300, 250]] + adslot does not specify any size + => banner imp object should have primary w and h set to 300 and 250. fluid is ignored + */ + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; + bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; + + request = spec.buildRequests(bannerAndVideoBidRequests); + data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(160); + expect(data.banner.h).to.equal(600); + expect(data.banner.format).to.not.exist; + + /* Adslot configured for banner and video. + banner size is set to [[728 90], ['fluid'], [300, 250]] + adslot does not specify any size + => banner imp object should have primary w and h set to 728 and 90. + banner.format should have 300, 250 set in it + fluid is ignore + */ + + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [[728, 90], ['fluid'], [300, 250]]; + request = spec.buildRequests(bannerAndVideoBidRequests); + data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(728); + expect(data.banner.h).to.equal(90); + expect(data.banner.format).to.exist; + expect(data.banner.format[0].w).to.equal(300); + expect(data.banner.format[0].h).to.equal(250); + + /* Adslot configured for banner and video. + banner size is set to [['fluid']] + adslot does not specify any size + => banner object should not be sent in the request. only video should be sent. + */ + + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid']]; + request = spec.buildRequests(bannerAndVideoBidRequests); + data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.not.exist; + expect(data.video).to.exist; + }); + + it('Request params - should not contain banner imp if mediaTypes.banner is not present and sizes is specified in bid.sizes', function() { + delete bannerAndVideoBidRequests[0].mediaTypes.banner; + bannerAndVideoBidRequests[0].params.sizes = [300, 250]; + + let request = spec.buildRequests(bannerAndVideoBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.banner).to.not.exist; + }); + + it('Request params - should handle banner and native format in single adunit', function() { + let request = spec.buildRequests(bannerAndNativeBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(300); + expect(data.banner.h).to.equal(250); + expect(data.banner.format).to.exist; + expect(data.banner.format.length).to.equal(bannerAndNativeBidRequests[0].mediaTypes.banner.sizes.length); + + expect(data.native).to.exist; + expect(data.native.request).to.exist; + }); + + it('Request params - should handle video and native format in single adunit', function() { + let request = spec.buildRequests(videoAndNativeBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.video).to.exist; + expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); + + expect(data.native).to.exist; + expect(data.native.request).to.exist; + }); + + it('Request params - should handle banner, video and native format in single adunit', function() { + let request = spec.buildRequests(bannerVideoAndNativeBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(300); + expect(data.banner.h).to.equal(250); + expect(data.banner.format).to.exist; + expect(data.banner.format.length).to.equal(bannerAndNativeBidRequests[0].mediaTypes.banner.sizes.length); + + expect(data.video).to.exist; + expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); + + expect(data.native).to.exist; + expect(data.native.request).to.exist; + }); + + it('Request params - should not add banner object if mediaTypes.banner is missing, but adunits.sizes is present', function() { + delete bannerAndNativeBidRequests[0].mediaTypes.banner; + bannerAndNativeBidRequests[0].sizes = [729, 90]; + + let request = spec.buildRequests(bannerAndNativeBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.not.exist; + + expect(data.native).to.exist; + expect(data.native.request).to.exist; + }); + + it('Request params - banner and native multiformat request - should not have native object incase of invalid config present', function() { + bannerAndNativeBidRequests[0].mediaTypes.native = { + title: { required: true }, + image: { required: true }, + sponsoredBy: { required: true }, + clickUrl: { required: true } + }; + bannerAndNativeBidRequests[0].nativeParams = { + title: { required: true }, + image: { required: true }, + sponsoredBy: { required: true }, + clickUrl: { required: true } + } + let request = spec.buildRequests(bannerAndNativeBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.native).to.not.exist; + }); + + it('Request params - video and native multiformat request - should not have native object incase of invalid config present', function() { + videoAndNativeBidRequests[0].mediaTypes.native = { + title: { required: true }, + image: { required: true }, + sponsoredBy: { required: true }, + clickUrl: { required: true } + }; + videoAndNativeBidRequests[0].nativeParams = { + title: { required: true }, + image: { required: true }, + sponsoredBy: { required: true }, + clickUrl: { required: true } + } + let request = spec.buildRequests(videoAndNativeBidRequests); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.video).to.exist; + expect(data.native).to.not.exist; + }); }); - describe('Response checking', () => { - it('should check for valid response values', () => { + it('Request params dctr check', function () { + let multipleBidRequests = [ + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'AUD', + dctr: 'key1=val1|key2=val2,!val3' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + }, + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'GBP', + dctr: 'key1=val3|key2=val1,!val3|key3=val123' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + + let request = spec.buildRequests(multipleBidRequests); + let data = JSON.parse(request.data); + + /* case 1 - + dctr is found in adunit[0] + */ + + expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.site.ext.key_val).to.exist.and.to.equal(multipleBidRequests[0].params.dctr); + + /* case 2 - + dctr not present in adunit[0] + */ + delete multipleBidRequests[0].params.dctr; + request = spec.buildRequests(multipleBidRequests); + data = JSON.parse(request.data); + + expect(data.site.ext).to.not.exist; + + /* case 3 - + dctr is present in adunit[0], but is not a string value + */ + multipleBidRequests[0].params.dctr = 123; + request = spec.buildRequests(multipleBidRequests); + data = JSON.parse(request.data); + + expect(data.site.ext).to.not.exist; + }); + + describe('Request param bcat checking', function() { + let multipleBidRequests = [ + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'AUD', + dctr: 'key1=val1|key2=val2,!val3' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + }, + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + kadfloor: '1.2', + pmzoneid: 'aabc, ddef', + kadpageurl: 'www.publisher.com', + yob: '1986', + gender: 'M', + lat: '12.3', + lon: '23.7', + wiid: '1234567890', + profId: '100', + verId: '200', + currency: 'GBP', + dctr: 'key1=val3|key2=val1,!val3|key3=val123' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + + it('bcat: pass only strings', function() { + multipleBidRequests[0].params.bcat = [1, 2, 3, 'IAB1', 'IAB2']; + let request = spec.buildRequests(multipleBidRequests); + let data = JSON.parse(request.data); + expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2']); + }); + + it('bcat: pass strings with length greater than 3', function() { + multipleBidRequests[0].params.bcat = ['AB', 'CD', 'IAB1', 'IAB2']; + let request = spec.buildRequests(multipleBidRequests); + let data = JSON.parse(request.data); + expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2']); + }); + + it('bcat: trim the strings', function() { + multipleBidRequests[0].params.bcat = [' IAB1 ', ' IAB2 ']; + let request = spec.buildRequests(multipleBidRequests); + let data = JSON.parse(request.data); + expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2']); + }); + + it('bcat: pass only unique strings', function() { + // multi slot + multipleBidRequests[0].params.bcat = ['IAB1', 'IAB2', 'IAB1', 'IAB2', 'IAB1', 'IAB2']; + multipleBidRequests[1].params.bcat = ['IAB1', 'IAB2', 'IAB1', 'IAB2', 'IAB1', 'IAB3']; + let request = spec.buildRequests(multipleBidRequests); + let data = JSON.parse(request.data); + expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2', 'IAB3']); + }); + + it('bcat: do not pass bcat if all entries are invalid', function() { + // multi slot + multipleBidRequests[0].params.bcat = ['', 'IAB', 'IAB']; + multipleBidRequests[1].params.bcat = [' ', 22, 99999, 'IA']; + let request = spec.buildRequests(multipleBidRequests); + let data = JSON.parse(request.data); + expect(data.bcat).to.deep.equal(undefined); + }); + }); + + describe('Response checking', function () { + it('should check for valid response values', function () { let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); let response = spec.interpretResponse(bidResponses, request); expect(response).to.be.an('array').with.length.above(0); expect(response[0].requestId).to.equal(bidResponses.body.seatbid[0].bid[0].impid); @@ -223,7 +2216,10 @@ describe('PubMatic adapter', () => { expect(response[0].currency).to.equal('USD'); expect(response[0].netRevenue).to.equal(false); expect(response[0].ttl).to.equal(300); - expect(response[0].referrer).to.include(utils.getTopWindowUrl()); + expect(response[0].meta.networkId).to.equal(123); + expect(response[0].meta.buyerId).to.equal(976); + expect(response[0].meta.clickUrl).to.equal('blackrock.com'); + expect(response[0].referrer).to.include(data.site.ref); expect(response[0].ad).to.equal(bidResponses.body.seatbid[0].bid[0].adm); expect(response[1].requestId).to.equal(bidResponses.body.seatbid[1].bid[0].impid); @@ -239,11 +2235,14 @@ describe('PubMatic adapter', () => { expect(response[1].currency).to.equal('USD'); expect(response[1].netRevenue).to.equal(false); expect(response[1].ttl).to.equal(300); - expect(response[1].referrer).to.include(utils.getTopWindowUrl()); + expect(response[1].meta.networkId).to.equal(422); + expect(response[1].meta.buyerId).to.equal(832); + expect(response[1].meta.clickUrl).to.equal('hivehome.com'); + expect(response[1].referrer).to.include(data.site.ref); expect(response[1].ad).to.equal(bidResponses.body.seatbid[1].bid[0].adm); }); - it('should check for dealChannel value selection', () => { + it('should check for dealChannel value selection', function () { let request = spec.buildRequests(bidRequests); let response = spec.interpretResponse(bidResponses, request); expect(response).to.be.an('array').with.length.above(0); @@ -251,7 +2250,7 @@ describe('PubMatic adapter', () => { expect(response[1].dealChannel).to.equal('PREF'); }); - it('should check for unexpected dealChannel value selection', () => { + it('should check for unexpected dealChannel value selection', function () { let request = spec.buildRequests(bidRequests); let updateBiResponse = bidResponses; updateBiResponse.body.seatbid[0].bid[0].ext.deal_channel = 11; @@ -261,6 +2260,45 @@ describe('PubMatic adapter', () => { expect(response).to.be.an('array').with.length.above(0); expect(response[0].dealChannel).to.equal(null); }); + + it('should have a valid native bid response', function() { + let request = spec.buildRequests(nativeBidRequests); + let data = JSON.parse(request.data); + data.imp[0].id = '2a5571261281d4'; + request.data = JSON.stringify(data); + let response = spec.interpretResponse(nativeBidResponse, request); + expect(response).to.be.an('array').with.length.above(0); + expect(response[0].native).to.exist.and.to.be.an('object'); + expect(response[0].mediaType).to.exist.and.to.equal('native'); + expect(response[0].native.title).to.exist.and.to.be.an('string'); + expect(response[0].native.image).to.exist.and.to.be.an('object'); + expect(response[0].native.image.url).to.exist.and.to.be.an('string'); + expect(response[0].native.image.height).to.exist; + expect(response[0].native.image.width).to.exist; + expect(response[0].native.sponsoredBy).to.exist.and.to.be.an('string'); + expect(response[0].native.clickUrl).to.exist.and.to.be.an('string'); + }); + + it('should check for valid banner mediaType in case of multiformat request', function() { + let request = spec.buildRequests(bidRequests); + let response = spec.interpretResponse(bannerBidResponse, request); + + expect(response[0].mediaType).to.equal('banner'); + }); + + it('should check for valid video mediaType in case of multiformat request', function() { + let request = spec.buildRequests(videoBidRequests); + let response = spec.interpretResponse(videoBidResponse, request); + + expect(response[0].mediaType).to.equal('video'); + }); + + it('should check for valid native mediaType in case of multiformat request', function() { + let request = spec.buildRequests(nativeBidRequests); + let response = spec.interpretResponse(nativeBidResponse, request); + + expect(response[0].mediaType).to.equal('native'); + }); }); }); }); diff --git a/test/spec/modules/pubnxBidAdapter_spec.js b/test/spec/modules/pubnxBidAdapter_spec.js new file mode 100644 index 00000000000..002922fa066 --- /dev/null +++ b/test/spec/modules/pubnxBidAdapter_spec.js @@ -0,0 +1,112 @@ +import { expect } from 'chai'; +import { spec } from 'modules/pubnxBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const BASE_URI = '//hb.pubnxserv.com/vzhbidder/bid?'; + +describe('PubNXAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'pubnx', + 'params': { + 'placementId': 'PNX-HB-G396432V4809F3' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'pubnx', + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal(BASE_URI); + expect(request.method).to.equal('POST'); + }); + }); + + describe('interpretResponse', function () { + let response = { + 'vzhPlacementId': 'PNX-HB-G396432V4809F3', + 'bid': '76021e56-adaf-4114-b68d-ccacd1b3e551_1', + 'adWidth': '300', + 'adHeight': '250', + 'cpm': '0.16312590000000002', + 'ad': '', + 'slotBidId': '44b3fcfd24aa93', + 'nurl': '', + 'statusText': 'Success' + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '44b3fcfd24aa93', + 'cpm': 0.16312590000000002, + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'mediaType': 'banner', + 'currency': 'USD', + 'dealId': null, + 'creativeId': null, + 'ttl': 300, + 'ad': '' + } + ]; + let bidderRequest; + let result = spec.interpretResponse({body: response}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].cpm).to.not.equal(null); + }); + + it('handles nobid responses', function () { + let response = { + 'vzhPlacementId': 'PNX-HB-G396432V4809F3', + 'slotBidId': 'f00412ac86b79', + 'statusText': 'NO_BIDS' + }; + let bidderRequest; + + let result = spec.interpretResponse({body: response}); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js index ffb8d3c0570..b77788fff4a 100644 --- a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js @@ -1,38 +1,38 @@ import pubwiseAnalytics from 'modules/pubwiseAnalyticsAdapter'; let events = require('src/events'); -let adaptermanager = require('src/adaptermanager'); +let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); describe('PubWise Prebid Analytics', function () { let xhr; - before(() => { + before(function () { xhr = sinon.useFakeXMLHttpRequest(); }); - after(() => { + after(function () { xhr.restore(); pubwiseAnalytics.disableAnalytics(); }); describe('enableAnalytics', function () { - beforeEach(() => { + beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); }); - afterEach(() => { + afterEach(function () { events.getEvents.restore(); }); it('should catch all events', function () { sinon.spy(pubwiseAnalytics, 'track'); - adaptermanager.registerAnalyticsAdapter({ + adapterManager.registerAnalyticsAdapter({ code: 'pubwiseanalytics', adapter: pubwiseAnalytics }); - adaptermanager.enableAnalytics({ + adapterManager.enableAnalytics({ provider: 'pubwiseanalytics', options: { site: ['test-test-test-test'] diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 709dbeb76a2..1d22ca6eadc 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -1,10 +1,9 @@ /* eslint dot-notation:0, quote-props:0 */ import {expect} from 'chai'; import {spec} from 'modules/pulsepointBidAdapter'; -import {getTopWindowLocation} from 'src/utils'; -import {newBidder} from 'src/adapters/bidderFactory'; +import {deepClone, getTopWindowLocation} from 'src/utils'; -describe('PulsePoint Adapter Tests', () => { +describe('PulsePoint Adapter Tests', function () { const slotConfigs = [{ placementCode: '/DfpAccount1/slot1', bidId: 'bid12345', @@ -48,12 +47,103 @@ describe('PulsePoint Adapter Tests', () => { } } }]; + const videoSlotConfig = [{ + placementCode: '/DfpAccount1/slotVideo', + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + video: { + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + startdelay: 0, + skip: 1, + minbitrate: 200, + protocols: [1, 2, 4] + } + } + }]; + const additionalParamsConfig = [{ + placementCode: '/DfpAccount1/slot1', + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '1x1', + extra_key1: 'extra_val1', + extra_key2: 12345, + extra_key3: { + key1: 'val1', + key2: 23456, + }, + extra_key4: [1, 2, 3] + } + }]; - it('Verify build request', () => { + const ortbParamsSlotConfig = [{ + placementCode: '/DfpAccount1/slot1', + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '1x1', + bcat: ['IAB-1', 'IAB-20'], + battr: [1, 2, 3], + bidfloor: 1.5, + badv: ['cocacola.com', 'lays.com'] + } + }, { + placementCode: '/DfpAccount1/slotVideo', + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + video: { + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + }, + battr: [2, 3, 4], + bidfloor: 2.5, + } + }]; + + const outstreamSlotConfig = [{ + placementCode: '/DfpAccount1/slot1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream' + } + }, + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '1x1', + video: { + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + linearity: 1, + } + }, + renderer: { + options: { + text: 'PulsePoint Outstream' + } + } + }]; + + it('Verify build request', function () { const request = spec.buildRequests(slotConfigs); - expect(request.url).to.equal('//bid.contextweb.com/header/ortb'); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); + const ortbRequest = request.data; // site object expect(ortbRequest.site).to.not.equal(null); expect(ortbRequest.site.publisher).to.not.equal(null); @@ -76,15 +166,16 @@ describe('PulsePoint Adapter Tests', () => { expect(ortbRequest.imp[1].banner.h).to.equal(90); }); - it('Verify parse response', () => { + it('Verify parse response', function () { const request = spec.buildRequests(slotConfigs); - const ortbRequest = JSON.parse(request.data); + const ortbRequest = request.data; const ortbResponse = { seatbid: [{ bid: [{ impid: ortbRequest.imp[0].id, price: 1.25, - adm: 'This is an Ad' + adm: 'This is an Ad', + crid: 'Creative#123' }] }] }; @@ -97,16 +188,16 @@ describe('PulsePoint Adapter Tests', () => { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.adId).to.equal('bid12345'); - expect(bid.creative_id).to.equal('bid12345'); - expect(bid.creativeId).to.equal('bid12345'); + expect(bid.creative_id).to.equal('Creative#123'); + expect(bid.creativeId).to.equal('Creative#123'); expect(bid.netRevenue).to.equal(true); expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(20); }); - it('Verify use ttl in ext', () => { + it('Verify use ttl in ext', function () { const request = spec.buildRequests(slotConfigs); - const ortbRequest = JSON.parse(request.data); + const ortbRequest = request.data; const ortbResponse = { seatbid: [{ bid: [{ @@ -130,17 +221,17 @@ describe('PulsePoint Adapter Tests', () => { expect(bid.currency).to.equal('INR'); }); - it('Verify full passback', () => { + it('Verify full passback', function () { const request = spec.buildRequests(slotConfigs); const bids = spec.interpretResponse({ body: null }, request) expect(bids).to.have.lengthOf(0); }); - it('Verify Native request', () => { + it('Verify Native request', function () { const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('//bid.contextweb.com/header/ortb'); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); + const ortbRequest = request.data; // native impression expect(ortbRequest.imp[0].tagid).to.equal('t10000'); expect(ortbRequest.imp[0].banner).to.equal(null); @@ -174,11 +265,11 @@ describe('PulsePoint Adapter Tests', () => { expect(nativeRequest.assets[2].img.type).to.equal(3); }); - it('Verify Native response', () => { + it('Verify Native response', function () { const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('//bid.contextweb.com/header/ortb'); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); + const ortbRequest = request.data; const nativeResponse = { 'native': { assets: [ @@ -217,22 +308,24 @@ describe('PulsePoint Adapter Tests', () => { expect(nativeBid.impressionTrackers[1]).to.equal('http://imp1.contextweb.com/'); }); - it('Verifies bidder code', () => { + it('Verifies bidder code', function () { expect(spec.code).to.equal('pulsepoint'); }); - it('Verifies bidder aliases', () => { + it('Verifies bidder aliases', function () { expect(spec.aliases).to.have.lengthOf(2); expect(spec.aliases[0]).to.equal('pulseLite'); expect(spec.aliases[1]).to.equal('pulsepointLite'); }); - it('Verifies supported media types', () => { - expect(spec.supportedMediaTypes).to.have.lengthOf(2); + it('Verifies supported media types', function () { + expect(spec.supportedMediaTypes).to.have.lengthOf(3); + expect(spec.supportedMediaTypes[0]).to.equal('banner'); expect(spec.supportedMediaTypes[1]).to.equal('native'); + expect(spec.supportedMediaTypes[2]).to.equal('video'); }); - it('Verifies if bid request valid', () => { + it('Verifies if bid request valid', function () { expect(spec.isBidRequestValid(slotConfigs[0])).to.equal(true); expect(spec.isBidRequestValid(slotConfigs[1])).to.equal(true); expect(spec.isBidRequestValid(nativeSlotConfig[0])).to.equal(true); @@ -243,27 +336,27 @@ describe('PulsePoint Adapter Tests', () => { expect(spec.isBidRequestValid({ params: { ct: 123, cp: 234 } })).to.equal(true); }); - it('Verifies sync options', () => { + it('Verifies sync options', function () { expect(spec.getUserSyncs({})).to.be.undefined; expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; const options = spec.getUserSyncs({ iframeEnabled: true }); expect(options).to.not.be.undefined; expect(options).to.have.lengthOf(1); expect(options[0].type).to.equal('iframe'); - expect(options[0].url).to.equal('//bh.contextweb.com/visitormatch'); + expect(options[0].url).to.equal('https://bh.contextweb.com/visitormatch'); }); - it('Verifies image pixel sync', () => { + it('Verifies image pixel sync', function () { const options = spec.getUserSyncs({ pixelEnabled: true }); expect(options).to.not.be.undefined; expect(options).to.have.lengthOf(1); expect(options[0].type).to.equal('image'); - expect(options[0].url).to.equal('//bh.contextweb.com/visitormatch/prebid'); + expect(options[0].url).to.equal('https://bh.contextweb.com/visitormatch/prebid'); }); - it('Verify app requests', () => { + it('Verify app requests', function () { const request = spec.buildRequests(appSlotConfig); - const ortbRequest = JSON.parse(request.data); + const ortbRequest = request.data; // site object expect(ortbRequest.site).to.equal(null); expect(ortbRequest.app).to.not.be.null; @@ -274,7 +367,7 @@ describe('PulsePoint Adapter Tests', () => { expect(ortbRequest.app.domain).to.equal('pulsepoint.com'); }); - it('Verify GDPR', () => { + it('Verify GDPR', function () { const bidderRequest = { gdprConsent: { gdprApplies: true, @@ -282,9 +375,9 @@ describe('PulsePoint Adapter Tests', () => { } }; const request = spec.buildRequests(slotConfigs, bidderRequest); - expect(request.url).to.equal('//bid.contextweb.com/header/ortb'); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); + const ortbRequest = request.data; // user object expect(ortbRequest.user).to.not.equal(null); expect(ortbRequest.user.ext).to.not.equal(null); @@ -294,4 +387,157 @@ describe('PulsePoint Adapter Tests', () => { expect(ortbRequest.regs.ext).to.not.equal(null); expect(ortbRequest.regs.ext.gdpr).to.equal(1); }); + + it('Verify Video request', function () { + const request = spec.buildRequests(videoSlotConfig); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.be.null; + expect(ortbRequest.imp[0].native).to.be.null; + expect(ortbRequest.imp[0].banner).to.be.null; + expect(ortbRequest.imp[0].video.w).to.equal(400); + expect(ortbRequest.imp[0].video.h).to.equal(300); + expect(ortbRequest.imp[0].video.minduration).to.equal(5); + expect(ortbRequest.imp[0].video.maxduration).to.equal(10); + expect(ortbRequest.imp[0].video.startdelay).to.equal(0); + expect(ortbRequest.imp[0].video.skip).to.equal(1); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); + + it('Verify Video response', function () { + const request = spec.buildRequests(videoSlotConfig); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'http://pulsepoint.video.mp4' + }] + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.adId).to.equal('bid12345'); + expect(bid.ad).to.be.undefined; + expect(bid['native']).to.be.undefined; + expect(bid.mediaType).to.equal('video'); + expect(bid.vastXml).to.equal(ortbResponse.seatbid[0].bid[0].adm); + }); + + it('Verify extra parameters', function () { + let request = spec.buildRequests(additionalParamsConfig); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].ext).to.not.equal(null); + expect(ortbRequest.imp[0].ext.prebid).to.not.equal(null); + expect(ortbRequest.imp[0].ext.prebid).to.not.be.null; + expect(ortbRequest.imp[0].ext.prebid.extra_key1).to.equal('extra_val1'); + expect(ortbRequest.imp[0].ext.prebid.extra_key2).to.equal(12345); + expect(ortbRequest.imp[0].ext.prebid.extra_key3).to.not.be.null; + expect(ortbRequest.imp[0].ext.prebid.extra_key3.key1).to.equal('val1'); + expect(ortbRequest.imp[0].ext.prebid.extra_key3.key2).to.equal(23456); + expect(ortbRequest.imp[0].ext.prebid.extra_key4).to.eql([1, 2, 3]); + expect(Object.keys(ortbRequest.imp[0].ext.prebid)).to.eql(['extra_key1', 'extra_key2', 'extra_key3', 'extra_key4']); + // attempting with a configuration with no unknown params. + request = spec.buildRequests(outstreamSlotConfig); + ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].ext).to.equal(null); + }); + + it('Verify ortb parameters', function () { + const request = spec.buildRequests(ortbParamsSlotConfig); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.bcat).to.eql(['IAB-1', 'IAB-20']); + expect(ortbRequest.badv).to.eql(['cocacola.com', 'lays.com']); + expect(ortbRequest.imp).to.have.lengthOf(2); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.5); + expect(ortbRequest.imp[0].banner.battr).to.eql([1, 2, 3]); + expect(ortbRequest.imp[0].ext).to.be.null; + // slot 2 + expect(ortbRequest.imp[1].bidfloor).to.equal(2.5); + expect(ortbRequest.imp[1].video.battr).to.eql([2, 3, 4]); + expect(ortbRequest.imp[1].ext).to.be.null; + }); + + it('Verify outstream renderer', function () { + const request = spec.buildRequests(outstreamSlotConfig, {bids: [outstreamSlotConfig[0]]}); + const ortbRequest = request.data; + expect(ortbRequest).to.not.be.null; + expect(ortbRequest.imp[0]).to.not.be.null; + expect(ortbRequest.imp[0].video).to.not.be.null; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'http://pulsepoint.video.mp4', + ext: { + outstream: { + type: 'Inline', + config: { + text: 'ADVERTISEMENT', + skipaftersec: 5 + }, + rendererUrl: 'http://tag.contextweb.com/hb-outstr-renderer.js' + } + } + }] + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.renderer).to.not.be.null; + expect(bid.renderer.url).to.equal('http://tag.contextweb.com/hb-outstr-renderer.js'); + expect(bid.renderer.getConfig()).to.not.be.null; + expect(bid.renderer.getConfig().defaultOptions).to.eql(ortbResponse.seatbid[0].bid[0].ext.outstream.config); + expect(bid.renderer.getConfig().rendererOptions).to.eql(outstreamSlotConfig[0].renderer.options); + expect(bid.renderer.getConfig().type).to.equal('Inline'); + }); + it('Verify common id parameters', function () { + const bidRequests = deepClone(slotConfigs); + bidRequests[0].userId = { + pubcid: 'userid_pubcid', + tdid: 'userid_ttd', + digitrustid: { + data: { + id: 'userid_digitrust', + keyv: 4, + privacy: {optout: false}, + producer: 'ABC', + version: 2 + } + } + }; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.not.null; + const ortbRequest = request.data; + expect(request.data).to.be.not.null; + // user object + expect(ortbRequest.user).to.not.be.undefined; + expect(ortbRequest.user.ext).to.not.be.undefined; + expect(ortbRequest.user.ext.eids).to.not.be.undefined; + expect(ortbRequest.user.ext.eids).to.have.lengthOf(3); + expect(ortbRequest.user.ext.eids[0].source).to.equal('pubcommon'); + expect(ortbRequest.user.ext.eids[0].uids).to.have.lengthOf(1); + expect(ortbRequest.user.ext.eids[0].uids[0].id).to.equal('userid_pubcid'); + expect(ortbRequest.user.ext.eids[1].source).to.equal('ttdid'); + expect(ortbRequest.user.ext.eids[1].uids).to.have.lengthOf(1); + expect(ortbRequest.user.ext.eids[1].uids[0].id).to.equal('userid_ttd'); + expect(ortbRequest.user.ext.eids[2].source).to.equal('digitrust'); + expect(ortbRequest.user.ext.eids[2].uids).to.have.lengthOf(1); + expect(ortbRequest.user.ext.eids[2].uids[0].id).to.equal('userid_digitrust'); + }); }); diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index 6fd5c3abdf9..b553cf5d37e 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -1,19 +1,23 @@ -import * as utils from 'src/utils'; import { expect } from 'chai'; import { - QUANTCAST_CALLBACK_URL_TEST, - QUANTCAST_CALLBACK_URL, + QUANTCAST_DOMAIN, + QUANTCAST_TEST_DOMAIN, QUANTCAST_NET_REVENUE, QUANTCAST_TTL, + QUANTCAST_TEST_PUBLISHER, + QUANTCAST_PROTOCOL, + QUANTCAST_PORT, spec as qcSpec } from '../../../modules/quantcastBidAdapter'; import { newBidder } from '../../../src/adapters/bidderFactory'; +import { parse } from 'src/url'; -describe('Quantcast adapter', () => { +describe('Quantcast adapter', function () { const quantcastAdapter = newBidder(qcSpec); let bidRequest; + let bidderRequest; - beforeEach(() => { + beforeEach(function () { bidRequest = { bidder: 'quantcast', bidId: '2f7b179d443f14', @@ -21,98 +25,105 @@ describe('Quantcast adapter', () => { bidderRequestId: '1cc026909c24c8', placementCode: 'div-gpt-ad-1438287399331-0', params: { - publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast + publisherId: QUANTCAST_TEST_PUBLISHER, // REQUIRED - Publisher ID provided by Quantcast battr: [1, 2] // OPTIONAL - Array of blocked creative attributes as per OpenRTB Spec List 5.3 }, sizes: [[300, 250]] }; + + bidderRequest = { + refererInfo: { + referer: 'http://example.com/hello.html', + canonicalUrl: 'http://example.com/hello.html' + } + }; }); - describe('inherited functions', () => { - it('exists and is a function', () => { + function setupVideoBidRequest(videoParams) { + bidRequest.params = { + publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast + // Video object as specified in OpenRTB 2.5 + video: videoParams + }; + bidRequest['mediaTypes'] = { + video: { + context: 'instream', + playerSize: [600, 300] + } + } + }; + + describe('inherited functions', function () { + it('exists and is a function', function () { expect(quantcastAdapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('`isBidRequestValid`', () => { - it('should return `false` when bid is not passed', () => { - expect(qcSpec.isBidRequestValid()).to.equal(false); - }); - - it('should return `false` when bid `mediaType` is `video`', () => { - const bidRequest = { mediaType: 'video' }; + describe('`isBidRequestValid`', function () { + it('should return `true` when bid has publisherId', function () { + const bidRequest = { + bidder: 'quantcast', + params: { + publisherId: 'my_publisher_id' + } + }; - expect(qcSpec.isBidRequestValid(bidRequest)).to.equal(false); + expect(qcSpec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return `true` when bid contains required params', () => { - const bidRequest = { mediaType: 'banner' }; + it('should return `false` when bid has no publisherId', function () { + const bidRequest = { + bidder: 'quantcast', + params: { + } + }; - expect(qcSpec.isBidRequestValid(bidRequest)).to.equal(true); + expect(qcSpec.isBidRequestValid(bidRequest)).to.equal(false); }); }); - describe('`buildRequests`', () => { - it('sends bid requests to Quantcast Canary Endpoint if `publisherId` is `test-publisher`', () => { - const requests = qcSpec.buildRequests([bidRequest]); - + describe('`buildRequests`', function () { + it('selects protocol and port', function () { switch (window.location.protocol) { case 'https:': - expect(requests[0]['url']).to.equal( - `https://${QUANTCAST_CALLBACK_URL_TEST}:8443/qchb` - ); + expect(QUANTCAST_PROTOCOL).to.equal('https'); + expect(QUANTCAST_PORT).to.equal('8443'); break; default: - expect(requests[0]['url']).to.equal( - `http://${QUANTCAST_CALLBACK_URL_TEST}:8080/qchb` - ); + expect(QUANTCAST_PROTOCOL).to.equal('http'); + expect(QUANTCAST_PORT).to.equal('8080'); break; } }); - it('sends bid requests to Quantcast Global Endpoint for regular `publisherId`', () => { - const bidRequest = { - bidder: 'quantcast', - bidId: '2f7b179d443f14', - auctionId: '595ffa73-d78a-46c9-b18e-f99548a5be6b', - bidderRequestId: '1cc026909c24c8', - placementCode: 'div-gpt-ad-1438287399331-0', - params: { - publisherId: 'regular-publisher', // REQUIRED - Publisher ID provided by Quantcast - battr: [1, 2] // OPTIONAL - Array of blocked creative attributes as per OpenRTB Spec List 5.3 - }, - sizes: [[300, 250]] - }; + it('sends bid requests to Quantcast Canary Endpoint if `publisherId` is `test-publisher`', function () { const requests = qcSpec.buildRequests([bidRequest]); + const url = parse(requests[0]['url']); + expect(url.hostname).to.equal(QUANTCAST_TEST_DOMAIN); + }); - switch (window.location.protocol) { - case 'https:': - expect(requests[0]['url']).to.equal( - `https://${QUANTCAST_CALLBACK_URL}:8443/qchb` - ); - break; - default: - expect(requests[0]['url']).to.equal( - `http://${QUANTCAST_CALLBACK_URL}:8080/qchb` - ); - break; - } + it('sends bid requests to default endpoint for non standard publisher IDs', function () { + const modifiedBidRequest = Object.assign({}, bidRequest, { + params: Object.assign({}, bidRequest.params, { + publisherId: 'foo-bar', + }), + }); + const requests = qcSpec.buildRequests([modifiedBidRequest]); + expect(requests[0]['url']).to.equal( + `${QUANTCAST_PROTOCOL}://${QUANTCAST_DOMAIN}:${QUANTCAST_PORT}/qchb` + ); }); - it('sends bid requests to Quantcast Header Bidding Endpoints via POST', () => { + it('sends bid requests to Quantcast Header Bidding Endpoints via POST', function () { const requests = qcSpec.buildRequests([bidRequest]); expect(requests[0].method).to.equal('POST'); }); - it('sends bid requests contains all the required parameters', () => { - const referrer = utils.getTopWindowUrl(); - const loc = utils.getTopWindowLocation(); - const domain = loc.hostname; - - const requests = qcSpec.buildRequests([bidRequest]); - const expectedBidRequest = { - publisherId: 'test-publisher', + it('sends banner bid requests contains all the required parameters', function () { + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + const expectedBannerBidRequest = { + publisherId: QUANTCAST_TEST_PUBLISHER, requestId: '2f7b179d443f14', imp: [ { @@ -125,18 +136,210 @@ describe('Quantcast adapter', () => { } ], site: { - page: loc.href, - referrer, - domain + page: 'http://example.com/hello.html', + referrer: 'http://example.com/hello.html', + domain: 'example.com' + }, + bidId: '2f7b179d443f14', + gdprSignal: 0, + prebidJsVersion: '$prebid.version$' + }; + + expect(requests[0].data).to.equal(JSON.stringify(expectedBannerBidRequest)); + }); + + it('sends video bid requests containing all the required parameters', function () { + setupVideoBidRequest({ + mimes: ['video/mp4'], // required + minduration: 3, // optional + maxduration: 5, // optional + protocols: [3], // optional + startdelay: 1, // optional + linearity: 1, // optinal + battr: [1, 2], // optional + maxbitrate: 10, // optional + playbackmethod: [1], // optional + delivery: [1], // optional + placement: 1, // optional + api: [2, 3] // optional + }); + + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + const expectedVideoBidRequest = { + publisherId: QUANTCAST_TEST_PUBLISHER, + requestId: '2f7b179d443f14', + imp: [ + { + video: { + mimes: ['video/mp4'], + minduration: 3, + maxduration: 5, + protocols: [3], + startdelay: 1, + linearity: 1, + battr: [1, 2], + maxbitrate: 10, + playbackmethod: [1], + delivery: [1], + placement: 1, + api: [2, 3], + w: 600, + h: 300 + }, + placementCode: 'div-gpt-ad-1438287399331-0', + bidFloor: 1e-10 + } + ], + site: { + page: 'http://example.com/hello.html', + referrer: 'http://example.com/hello.html', + domain: 'example.com' + }, + bidId: '2f7b179d443f14', + gdprSignal: 0, + prebidJsVersion: '$prebid.version$' + }; + + expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); + }); + + it('overrides video parameters with parameters from adunit', function() { + setupVideoBidRequest({ + mimes: ['video/mp4'] + }); + bidRequest.mediaTypes.video.mimes = ['video/webm']; + + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + const expectedVideoBidRequest = { + publisherId: QUANTCAST_TEST_PUBLISHER, + requestId: '2f7b179d443f14', + imp: [ + { + video: { + mimes: ['video/webm'], + w: 600, + h: 300 + }, + placementCode: 'div-gpt-ad-1438287399331-0', + bidFloor: 1e-10 + } + ], + site: { + page: 'http://example.com/hello.html', + referrer: 'http://example.com/hello.html', + domain: 'example.com' + }, + bidId: '2f7b179d443f14', + gdprSignal: 0, + prebidJsVersion: '$prebid.version$' + }; + + expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); + }); + + it('sends video bid request when no video parameters are given', function () { + setupVideoBidRequest(null); + + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + const expectedVideoBidRequest = { + publisherId: QUANTCAST_TEST_PUBLISHER, + requestId: '2f7b179d443f14', + imp: [ + { + video: { + w: 600, + h: 300 + }, + placementCode: 'div-gpt-ad-1438287399331-0', + bidFloor: 1e-10 + } + ], + site: { + page: 'http://example.com/hello.html', + referrer: 'http://example.com/hello.html', + domain: 'example.com' + }, + bidId: '2f7b179d443f14', + gdprSignal: 0, + prebidJsVersion: '$prebid.version$' + }; + + expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); + }); + + it('ignores unsupported video bid requests', function () { + bidRequest.mediaTypes = { + video: { + context: 'outstream', + playerSize: [[550, 310]] + } + }; + + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + + expect(requests).to.be.empty; + }); + + it('parses multi-format bid request', function () { + bidRequest.mediaTypes = { + banner: {sizes: [[300, 250], [728, 90], [250, 250], [468, 60], [320, 50]]}, + native: { + image: {required: true, sizes: [150, 50]}, + title: {required: true, len: 80}, + sponsoredBy: {required: true}, + clickUrl: {required: true}, + privacyLink: {required: false}, + body: {required: true}, + icon: {required: true, sizes: [50, 50]} + }, + video: { + context: 'outstream', + playerSize: [[550, 310]] + } + }; + bidRequest.sizes = [[300, 250], [728, 90], [250, 250], [468, 60], [320, 50]]; + + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + const expectedBidRequest = { + publisherId: QUANTCAST_TEST_PUBLISHER, + requestId: '2f7b179d443f14', + imp: [{ + banner: { + battr: [1, 2], + sizes: [ + {width: 300, height: 250}, + {width: 728, height: 90}, + {width: 250, height: 250}, + {width: 468, height: 60}, + {width: 320, height: 50} + ] + }, + placementCode: 'div-gpt-ad-1438287399331-0', + bidFloor: 1e-10 + }], + site: { + page: 'http://example.com/hello.html', + referrer: 'http://example.com/hello.html', + domain: 'example.com' }, - bidId: '2f7b179d443f14' + bidId: '2f7b179d443f14', + gdprSignal: 0, + prebidJsVersion: '$prebid.version$' }; expect(requests[0].data).to.equal(JSON.stringify(expectedBidRequest)); }); }); - describe('`interpretResponse`', () => { + it('propagates GDPR consent string and signal', function () { + const gdprConsent = { gdprApplies: true, consentString: 'consentString' } + const requests = qcSpec.buildRequests([bidRequest], { gdprConsent }); + const parsed = JSON.parse(requests[0].data) + expect(parsed.gdprSignal).to.equal(1); + expect(parsed.gdprConsent).to.equal(gdprConsent.consentString); + }); + + describe('`interpretResponse`', function () { // The sample response is from https://wiki.corp.qc/display/adinf/QCX const body = { bidderCode: 'qcx', // Renaming it to use CamelCase since that is what is used in the Prebid.js variable name @@ -161,25 +364,46 @@ describe('Quantcast adapter', () => { headers: {} }; - it('should return an empty array if `serverResponse` is `undefined`', () => { + const videoBody = { + bidderCode: 'qcx', + requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', + bids: [ + { + statusCode: 1, + placementCode: 'video1', + cpm: 4.5, + currency: 'USD', + videoUrl: 'https://vast.quantserve.com/vast?p=&r=&gdpr=&gdpr_consent=&rand=1337&d=H4sIAAAAAAAAAONi4mIQcrzFqGLi5OzibOzmpGtm4eyia-LoaqDraGRupOtobGJhYuni6GRiYLmLiYWrp5f_BBPDDybGScxcPs7-aRYmpmVVoVJgCSXBkozMYl0gKslI1S1Izk9JBQALkFy_YAAAAA&h=uRnsTjyXbOrXJtBQiaMn239i9GI', + width: 600, + height: 300 + } + ] + }; + + const videoResponse = { + body: videoBody, + headers: {} + }; + + it('should return an empty array if `serverResponse` is `undefined`', function () { const interpretedResponse = qcSpec.interpretResponse(); expect(interpretedResponse.length).to.equal(0); }); - it('should return an empty array if the parsed response does NOT include `bids`', () => { + it('should return an empty array if the parsed response does NOT include `bids`', function () { const interpretedResponse = qcSpec.interpretResponse({}); expect(interpretedResponse.length).to.equal(0); }); - it('should return an empty array if the parsed response has an empty `bids`', () => { + it('should return an empty array if the parsed response has an empty `bids`', function () { const interpretedResponse = qcSpec.interpretResponse({ bids: [] }); expect(interpretedResponse.length).to.equal(0); }); - it('should get correct bid response', () => { + it('should get correct bid response', function () { const expectedResponse = { requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', cpm: 4.5, @@ -197,7 +421,46 @@ describe('Quantcast adapter', () => { expect(interpretedResponse[0]).to.deep.equal(expectedResponse); }); - it('handles no bid response', () => { + it('should include dealId in bid response', function () { + response.body.bids[0].dealId = 'test-dealid'; + const expectedResponse = { + requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', + cpm: 4.5, + width: 300, + height: 250, + ad: + '
    Quantcast
    ', + ttl: QUANTCAST_TTL, + creativeId: 1001, + netRevenue: QUANTCAST_NET_REVENUE, + currency: 'USD', + dealId: 'test-dealid' + }; + const interpretedResponse = qcSpec.interpretResponse(response); + + expect(interpretedResponse[0]).to.deep.equal(expectedResponse); + }); + + it('should get correct bid response for instream video', function() { + const expectedResponse = { + requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', + cpm: 4.5, + width: 600, + height: 300, + vastUrl: 'https://vast.quantserve.com/vast?p=&r=&gdpr=&gdpr_consent=&rand=1337&d=H4sIAAAAAAAAAONi4mIQcrzFqGLi5OzibOzmpGtm4eyia-LoaqDraGRupOtobGJhYuni6GRiYLmLiYWrp5f_BBPDDybGScxcPs7-aRYmpmVVoVJgCSXBkozMYl0gKslI1S1Izk9JBQALkFy_YAAAAA&h=uRnsTjyXbOrXJtBQiaMn239i9GI', + mediaType: 'video', + ttl: QUANTCAST_TTL, + creativeId: undefined, + ad: undefined, + netRevenue: QUANTCAST_NET_REVENUE, + currency: 'USD' + }; + const interpretedResponse = qcSpec.interpretResponse(videoResponse); + + expect(interpretedResponse[0]).to.deep.equal(expectedResponse); + }); + + it('handles no bid response', function () { const body = { bidderCode: 'qcx', // Renaming it to use CamelCase since that is what is used in the Prebid.js variable name requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', // Added this field. This is not used now but could be useful in troubleshooting later on. Specially for sites using iFrames @@ -207,7 +470,6 @@ describe('Quantcast adapter', () => { body, headers: {} }; - const expectedResponse = []; const interpretedResponse = qcSpec.interpretResponse(response); expect(interpretedResponse.length).to.equal(0); diff --git a/test/spec/modules/quantumBidAdapter_spec.js b/test/spec/modules/quantumBidAdapter_spec.js index 053ec98ffaa..d14d24ebfe1 100644 --- a/test/spec/modules/quantumBidAdapter_spec.js +++ b/test/spec/modules/quantumBidAdapter_spec.js @@ -203,44 +203,101 @@ const nativeServerResponse = { ] } -describe('quantumBidAdapter', () => { +describe('quantumBidAdapter', function () { const adapter = newBidder(spec) - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function') }) }) - describe('isBidRequestValid', () => { - it('should return true when required params found', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(REQUEST)).to.equal(true) }) - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, REQUEST) delete bid.params expect(spec.isBidRequestValid(bid)).to.equal(false) }) }) - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [REQUEST] const request = spec.buildRequests(bidRequests, {}) - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { expect(request[0].method).to.equal('GET') }) }) - describe('interpretResponse', () => { + describe('GDPR conformity', function () { + const bidRequests = [{ + 'bidder': 'quantum', + 'mediaType': 'native', + 'params': { + placementId: 21546 + }, + adUnitCode: 'aaa', + transactionId: '2b8389fe-615c-482d-9f1a-376fb8f7d6b0', + sizes: [[0, 0]], + bidId: '1abgs362e0x48a8', + bidderRequestId: '70deaff71c281d', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337' + }]; + + const bidderRequest = { + gdprConsent: { + consentString: 'awefasdfwefasdfasd', + gdprApplies: true + } + }; + + it('should transmit correct data', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests.length).to.equal(1); + expect(requests[0].data.quantx_gdpr).to.equal(1); + expect(requests[0].data.quantx_user_consent_string).to.equal('awefasdfwefasdfasd'); + }); + }); + + describe('GDPR absence conformity', function () { + const bidRequests = [{ + 'bidder': 'quantum', + 'mediaType': 'native', + 'params': { + placementId: 21546 + }, + adUnitCode: 'aaa', + transactionId: '2b8389fe-615c-482d-9f1a-376fb8f7d6b0', + sizes: [[0, 0]], + bidId: '1abgs362e0x48a8', + bidderRequestId: '70deaff71c281d', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337' + }]; + + const bidderRequest = { + gdprConsent: undefined + }; + + it('should transmit correct data', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests.length).to.equal(1); + expect(requests[0].data.quantx_gdpr).to.be.undefined; + expect(requests[0].data.quantx_user_consent_string).to.be.undefined; + }); + }); + + describe('interpretResponse', function () { let bidderRequest = { bidderCode: 'bidderCode', bids: [] } - it('handles native request : should get correct bid response', () => { + it('handles native request : should get correct bid response', function () { const result = spec.interpretResponse({body: nativeServerResponse}, NATIVE_REQUEST) expect(result[0]).to.have.property('cpm').equal(0.3) expect(result[0]).to.have.property('width').to.be.below(2) @@ -249,7 +306,7 @@ describe('quantumBidAdapter', () => { expect(result[0]).to.have.property('native') }) - it('should get correct bid response', () => { + it('should get correct bid response', function () { const result = spec.interpretResponse({body: serverResponse}, REQUEST) expect(result[0]).to.have.property('cpm').equal(0.3) expect(result[0]).to.have.property('width').equal(300) @@ -258,7 +315,7 @@ describe('quantumBidAdapter', () => { expect(result[0]).to.have.property('ad') }) - it('handles nobid responses', () => { + it('handles nobid responses', function () { const nobidServerResponse = {bids: []} const nobidResult = spec.interpretResponse({body: nobidServerResponse}, bidderRequest) // console.log(nobidResult) diff --git a/test/spec/modules/radsBidAdapter_spec.js b/test/spec/modules/radsBidAdapter_spec.js new file mode 100644 index 00000000000..6981955f261 --- /dev/null +++ b/test/spec/modules/radsBidAdapter_spec.js @@ -0,0 +1,206 @@ +import { expect } from 'chai'; +import { spec } from 'modules/radsBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const RADS_ENDPOINT_URL = 'https://rads.recognified.net/md.request.php'; + +describe('radsAdapter', function () { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'rads', + 'params': { + 'placement': '6682', + 'pfilter': { + 'floorprice': 1000000 + }, + 'bcat': 'IAB2,IAB4', + 'dvt': 'desktop', + 'ip': '1.1.1.1' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'someIncorrectParam': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [{ + 'bidder': 'rads', + 'params': { + 'placement': '6682', + 'pfilter': { + 'floorprice': 1000000, + 'geo': { + 'country': 'DE' + } + }, + 'bcat': 'IAB2,IAB4', + 'dvt': 'desktop', + 'ip': '1.1.1.1' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }, { + 'bidder': 'rads', + 'params': { + 'placement': '6682', + 'pfilter': { + 'floorprice': 1000000, + 'geo': { + 'country': 'DE', + 'region': 'DE-BE' + }, + }, + 'bcat': 'IAB2,IAB4', + 'dvt': 'desktop' + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + + let bidderRequest = { + refererInfo: { + referer: 'some_referrer.net' + } + } + + const request = spec.buildRequests(bidRequests, bidderRequest); + it('sends bid request to our endpoint via GET', function () { + expect(request[0].method).to.equal('GET'); + let data = request[0].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); + expect(data).to.equal('rt=bid-response&_f=prebid_js&_ps=6682&srw=300&srh=250&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&bcat=IAB2%2CIAB4&dvt=desktop&i=1.1.1.1'); + }); + + it('sends bid video request to our rads endpoint via GET', function () { + expect(request[1].method).to.equal('GET'); + let data = request[1].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); + expect(data).to.equal('rt=vast2&_f=prebid_js&_ps=6682&srw=640&srh=480&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgeo%5D%5Bregion%5D=DE-BE&bcat=IAB2%2CIAB4&dvt=desktop'); + }); + }); + + describe('interpretResponse', function () { + let serverBannerResponse = { + 'body': { + 'cpm': 5000000, + 'crid': 100500, + 'width': '300', + 'height': '250', + 'adTag': '', + 'requestId': '220ed41385952a', + 'currency': 'EUR', + 'ttl': 60, + 'netRevenue': true, + 'zone': '6682' + } + }; + let serverVideoResponse = { + 'body': { + 'cpm': 5000000, + 'crid': 100500, + 'width': '300', + 'height': '250', + 'vastXml': '{"reason":7001,"status":"accepted"}', + 'requestId': '220ed41385952a', + 'currency': 'EUR', + 'ttl': 60, + 'netRevenue': true, + 'zone': '6682' + } + }; + + let expectedResponse = [{ + requestId: '23beaa6af6cdde', + cpm: 0.5, + width: 0, + height: 0, + creativeId: 100500, + dealId: '', + currency: 'EUR', + netRevenue: true, + ttl: 300, + ad: '' + }, { + requestId: '23beaa6af6cdde', + cpm: 0.5, + width: 0, + height: 0, + creativeId: 100500, + dealId: '', + currency: 'EUR', + netRevenue: true, + ttl: 300, + vastXml: '{"reason":7001,"status":"accepted"}', + mediaType: 'video' + }]; + + it('should get the correct bid response by display ad', function () { + let bidRequest = [{ + 'method': 'GET', + 'url': RADS_ENDPOINT_URL, + 'refererInfo': { + 'referer': '' + }, + 'data': { + 'bid_id': '30b31c1838de1e' + } + }]; + let result = spec.interpretResponse(serverBannerResponse, bidRequest[0]); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('should get the correct rads video bid response by display ad', function () { + let bidRequest = [{ + 'method': 'GET', + 'url': RADS_ENDPOINT_URL, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + } + }, + 'data': { + 'bid_id': '30b31c1838de1e' + } + }]; + let result = spec.interpretResponse(serverVideoResponse, bidRequest[0]); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[1])); + }); + + it('handles empty bid response', function () { + let response = { + body: {} + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/rdnBidAdapter_spec.js b/test/spec/modules/rdnBidAdapter_spec.js new file mode 100644 index 00000000000..8f53502bc44 --- /dev/null +++ b/test/spec/modules/rdnBidAdapter_spec.js @@ -0,0 +1,156 @@ +import { expect } from 'chai' +import * as utils from 'src/utils' +import { spec } from 'modules/rdnBidAdapter' +import { newBidder } from 'src/adapters/bidderFactory' +import {config} from '../../../src/config'; + +describe('rdnBidAdapter', function() { + const adapter = newBidder(spec); + const ENDPOINT = 'https://s-bid.rmp.rakuten.co.jp/h'; + let sandbox; + + beforeEach(function() { + config.resetConfig(); + }); + + afterEach(function () { + config.resetConfig(); + }); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function') + }) + }); + + describe('isBidRequestValid', () => { + let bid = { + bidder: 'rdn', + params: { + adSpotId: '56789' + } + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true) + }); + + it('should return false when required params are not passed', () => { + bid.params.adSpotId = ''; + expect(spec.isBidRequestValid(bid)).to.equal(false) + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }); + + describe('buildRequests', () => { + const bidRequests = [ + { + // banner + params: { + adSpotId: '58278' + } + } + ]; + + it('sends bid request to ENDPOINT via GET', () => { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('GET') + }) + + it('allows url override', () => { + config.setConfig({ + rdn: { + endpoint: '//test.rakuten.com' + } + }); + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal('//test.rakuten.com'); + }) + }); + + describe('interpretResponse', () => { + const bidRequests = { + banner: { + method: 'GET', + url: '', + data: { + t: '56789', + s: 'https', + ua: + 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Mobile Safari/537.36', + l: 'ja', + d: 'examples.com', + tp: 'https://examples.com/foo/fuga', + pp: 'https://examples.com/hoge/muga' + } + } + }; + + const serverResponse = { + noAd: [], + noAd2: { + requestId: 'biequa9oaph4we' + }, + banner: { + requestId: 'biequa9oaph4we', + cpm: 37.66, + width: 300, + height: 250, + creativeId: 140281, + dealId: 'phoh3pad-ai4ah-xoh7x-ahk7cheasae3oh', + currency: 'JPY', + netRevenue: 300, + ttl: 3000, + referrer: utils.getTopWindowUrl(), + ad: '' + } + }; + + it('handles nobid responses', () => { + const result = spec.interpretResponse( + { body: serverResponse.noAd }, + bidRequests.banner + ); + expect(result.length).to.equal(0); + + const result2 = spec.interpretResponse( + { body: serverResponse.noAd2 }, + bidRequests.banner + ); + expect(result2.length).to.equal(0); + }) + }); + describe('spec.getUserSyncs', function () { + const syncResponse = [{ + body: { + request_id: 'biequa9oaph4we', + sync_urls: ['https://rdn1.test/sync?uid=9876543210', 'https://rdn2.test/sync?uid=9876543210'] + } + }]; + const nosyncResponse = [{ + body: { + request_id: 'biequa9oaph4we', + sync_urls: [] + } + }]; + let syncOptions + beforeEach(function () { + syncOptions = { + pixelEnabled: true + } + }); + it('sucess usersync url', function () { + const result = []; + result.push({type: 'image', url: 'https://rdn1.test/sync?uid=9876543210'}); + result.push({type: 'image', url: 'https://rdn2.test/sync?uid=9876543210'}); + expect(spec.getUserSyncs(syncOptions, syncResponse)).to.deep.equal(result); + }); + }); +}); diff --git a/test/spec/modules/readpeakBidAdapter_spec.js b/test/spec/modules/readpeakBidAdapter_spec.js index 776261c8db2..5fcdf9b5836 100644 --- a/test/spec/modules/readpeakBidAdapter_spec.js +++ b/test/spec/modules/readpeakBidAdapter_spec.js @@ -1,13 +1,14 @@ import { expect } from 'chai'; import { spec, ENDPOINT } from 'modules/readpeakBidAdapter'; import * as utils from 'src/utils'; +import {config} from 'src/config'; -describe('ReadPeakAdapter', () => { +describe('ReadPeakAdapter', function () { let bidRequest let serverResponse let serverRequest - beforeEach(() => { + beforeEach(function () { bidRequest = { bidder: 'readpeak', nativeParams: { @@ -113,42 +114,48 @@ describe('ReadPeakAdapter', () => { } }); - describe('spec.isBidRequestValid', () => { - it('should return true when the required params are passed', () => { + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed', function () { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the native params are missing', () => { + it('should return false when the native params are missing', function () { bidRequest.nativeParams = undefined; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when the "publisherId" param is missing', () => { + it('should return false when the "publisherId" param is missing', function () { bidRequest.params = { bidfloor: 5.00 }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when no bid params are passed', () => { + it('should return false when no bid params are passed', function () { bidRequest.params = {}; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when a bid request is not passed', () => { + it('should return false when a bid request is not passed', function () { expect(spec.isBidRequestValid()).to.equal(false); expect(spec.isBidRequestValid({})).to.equal(false); }); }); - describe('spec.buildRequests', () => { - it('should create a POST request for every bid', () => { + describe('spec.buildRequests', function () { + it('should create a POST request for every bid', function () { const request = spec.buildRequests([ bidRequest ]); expect(request.method).to.equal('POST'); expect(request.url).to.equal(ENDPOINT); }); - it('should attach request data', () => { + it('should attach request data', function () { + config.setConfig({ + currency: { + adServerCurrency: 'EUR' + } + }); + const request = spec.buildRequests([ bidRequest ]); const data = JSON.parse(request.data); @@ -167,17 +174,18 @@ describe('ReadPeakAdapter', () => { page: utils.getTopWindowLocation().href, domain: utils.getTopWindowLocation().hostname, }); - expect(data.device).to.deep.contain({ ua: navigator.userAgent }); + expect(data.device).to.deep.contain({ ua: navigator.userAgent, language: navigator.language }); + expect(data.cur).to.deep.equal(['EUR']); }); }); - describe('spec.interpretResponse', () => { - it('should return no bids if the response is not valid', () => { + describe('spec.interpretResponse', function () { + it('should return no bids if the response is not valid', function () { const bidResponse = spec.interpretResponse({ body: null }, serverRequest); expect(bidResponse.length).to.equal(0); }); - it('should return a valid bid response', () => { + it('should return a valid bid response', function () { const bidResponse = spec.interpretResponse({ body: serverResponse }, serverRequest)[0]; expect(bidResponse).to.contain({ requestId: bidRequest.bidId, diff --git a/test/spec/modules/realvuAnalyticsAdapter_spec.js b/test/spec/modules/realvuAnalyticsAdapter_spec.js index 7bb43002939..359fb329359 100644 --- a/test/spec/modules/realvuAnalyticsAdapter_spec.js +++ b/test/spec/modules/realvuAnalyticsAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import realvuAnalyticsAdapter from 'modules/realvuAnalyticsAdapter'; +import realvuAnalyticsAdapter, { lib } from 'modules/realvuAnalyticsAdapter'; import CONSTANTS from 'src/constants.json'; function addDiv(id) { @@ -22,142 +22,170 @@ function addDiv(id) { return dv; } -describe('RealVu Analytics Adapter.', () => { - before(() => { +describe('RealVu', function() { + let sandbox; + beforeEach(function () { + sandbox = sinon.sandbox.create(); addDiv('ad1'); addDiv('ad2'); + sandbox.stub(lib, 'scr'); }); - after(() => { + + afterEach(function () { let a1 = document.getElementById('ad1'); document.body.removeChild(a1); let a2 = document.getElementById('ad2'); document.body.removeChild(a2); + sandbox.restore(); + realvuAnalyticsAdapter.disableAnalytics(); }); - it('enableAnalytics', () => { - const config = { - options: { - partnerId: '1Y', - regAllUnits: true - // unitIds: ['ad1', 'ad2'] - } - }; - let p = realvuAnalyticsAdapter.enableAnalytics(config); - expect(p).to.equal('1Y'); + after(function () { + delete window.top1; + delete window.realvu_aa_fifo; + delete window.realvu_aa; + clearInterval(window.boost_poll); + delete window.boost_poll; }); - it('checkIn', () => { - const bid = { - adUnitCode: 'ad1', - sizes: [ - [728, 90], - [970, 250], - [970, 90] - ] - }; - let result = realvuAnalyticsAdapter.checkIn(bid, '1Y'); - const b = window.top1.realvu_aa; - let a = b.ads[0]; - // console.log('a: ' + a.x + ', ' + a.y + ', ' + a.w + ', ' + a.h); - // console.log('b: ' + b.x1 + ', ' + b.y1 + ', ' + b.x2 + ', ' + b.y2); - expect(result).to.equal('yes'); - - result = realvuAnalyticsAdapter.checkIn(bid); // test invalid partnerId 'undefined' - result = realvuAnalyticsAdapter.checkIn(bid, ''); // test invalid partnerId '' - }); + describe('Analytics Adapter.', function () { + it('enableAnalytics', function () { + const config = { + options: { + partnerId: '1Y', + regAllUnits: true + // unitIds: ['ad1', 'ad2'] + } + }; + let p = realvuAnalyticsAdapter.enableAnalytics(config); + expect(p).to.equal('1Y'); + }); - it.skip('isInView returns "yes"', () => { - let inview = realvuAnalyticsAdapter.isInView('ad1'); - expect(inview).to.equal('yes'); - }); + it('checkIn', function () { + const bid = { + adUnitCode: 'ad1', + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ] + }; + let result = realvuAnalyticsAdapter.checkIn(bid, '1Y'); + const b = window.top1.realvu_aa; + let a = b.ads[0]; + // console.log('a: ' + a.x + ', ' + a.y + ', ' + a.w + ', ' + a.h); + // console.log('b: ' + b.x1 + ', ' + b.y1 + ', ' + b.x2 + ', ' + b.y2); + expect(result).to.equal('yes'); + + result = realvuAnalyticsAdapter.checkIn(bid); // test invalid partnerId 'undefined' + result = realvuAnalyticsAdapter.checkIn(bid, ''); // test invalid partnerId '' + }); - it('isInView return "NA"', () => { - const adUnitCode = '1234'; - let result = realvuAnalyticsAdapter.isInView(adUnitCode); - expect(result).to.equal('NA'); - }); + it.skip('isInView returns "yes"', () => { + let inview = realvuAnalyticsAdapter.isInView('ad1'); + expect(inview).to.equal('yes'); + }); - it('bid response event', () => { - const config = { - options: { - partnerId: '1Y', - regAllUnits: true - // unitIds: ['ad1', 'ad2'] - } - }; - realvuAnalyticsAdapter.enableAnalytics(config); - const args = { - 'biddercode': 'realvu', - 'adUnitCode': 'ad1', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '7ba299eba818c1', - 'mediaType': 'banner', - 'creative_id': 85792851, - 'cpm': 0.4308 - }; - realvuAnalyticsAdapter.track({ - eventType: CONSTANTS.EVENTS.BID_RESPONSE, - args: args + it('isInView return "NA"', function () { + const adUnitCode = '1234'; + let result = realvuAnalyticsAdapter.isInView(adUnitCode); + expect(result).to.equal('NA'); }); - const boost = window.top1.realvu_aa; - expect(boost.ads[boost.len - 1].bids.length).to.equal(1); - realvuAnalyticsAdapter.track({ - eventType: CONSTANTS.EVENTS.BID_WON, - args: args + it('bid response event', function () { + const config = { + options: { + partnerId: '1Y', + regAllUnits: true + // unitIds: ['ad1', 'ad2'] + } + }; + realvuAnalyticsAdapter.enableAnalytics(config); + const args = { + 'biddercode': 'realvu', + 'adUnitCode': 'ad1', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '7ba299eba818c1', + 'mediaType': 'banner', + 'creative_id': 85792851, + 'cpm': 0.4308 + }; + realvuAnalyticsAdapter.track({ + eventType: CONSTANTS.EVENTS.BID_RESPONSE, + args: args + }); + const boost = window.top1.realvu_aa; + expect(boost.ads[boost.len - 1].bids.length).to.equal(1); + + realvuAnalyticsAdapter.track({ + eventType: CONSTANTS.EVENTS.BID_WON, + args: args + }); + expect(boost.ads[boost.len - 1].bids[0].winner).to.equal(1); }); - expect(boost.ads[boost.len - 1].bids[0].winner).to.equal(1); }); -}); -describe('RealVu Boost.', () => { - before(() => { - addDiv('ad1'); - addDiv('ad2'); - }); - after(() => { - let a1 = document.getElementById('ad1'); - document.body.removeChild(a1); - let a2 = document.getElementById('ad2'); - document.body.removeChild(a2); - }); + describe('Boost.', function () { + const boost = window.top1.realvu_aa; - const boost = window.top1.realvu_aa; + it('brd', function () { + let a1 = document.getElementById('ad1'); + let p = boost.brd(a1, 'Left'); + expect(typeof p).to.not.equal('undefined'); + }); - it('brd', () => { - let a1 = document.getElementById('ad1'); - let p = boost.brd(a1, 'Left'); - expect(typeof p).to.not.equal('undefined'); - }); + it('addUnitById', function () { + let a1 = document.getElementById('ad1'); + let p = boost.addUnitById('1Y', 'ad1'); + expect(typeof p).to.not.equal('undefined'); + }); - it('addUnitById', () => { - let a1 = document.getElementById('ad1'); - let p = boost.addUnitById('1Y', 'ad1'); - expect(typeof p).to.not.equal('undefined'); - }); + it('questA', function () { + const dv = document.getElementById('ad1'); + let q = boost.questA(dv); + expect(q).to.not.equal(null); + }); - it('questA', () => { - const dv = document.getElementById('ad1'); - let q = boost.questA(dv); - expect(q).to.not.equal(null); - }); + it('render', function () { + let dv = document.getElementById('ad1'); + // dv.style.width = '728px'; + // dv.style.height = '90px'; + // dv.style.display = 'block'; + dv.getBoundingClientRect = false; + // document.body.appendChild(dv); + let q = boost.findPosG(dv); + expect(q).to.not.equal(null); + }); - it('render', () => { - let dv = document.getElementById('ad1'); - // dv.style.width = '728px'; - // dv.style.height = '90px'; - // dv.style.display = 'block'; - dv.getBoundingClientRect = false; - // document.body.appendChild(dv); - let q = boost.findPosG(dv); - expect(q).to.not.equal(null); - }); + it('readPos', function () { + const a = boost.ads[boost.len - 1]; + let r = boost.readPos(a); + expect(r).to.equal(true); + }); + + it('send_track', function () { + const a = boost.ads[boost.len - 1]; + boost.track(a, 'show', ''); + boost.sr = 'a'; + boost.send_track(); + expect(boost.beacons.length).to.equal(0); + }); + + it('questA text', function () { + let p = document.createElement('p'); + p.innerHTML = 'ABC'; + document.body.appendChild(p); + let r = boost.questA(p.firstChild); + document.body.removeChild(p); + expect(r).to.not.equal(null); + }); - it('readPos', () => { - const a = boost.ads[boost.len - 1]; - let r = boost.readPos(a); - expect(r).to.equal(true); + it('_f=conf', function () { + const a = boost.ads[boost.len - 1]; + let r = boost.tru(a, 'conf'); + expect(r).to.not.include('_ps='); + }); }); }); diff --git a/test/spec/modules/reklamstoreBidAdapter_spec.js b/test/spec/modules/reklamstoreBidAdapter_spec.js new file mode 100644 index 00000000000..3ac40e20eaf --- /dev/null +++ b/test/spec/modules/reklamstoreBidAdapter_spec.js @@ -0,0 +1,85 @@ +import { expect } from 'chai'; +import { spec } from 'modules/reklamstoreBidAdapter'; + +describe('reklamstoreBidAdapterTests', function() { + let bidRequestData = { + bids: [ + { + bidder: 'reklamstore', + params: { + regionId: 532211 + }, + sizes: [[300, 250]] + } + ] + }; + let request = []; + + it('validate_params', function() { + expect( + spec.isBidRequestValid({ + bidder: 'reklamstore', + params: { + regionId: 532211 + } + }) + ).to.equal(true); + }); + + it('validate_generated_params', function() { + let bidderRequest = { + refererInfo: { + referer: 'http://reklamstore.com' + } + }; + request = spec.buildRequests(bidRequestData.bids, bidderRequest); + let req_data = request[0].data; + + expect(req_data.regionId).to.equal(532211); + }); + + const serverResponse = { + body: + { + cpm: 1.2, + ad: 'Ad html', + w: 300, + h: 250, + syncs: [{ + type: 'image', + url: 'http://link1' + }, + { + type: 'iframe', + url: 'http://link2' + } + ] + } + }; + + it('validate_response_params', function() { + let bids = spec.interpretResponse(serverResponse, bidRequestData.bids[0]); + expect(bids).to.have.lengthOf(1); + + let bid = bids[0]; + expect(bid.ad).to.equal('Ad html'); + expect(bid.cpm).to.equal(1.2); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.currency).to.equal('USD'); + }); + + it('should return no syncs when pixel syncing is disabled', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: false }, [serverResponse]); + expect(syncs).to.deep.equal([]); + }); + + it('should return user syncs', function () { + const syncs = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: true}, [serverResponse]); + const expected = [ + { type: 'image', url: 'http://link1' }, + { type: 'iframe', url: 'http://link2' }, + ]; + expect(syncs).to.deep.equal(expected); + }); +}); diff --git a/test/spec/modules/reloadBidAdapter_spec.js b/test/spec/modules/reloadBidAdapter_spec.js new file mode 100644 index 00000000000..674c810d48a --- /dev/null +++ b/test/spec/modules/reloadBidAdapter_spec.js @@ -0,0 +1,295 @@ +import { expect } from 'chai'; +import { spec } from 'modules/reloadBidAdapter'; + +let getParams = () => { + return JSON.parse(JSON.stringify({ + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 1, + 'type': 'pcm' + })); +}; + +let getBidderRequest = () => { + return JSON.parse(JSON.stringify({ + bidderCode: 'reload', + auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + bidderRequestId: '7101db09af0db2', + start: new Date().getTime(), + bids: [{ + bidder: 'reload', + bidId: '84ab500420319d', + bidderRequestId: '7101db09af0db2', + auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + params: getParams() + }] + })); +}; + +let getValidBidRequests = () => { + return JSON.parse(JSON.stringify([ + { + 'bidder': 'reload', + 'params': getParams(), + 'mediaTypes': { + 'banner': { + 'sizes': [[160, 600]] + } + }, + 'adUnitCode': '1b243858-3c53-43dc-9fdf-89f839ea4a0f', + 'transactionId': '8cbafa10-123d-4673-a1a5-04a1c7d62ded', + 'sizes': [[160, 600]], + 'bidId': '2236e11dc09931', + 'bidderRequestId': '1266bb886c2267', + 'auctionId': '4fb72c4d-94dc-4db1-8fac-3c2090ceeec0', + 'src': 'client', + 'bidRequestsCount': 1 + } + ])); +} + +let getExt1ServerResponse = () => { + return JSON.parse(JSON.stringify({ + 'pcmdata': { + 'thisVer': '100', + 'plcmSett': { + 'name': 'zz_test_mariano_adapter', + 'Version': '210', + 'lifeSpan': '100', + 'versionFolder': 'v4.14q', + 'versionFolderA': 'v4.14q', + 'versionFolderB': '', + 'stage': 'zz_test_mariano_adapter', + 'synchro': 1556916507000, + 'localCache': 'true', + 'testCase': 'A:00_B:100', + 'opdomain': '1', + 'checksum': '6378', + 'cpm': '0', + 'bstfct': '100', + 'totstop': 'false', + 'pcmurl': 'bidsrv01.reload.net' + }, + 'srvUrl': 'bidsrv01.reload.net', + 'instr': {'go': true, 'prc': 32, 'cur': 'USD'}, + 'statStr': 'eyN4aHYnQCk5OTotOC', + 'status': 'ok', + 'message': '', + 'log': '---- LOG ----' + }, + 'plcmID': 'zz_test_mariano_adapter', + 'partID': 'prx_part', + 'opdomID': '0', + 'bsrvID': 1, + 'adUnitCode': '1b243858-3c53-43dc-9fdf-89f839ea4a0f', + 'banner': {'w': 300, 'h': 250} + })); +} + +let getExt2ServerResponse = () => { + return JSON.parse(JSON.stringify({ + 'pcmdata': { + 'thisVer': '100', + 'plcmSett': { + 'name': 'placement_01', + 'Version': '210', + 'lifeSpan': '100', + 'versionFolder': 'v4.14q', + 'versionFolderA': 'v4.14q', + 'versionFolderB': '', + 'stage': 'placement_01', + 'synchro': 1556574760000, + 'localCache': 'true', + 'testCase': 'A:00_B:100', + 'opdomain': '1', + 'checksum': '6378', + 'cpm': '0', + 'bstfct': '100', + 'totstop': 'false', + 'pcmurl': 'bidsrv00.reload.net' + }, + 'srvUrl': 'bidsrv00.reload.net', + 'log': 'incomp_input_obj_version', + 'message': 'incomp_input_obj_version', + 'status': 'error' + }, + 'plcmID': 'placement_01', + 'partID': 'prx_part', + 'opdomID': '0', + 'bsrvID': 1, + 'adUnitCode': '1b243858-3c53-43dc-9fdf-89f839ea4a0f', + 'banner': {'w': 160, 'h': 600} + })); +} + +let getServerResponse = (pExt) => { + return JSON.parse(JSON.stringify({ + 'body': { + 'id': '2759340f70210d', + 'bidid': 'fbs-br-3mzdbycetjv8f8079', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'fbs-br-stbd-bd-3mzdbycetjv8f807b', + 'price': 0, + 'nurl': '', + 'adm': '', + 'ext': pExt + } + ], + 'seat': 'fbs-br-stbd-3mzdbycetjv8f807a', + 'group': 0 + } + ] + }, + 'headers': {} + })); +} + +describe('ReloadAdapter', function () { + describe('isBidRequestValid', function () { + var bid = { + 'bidder': 'reload', + 'params': { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 23, + 'type': 'pcm' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when bsrvID is not number', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 'abc' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when bsrvID > 99', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 230 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when bsrvID < 0', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': -3, + 'type': 'pcm' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests()', function () { + let vRequests = spec.buildRequests(getValidBidRequests(), {}); + let vData = JSON.parse(vRequests[0].data); + + it('should send one requests', () => { + expect(vRequests.length).to.equal(1); + }); + + it('should send one requests, one impression', () => { + expect(vData.imp.length).to.equal(1); + }); + + it('should exists ext.type and ext.pcmdata', () => { + expect(vData.imp[0].banner).to.exist; + expect(vData.imp[0].banner.ext).to.exist; + expect(vData.imp[0].banner.ext.type).to.exist; + expect(vData.imp[0].banner.ext.pcmdata).to.exist; + expect(vData.imp[0].banner.ext.type).to.equal('pcm'); + }); + }); + + describe('interpretResponse()', function () { + it('Returns an empty array', () => { + let vData = spec.interpretResponse(getServerResponse(getExt2ServerResponse()), {}); + + expect(vData.length).to.equal(0); + }); + + it('Returns an array with one response', () => { + let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); + expect(vData.length).to.equal(1); + }); + + it('required fileds', () => { + let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); + expect(vData.length).to.equal(1); + expect(vData[0]).to.have.all.keys(['requestId', 'ad', 'cpm', 'width', 'height', 'creativeId', 'currency', 'ttl', 'netRevenue']); + }); + + it('CPM great than 0', () => { + let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); + expect(vData[0].cpm).to.greaterThan(0); + }); + + it('instruction empty', () => { + let vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr = null; + let vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + + vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr = undefined; + vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + + vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr.go = undefined; + vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + }); + + it('instruction with go = false', () => { + let vResponse = getServerResponse(getExt1ServerResponse()); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr.go = false; + let vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + }); + + it('incompatibility output object version (thisVer)', () => { + let vResponse = getServerResponse(getExt1ServerResponse()); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.thisVer = '200'; + let vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/rxrtbBidAdapter_spec.js b/test/spec/modules/rexrtbBidAdapter_spec.js similarity index 75% rename from test/spec/modules/rxrtbBidAdapter_spec.js rename to test/spec/modules/rexrtbBidAdapter_spec.js index 0785c6f144b..b35e05bbf46 100644 --- a/test/spec/modules/rxrtbBidAdapter_spec.js +++ b/test/spec/modules/rexrtbBidAdapter_spec.js @@ -1,11 +1,11 @@ import {expect} from 'chai'; -import {spec} from 'modules/rxrtbBidAdapter'; +import {spec} from 'modules/rexrtbBidAdapter'; -describe('rxrtb adapater', () => { - describe('Test validate req', () => { - it('should accept minimum valid bid', () => { +describe('rexrtb adapater', function () { + describe('Test validate req', function () { + it('should accept minimum valid bid', function () { let bid = { - bidder: 'rxrtb', + bidder: 'rexrtb', params: { id: 89, token: '658f11a5efbbce2f9be3f1f146fcbc22', @@ -17,9 +17,9 @@ describe('rxrtb adapater', () => { expect(isValid).to.equal(true); }); - it('should reject missing id', () => { + it('should reject missing id', function () { let bid = { - bidder: 'rxrtb', + bidder: 'rexrtb', params: { token: '658f11a5efbbce2f9be3f1f146fcbc22', source: 'prebidtest' @@ -30,9 +30,9 @@ describe('rxrtb adapater', () => { expect(isValid).to.equal(false); }); - it('should reject id not Integer', () => { + it('should reject id not Integer', function () { let bid = { - bidder: 'rxrtb', + bidder: 'rexrtb', params: { id: '123', token: '658f11a5efbbce2f9be3f1f146fcbc22', @@ -43,25 +43,12 @@ describe('rxrtb adapater', () => { expect(isValid).to.equal(false); }); - - it('should reject missing source', () => { - let bid = { - bidder: 'rxrtb', - params: { - id: 89, - token: '658f11a5efbbce2f9be3f1f146fcbc22' - } - }; - const isValid = spec.isBidRequestValid(bid); - - expect(isValid).to.equal(false); - }); }); - describe('Test build request', () => { - it('minimum request', () => { + describe('Test build request', function () { + it('minimum request', function () { let bid = { - bidder: 'rxrtb', + bidder: 'rexrtb', sizes: [[728, 90]], bidId: '4d0a6829338a07', adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -88,8 +75,8 @@ describe('rxrtb adapater', () => { }); }); - describe('Test interpret response', () => { - it('General banner response', () => { + describe('Test interpret response', function () { + it('General banner response', function () { let resp = spec.interpretResponse({ body: { id: 'abcd', diff --git a/test/spec/modules/rhythmoneBidAdapter_spec.js b/test/spec/modules/rhythmoneBidAdapter_spec.js index ad113b9021a..b6ac09a6207 100644 --- a/test/spec/modules/rhythmoneBidAdapter_spec.js +++ b/test/spec/modules/rhythmoneBidAdapter_spec.js @@ -1,81 +1,745 @@ import {spec} from '../../../modules/rhythmoneBidAdapter'; -var assert = require('assert'); +import * as utils from '../../../src/utils'; +import * as sinon from 'sinon'; + +var r1adapter = spec; describe('rhythmone adapter tests', function () { - describe('auditBeacon', function() { - var z = spec; - var beaconURL = z.getUserSyncs({pixelEnabled: true})[0]; + beforeEach(function() { + this.defaultBidderRequest = { + 'refererInfo': { + 'referer': 'Reference Page', + 'stack': [ + 'aodomain.dvl', + 'page.dvl' + ] + } + }; + }); + + describe('Verify 1.0 POST Banner Bid Request', function () { + it('buildRequests works', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaType': 'banner', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'sizes': [[300, 250]], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + expect(bidRequest.url).to.have.string('//tag.1rx.io/rmp/myplacement/0/mypath?z=myzone&hbv='); + expect(bidRequest.method).to.equal('POST'); + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.site.ref).to.equal('Reference Page'); + expect(openrtbRequest.device).to.not.equal(null); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + expect(openrtbRequest.device.dnt).to.equal(0); + expect(openrtbRequest.imp[0].banner).to.not.equal(null); + expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(250); + expect(openrtbRequest.imp[0].ext.bidder.zone).to.equal('myzone'); + expect(openrtbRequest.imp[0].ext.bidder.path).to.equal('mypath'); + }); + + it('interpretResponse works', function() { + var bidList = { + 'body': [ + { + 'impid': 'div-gpt-ad-1438287399331-0', + 'w': 300, + 'h': 250, + 'adm': '
    My Compelling Ad
    ', + 'price': 1, + 'crid': 'cr-cfy24' + } + ] + }; + + var bannerBids = r1adapter.interpretResponse(bidList); + + expect(bannerBids.length).to.equal(1); + const bid = bannerBids[0]; + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('cr-cfy24'); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.cpm).to.equal(1.0); + expect(bid.ttl).to.equal(350); + }); + }); + + describe('Verify POST Video Bid Request', function() { + it('buildRequests works', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'sizes': [ + [300, 250] + ], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + expect(bidRequest.url).to.have.string('//tag.1rx.io/rmp/myplacement/0/mypath?z=myzone&hbv='); + expect(bidRequest.method).to.equal('POST'); + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.device).to.not.equal(null); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + expect(openrtbRequest.device).to.have.property('dnt'); + expect(openrtbRequest.imp[0].video).to.not.equal(null); + expect(openrtbRequest.imp[0].video.w).to.equal(640); + expect(openrtbRequest.imp[0].video.h).to.equal(480); + expect(openrtbRequest.imp[0].video.mimes[0]).to.equal('video/mp4'); + expect(openrtbRequest.imp[0].video.protocols).to.eql([2, 3, 5, 6]); + expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); + expect(openrtbRequest.imp[0].video.skip).to.equal(0); + expect(openrtbRequest.imp[0].video.playbackmethod).to.eql([1, 2, 3, 4]); + expect(openrtbRequest.imp[0].video.delivery[0]).to.equal(1); + expect(openrtbRequest.imp[0].video.api).to.eql([1, 2, 5]); + }); + + it('interpretResponse works', function() { + var bidList = { + 'body': [ + { + 'impid': 'div-gpt-ad-1438287399331-1', + 'price': 1, + 'nurl': 'http://testdomain/rmp/placementid/0/path?reqId=1636037', + 'adomain': [ + 'test.com' + ], + 'cid': '467415', + 'crid': 'cr-vid', + 'w': 800, + 'h': 600 + } + ] + }; + + var videoBids = r1adapter.interpretResponse(bidList); - it('should contain the correct path', function() { - var u = '//hbevents.1rx.io/audit?' - assert.equal(beaconURL.url.substring(0, u.length), u); + expect(videoBids.length).to.equal(1); + const bid = videoBids[0]; + expect(bid.width).to.equal(800); + expect(bid.height).to.equal(600); + expect(bid.vastUrl).to.equal('http://testdomain/rmp/placementid/0/path?reqId=1636037'); + expect(bid.mediaType).to.equal('video'); + expect(bid.creativeId).to.equal('cr-vid'); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.cpm).to.equal(1.0); + expect(bid.ttl).to.equal(600); }); }); - describe('rhythmoneResponse', function () { - var z = spec; + describe('Verify Multi-Format ads and Multiple Size Bid Request', function() { + it('buildRequests works', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath', + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250], + [300, 600] + ] + }, + 'video': { + 'playerSize': [[640, 480]], + 'context': 'instream' + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-5', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; - var rmpRequest = z.buildRequests( - [ + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.site.ref).to.equal('Reference Page'); + expect(openrtbRequest.device).to.not.equal(null); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + expect(openrtbRequest.device).to.have.property('dnt'); + expect(openrtbRequest.imp[0].video).to.not.equal(null); + expect(openrtbRequest.imp[0].video.w).to.equal(640); + expect(openrtbRequest.imp[0].video.h).to.equal(480); + expect(openrtbRequest.imp[0].video.mimes[0]).to.equal('video/mp4'); + expect(openrtbRequest.imp[0].video.protocols).to.eql([2, 3, 5, 6]); + expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); + expect(openrtbRequest.imp[0].video.skip).to.equal(0); + expect(openrtbRequest.imp[0].video.playbackmethod).to.eql([1, 2, 3, 4]); + expect(openrtbRequest.imp[0].video.delivery[0]).to.equal(1); + expect(openrtbRequest.imp[0].video.api).to.eql([1, 2, 5]); + expect(openrtbRequest.imp[0].banner).to.not.equal(null); + expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(250); + expect(openrtbRequest.imp[0].banner.format[1].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[1].h).to.equal(600); + expect(openrtbRequest.imp[0].ext.bidder.zone).to.equal('myzone'); + expect(openrtbRequest.imp[0].ext.bidder.path).to.equal('mypath'); + }); + + it('interpretResponse works', function() { + var bidList = { + 'body': { + 'id': '1e810245dd1779', + 'seatbid': [ + { + 'bid': [ + { + 'impid': 'div-gpt-ad-1438287399331-5', + 'price': 1, + 'nurl': 'http://testdomain/rmp/placementid/0/path?reqId=1636037', + 'adomain': [ + 'test.com' + ], + 'cid': '467415', + 'crid': 'cr-vid', + 'w': 800, + 'h': 600 + } + ] + } + ] + } + }; + + var forRMPMultiFormatResponse = r1adapter.interpretResponse(bidList); + + expect(forRMPMultiFormatResponse.length).to.equal(1); + const bid = forRMPMultiFormatResponse[0]; + expect(bid.width).to.equal(800); + expect(bid.height).to.equal(600); + expect(bid.vastUrl).to.equal('http://testdomain/rmp/placementid/0/path?reqId=1636037'); + expect(bid.mediaType).to.equal('video'); + expect(bid.creativeId).to.equal('cr-vid'); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.cpm).to.equal(1.0); + expect(bid.ttl).to.equal(600); + }); + }); + + describe('misc buildRequests', function() { + it('should send GDPR Consent data to RhythmOne tag', function () { + var bidRequestList = [ { 'bidder': 'rhythmone', 'params': { - 'placementId': 'xyz', - 'keywords': '', - 'categories': [], - 'trace': true, - 'method': 'get', - 'endpoint': 'http://fakedomain.com' + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-3', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var consentString = 'testConsentString'; + var gdprBidderRequest = this.defaultBidderRequest; + gdprBidderRequest.gdprConsent = { + 'gdprApplies': true, + 'consentString': consentString + }; + + var bidRequest = r1adapter.buildRequests(bidRequestList, gdprBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.user.ext.consent).to.equal(consentString); + expect(openrtbRequest.regs.ext.gdpr).to.equal(true); + }); + + it('prefer 2.0 sizes', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 600]] + } }, - 'mediaType': 'video', 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]] + 'sizes': [[300, 250]], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' } - ] - ); + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - it('should have one request to RMP', function() { - assert.equal(rmpRequest.length, 1); + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(600); }); - var mangoRequest = z.buildRequests( - [ + it('does not return request for invalid banner size configuration', function () { + var bidRequestList = [ { 'bidder': 'rhythmone', 'params': { - 'placementId': 'xyz', - 'keywords': '', - 'categories': [], - 'trace': true, - 'method': 'get', - 'api': 'mango', - 'endpoint': 'http://fakedomain.com' + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300]] + } }, 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]] + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' } - ] - ); + ]; - it('should have one request to Mango', function() { - assert.equal(mangoRequest.length, 1); + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + expect(bidRequest.method).to.be.undefined; }); - var bids = z.interpretResponse({ - body: [ + it('does not return request for missing banner size configuration', function () { + var bidRequestList = [ { - 'impid': 'div-gpt-ad-1438287399331-0', - 'w': 300, - 'h': 250, - 'adm': '
    My ad4 with cpm of a4ab3485f434f74f
    ', - 'price': 1, - 'crid': 'cr-cfy24' + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': {} + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' } - ] + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + expect(bidRequest.method).to.be.undefined; }); - it('should register one bid', function() { - assert.equal(bids.length, 1); + it('reject bad sizes', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': {'sizes': [['400', '500'], ['4n0', '5g0']]} + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].banner.format.length).to.equal(1); + }); + + it('dnt is correctly set to 1', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 600]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var dntStub = sinon.stub(utils, 'getDNT').returns(1); + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + dntStub.restore(); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.device.dnt).to.equal(1); + }); + + it('sets floor', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'floor': 100.0 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 600]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].bidfloor).to.equal(100.0); + }); + + it('supports string video sizes', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': ['600', '300'] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].video.w).to.equal(600); + expect(openrtbRequest.imp[0].video.h).to.equal(300); + }); + + it('rejects bad video sizes', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': ['badWidth', 'badHeight'] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].video.w).to.be.undefined; + expect(openrtbRequest.imp[0].video.h).to.be.undefined; + }); + + it('supports missing video size', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].video.w).to.be.undefined; + expect(openrtbRequest.imp[0].video.h).to.be.undefined; + }); + + it('uses default zone and path', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 600] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].ext.bidder.zone).to.equal('1r'); + expect(openrtbRequest.imp[0].ext.bidder.path).to.equal('mvo'); + }); + + it('should return empty when required params not found', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-3', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + expect(bidRequest).to.be.empty; + }); + + it('should return empty site data when refererInfo is missing', function() { + delete this.defaultBidderRequest.refererInfo; + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaType': 'banner', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'sizes': [[300, 250]], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(bidRequest.data); + + expect(openrtbRequest.site.domain).to.equal(''); + expect(openrtbRequest.site.page).to.equal(''); + expect(openrtbRequest.site.ref).to.equal(''); + }); + }); + + it('should return empty site.domain and site.page when refererInfo.stack is empty', function() { + this.defaultBidderRequest.refererInfo.stack = []; + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaType': 'banner', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'sizes': [[300, 250]], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(bidRequest.data); + + expect(openrtbRequest.site.domain).to.equal(''); + expect(openrtbRequest.site.page).to.equal(''); + expect(openrtbRequest.site.ref).to.equal('Reference Page'); + }); + + it('should secure correctly', function() { + this.defaultBidderRequest.refererInfo.stack[0] = ['https://securesite.dvl']; + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaType': 'banner', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'sizes': [[300, 250]], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + const openrtbRequest = JSON.parse(bidRequest.data); + + expect(openrtbRequest.imp[0].secure).to.equal(1); + }); + + describe('misc interpretResponse', function () { + it('No bid response', function() { + var noBidResponse = r1adapter.interpretResponse({ + 'body': '' + }); + expect(noBidResponse.length).to.equal(0); + }); + }); + + describe('isBidRequestValid', function () { + var bid = { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'path': 'mypath', + 'zone': 'myzone' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'bannerDiv' + }; + + it('should return true when required params found', function () { + expect(r1adapter.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when placementId missing', function () { + delete bid.params.placementId; + expect(r1adapter.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('getUserSyncs', function () { + it('returns an empty string', function () { + expect(r1adapter.getUserSyncs()).to.deep.equal([]); }); }); }); diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js new file mode 100644 index 00000000000..16d67ce7ceb --- /dev/null +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -0,0 +1,347 @@ +// import or require modules necessary for the test, e.g.: +import {expect} from 'chai'; // may prefer 'assert' in place of 'expect' +import { + spec +} from 'modules/richaudienceBidAdapter'; +import {config} from 'src/config'; +import * as utils from 'src/utils'; + +describe('Richaudience adapter tests', function () { + var DEFAULT_PARAMS = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + sizes: [ + [300, 250], + [300, 600], + [728, 90], + [970, 250] + ], + bidder: 'richaudience', + params: { + bidfloor: 0.5, + pid: 'ADb1f40rmi', + supplyType: 'site' + }, + auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', + transactionId: '29df2112-348b-4961-8863-1b33684d95e6' + }]; + + var DEFAULT_PARAMS_APP = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + sizes: [ + [300, 250], + [300, 600], + [728, 90], + [970, 250] + ], + bidder: 'richaudience', + params: { + bidfloor: 0.5, + ifa: 'AAAAAAAAA-BBBB-CCCC-1111-222222220000', + pid: 'ADb1f40rmi', + supplyType: 'app', + }, + auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', + transactionId: '29df2112-348b-4961-8863-1b33684d95e6' + }]; + + var DEFAULT_PARAMS_WO_OPTIONAL = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + sizes: [ + [300, 250], + [300, 600], + [728, 90], + [970, 250] + ], + bidder: 'richaudience', + params: { + pid: 'ADb1f40rmi', + supplyType: 'site', + }, + auctionId: '851adee7-d843-48f9-a7e9-9ff00573fcbf', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', + transactionId: '29df2112-348b-4961-8863-1b33684d95e6' + }]; + + var BID_RESPONSE = { + body: { + cpm: 1.50, + adm: '', + media_type: 'js', + width: 300, + height: 250, + creative_id: '189198063', + netRevenue: true, + currency: 'USD', + ttl: 300, + dealId: 'dealId' + + } + }; + + var BID_RESPONSE_VIDEO = { + body: { + cpm: 1.50, + media_type: 'video', + width: 1, + height: 1, + creative_id: '189198063', + netRevenue: true, + currency: 'USD', + ttl: 300, + vastXML: '', + dealId: 'dealId' + } + }; + + it('Verify build request', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'USD' + } + }); + + const request = spec.buildRequests(DEFAULT_PARAMS, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'http://domain.com', + numIframes: 0 + } + }); + + expect(request[0]).to.have.property('method').and.to.equal('POST'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('bidfloor').and.to.equal(0.5); + expect(requestContent).to.have.property('pid').and.to.equal('ADb1f40rmi'); + expect(requestContent).to.have.property('supplyType').and.to.equal('site'); + expect(requestContent).to.have.property('auctionId').and.to.equal('0cb3144c-d084-4686-b0d6-f5dbe917c563'); + expect(requestContent).to.have.property('bidId').and.to.equal('2c7c8e9c900244'); + expect(requestContent).to.have.property('BidRequestsCount').and.to.equal(1); + expect(requestContent).to.have.property('bidder').and.to.equal('richaudience'); + expect(requestContent).to.have.property('bidderRequestId').and.to.equal('1858b7382993ca'); + expect(requestContent).to.have.property('tagId').and.to.equal('test-div'); + expect(requestContent).to.have.property('referer').and.to.equal('http%3A%2F%2Fdomain.com'); + expect(requestContent).to.have.property('sizes'); + expect(requestContent.sizes[0]).to.have.property('w').and.to.equal(300); + expect(requestContent.sizes[0]).to.have.property('h').and.to.equal(250); + expect(requestContent.sizes[1]).to.have.property('w').and.to.equal(300); + expect(requestContent.sizes[1]).to.have.property('h').and.to.equal(600); + expect(requestContent).to.have.property('transactionId').and.to.equal('29df2112-348b-4961-8863-1b33684d95e6'); + expect(requestContent).to.have.property('timeout').and.to.equal(3000); + }); + + describe('gdpr test', function () { + it('Verify build request with GDPR', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'USD' + }, + consentManagement: { + cmpApi: 'iab', + timeout: 8000, + allowAuctionWithoutConsent: true + } + }); + + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'http://domain.com', + numIframes: 0 + } + }); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA'); + }); + + it('Verify adding ifa when supplyType equal to app', function () { + const request = spec.buildRequests(DEFAULT_PARAMS_APP, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'http://domain.com', + numIframes: 0 + } + }); + }); + + it('Verify build request with GDPR without gdprApplies', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + }, + consentManagement: { + cmp: 'iab', + consentRequired: true, + timeout: 8000, + allowAuctionWithoutConsent: true + } + }); + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA' + }, + refererInfo: { + referer: 'http://domain.com', + numIframes: 0 + } + }); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA'); + }); + }); + + it('Verify interprete response', function () { + const request = spec.buildRequests(DEFAULT_PARAMS, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'http://domain.com', + numIframes: 0 + } + }); + + const bids = spec.interpretResponse(BID_RESPONSE, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.50); + expect(bid.ad).to.equal(''); + expect(bid.mediaType).to.equal('js'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('189198063'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(300); + expect(bid.dealId).to.equal('dealId'); + }); + + it('no banner media response', function () { + const request = spec.buildRequests(DEFAULT_PARAMS, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'http://domain.com', + numIframes: 0 + } + }); + + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request[0]); + const bid = bids[0]; + expect(bid.vastXml).to.equal(''); + }); + + it('Verifies bidder_code', function () { + expect(spec.code).to.equal('richaudience'); + }); + + it('Verifies bidder aliases', function () { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('ra'); + }); + + it('Verifies if bid request is valid', function () { + expect(spec.isBidRequestValid(DEFAULT_PARAMS[0])).to.equal(true); + expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ + params: {} + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + pid: 'ADb1f40rmi' + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + supplyType: 'site' + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + supplyType: 'app' + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + pid: 'ADb1f40rmi', + supplyType: 'site' + } + })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + pid: ['1gCB5ZC4XL', '1a40xk8qSV'], + supplyType: 'site' + } + })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + pid: 'ADb1f40rmi', + supplyType: 'site' + } + })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + pid: 'ADb1f40rmi', + supplyType: 'app', + ifa: 'AAAAAAAAA-BBBB-CCCC-1111-222222220000', + } + })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + pid: 'ADb1f40rmi', + supplyType: 'site', + bidfloor: 0.50, + } + })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + pid: 'ADb1f40rmi', + supplyType: 'site', + bidfloor: 0.50, + } + })).to.equal(true); + }); + + it('Verifies user sync', function () { + var syncs = spec.getUserSyncs({ + iframeEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + syncs = spec.getUserSyncs({ + iframeEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true + }, [], {consentString: '', gdprApplies: false}); + expect(syncs).to.have.lengthOf(1); + }); +}); diff --git a/test/spec/modules/rivrAnalyticsAdapter_spec.js b/test/spec/modules/rivrAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..2676c3a59b6 --- /dev/null +++ b/test/spec/modules/rivrAnalyticsAdapter_spec.js @@ -0,0 +1,150 @@ +import * as utils from 'src/utils'; +import analyticsAdapter from 'modules/rivrAnalyticsAdapter'; +import { + sendImpressions, + handleClickEventWithClosureScope, + createUnOptimisedParamsField, + dataLoaderForHandler, + pinHandlerToHTMLElement, + setAuctionAbjectPosition, + createNewAuctionObject, + concatAllUnits, + trackAuctionEnd, + handleImpression, + getCookie, + storeAndReturnRivrUsrIdCookie, + arrayDifference, + activelyWaitForBannersToRender, +} from 'modules/rivrAnalyticsAdapter'; +import {expect} from 'chai'; +import adapterManager from 'src/adapterManager'; +import * as ajax from 'src/ajax'; +import CONSTANTS from 'src/constants.json'; + +const events = require('../../../src/events'); + +describe('RIVR Analytics adapter', () => { + const EXPIRING_QUEUE_TIMEOUT = 4000; + const EXPIRING_QUEUE_TIMEOUT_MOCK = 100; + const RVR_CLIENT_ID_MOCK = 'aCliendId'; + const SITE_CATEGORIES_MOCK = ['cat1', 'cat2']; + const EMITTED_AUCTION_ID = 1; + const TRACKER_BASE_URL_MOCK = 'tracker.rivr.simplaex.com'; + const UUID_REG_EXP = new RegExp('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', 'i'); + const MOCK_RIVRADDON_CONTEXT = {}; + let sandbox; + let ajaxStub; + let rivraddonsEnableAnalyticsStub; + let rivraddonsTrackPbjsEventStub; + let timer; + + before(() => { + sandbox = sinon.sandbox.create(); + window.rivraddon = { + analytics: { + enableAnalytics: () => {}, + getContext: () => { return MOCK_RIVRADDON_CONTEXT; }, + trackPbjsEvent: () => {}, + } + }; + rivraddonsEnableAnalyticsStub = sandbox.stub(window.rivraddon.analytics, 'enableAnalytics'); + }); + + beforeEach(() => { + timer = sandbox.useFakeTimers(0); + ajaxStub = sandbox.stub(ajax, 'ajax'); + sinon.stub(events, 'getEvents').returns([]); + + adapterManager.registerAnalyticsAdapter({ + code: 'rivr', + adapter: analyticsAdapter + }); + adapterManager.enableAnalytics({ + provider: 'rivr', + options: { + clientID: RVR_CLIENT_ID_MOCK, + adUnits: [utils.deepClone(BANNER_AD_UNITS_MOCK)], + siteCategories: SITE_CATEGORIES_MOCK, + } + }); + }); + + afterEach(() => { + analyticsAdapter.disableAnalytics(); + events.getEvents.restore(); + ajaxStub.restore(); + timer.restore(); + }); + + after(() => { + sandbox.restore(); + delete window.rivraddon; + }); + + it('enableAnalytics - should call rivraddon enableAnalytics with the correct arguments', () => { + // adapterManager.enableAnalytics() is called in beforeEach. If just called here it doesn't seem to work. + const firstArgument = rivraddonsEnableAnalyticsStub.getCall(0).args[0]; + const secondArgument = rivraddonsEnableAnalyticsStub.getCall(0).args[1]; + + expect(firstArgument.provider).to.be.equal('rivr'); + + expect(secondArgument).to.have.property('utils'); + expect(secondArgument).to.have.property('ajax'); + }); + + it('Firing an event when rivraddon context is not defined it should do nothing', () => { + let rivraddonsGetContextStub = sandbox.stub(window.rivraddon.analytics, 'getContext'); + rivraddonsTrackPbjsEventStub = sandbox.stub(window.rivraddon.analytics, 'trackPbjsEvent'); + + expect(rivraddonsTrackPbjsEventStub.callCount).to.be.equal(0); + + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: EMITTED_AUCTION_ID, config: {}, timeout: 3000}); + + expect(rivraddonsTrackPbjsEventStub.callCount).to.be.equal(0); + + window.rivraddon.analytics.getContext.restore(); + window.rivraddon.analytics.trackPbjsEvent.restore(); + }); + + it('Firing AUCTION_INIT should call rivraddon trackPbjsEvent passing the parameters', () => { + rivraddonsTrackPbjsEventStub = sandbox.stub(window.rivraddon.analytics, 'trackPbjsEvent'); + + expect(rivraddonsTrackPbjsEventStub.callCount).to.be.equal(0); + + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: EMITTED_AUCTION_ID, config: {}, timeout: 3000}); + + expect(rivraddonsTrackPbjsEventStub.callCount).to.be.equal(1); + + const firstArgument = rivraddonsTrackPbjsEventStub.getCall(0).args[0]; + expect(firstArgument.eventType).to.be.equal(CONSTANTS.EVENTS.AUCTION_INIT); + expect(firstArgument.args.auctionId).to.be.equal(EMITTED_AUCTION_ID); + + window.rivraddon.analytics.trackPbjsEvent.restore(); + }); + + const BANNER_AD_UNITS_MOCK = [ + { + code: 'banner-container1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 200], [300, 600]] + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: '10433394', + reserve: 0.5 + } + }, + { + bidder: 'huddledmasses', + params: { + placement_id: 0 + } + }, + ] + } + ]; +}); diff --git a/test/spec/modules/rockyouBidAdapter_spec.js b/test/spec/modules/rockyouBidAdapter_spec.js index f929b50d581..65d87566c26 100644 --- a/test/spec/modules/rockyouBidAdapter_spec.js +++ b/test/spec/modules/rockyouBidAdapter_spec.js @@ -2,16 +2,16 @@ import { expect } from 'chai'; import { spec, internals } from 'modules/rockyouBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('RockYouAdapter', () => { +describe('RockYouAdapter', function () { const adapter = newBidder(spec); - describe('bid validator', () => { - it('rejects a bid that is missing the placementId', () => { + describe('bid validator', function () { + it('rejects a bid that is missing the placementId', function () { let testBid = {}; expect(spec.isBidRequestValid(testBid)).to.be.false; }); - it('accepts a bid with all the expected parameters', () => { + it('accepts a bid with all the expected parameters', function () { let testBid = { params: { placementId: 'f39ba81609' @@ -22,7 +22,7 @@ describe('RockYouAdapter', () => { }); }); - describe('request builder', () => { + describe('request builder', function () { // Taken from the docs, so used as much as is valid const sampleBidRequest = { 'bidder': 'tests', @@ -43,7 +43,7 @@ describe('RockYouAdapter', () => { } }; - it('successfully generates a URL', () => { + it('successfully generates a URL', function () { const placementId = 'ZZZPLACEMENTZZZ'; let bidRequests = [ @@ -65,7 +65,7 @@ describe('RockYouAdapter', () => { expect(result.url).to.include('/servlet/rotator/' + placementId + '/0/vo?z=') }); - it('uses the bidId id as the openRtb request ID', () => { + it('uses the bidId id as the openRtb request ID', function () { const bidId = '51ef8751f9aead'; let bidRequests = [ @@ -84,7 +84,7 @@ describe('RockYouAdapter', () => { expect(payload.id).to.equal(bidId); }); - it('generates the device payload as expected', () => { + it('generates the device payload as expected', function () { let bidRequests = [ sampleBidRequest ]; @@ -103,7 +103,7 @@ describe('RockYouAdapter', () => { expect(userData).to.not.be.null; }); - it('generates multiple requests with single imp bodies', () => { + it('generates multiple requests with single imp bodies', function () { const SECOND_PLACEMENT_ID = 'YYYPLACEMENTIDYYY'; let firstBidRequest = JSON.parse(JSON.stringify(sampleBidRequest)); let secondBidRequest = JSON.parse(JSON.stringify(sampleBidRequest)); @@ -146,7 +146,7 @@ describe('RockYouAdapter', () => { expect(secondRequest.url.indexOf(SECOND_PLACEMENT_ID)).to.be.gt(0); }); - it('generates a banner request as expected', () => { + it('generates a banner request as expected', function () { // clone the sample for stability let localBidRequest = JSON.parse(JSON.stringify(sampleBidRequest)); @@ -172,7 +172,7 @@ describe('RockYouAdapter', () => { expect(bannerData.h).to.equal(50); }); - it('generates a banner request using a singular adSize instead of an array', () => { + it('generates a banner request using a singular adSize instead of an array', function () { // clone the sample for stability let localBidRequest = JSON.parse(JSON.stringify(sampleBidRequest)); localBidRequest.sizes = [320, 50]; @@ -200,7 +200,7 @@ describe('RockYouAdapter', () => { expect(bannerData.h).to.equal(50); }); - it('fails gracefully on an invalid size', () => { + it('fails gracefully on an invalid size', function () { // clone the sample for stability let localBidRequest = JSON.parse(JSON.stringify(sampleBidRequest)); localBidRequest.sizes = ['x', 'w']; @@ -229,7 +229,7 @@ describe('RockYouAdapter', () => { expect(bannerData.h).to.equal(null); }); - it('generates a video request as expected', () => { + it('generates a video request as expected', function () { // clone the sample for stability let localBidRequest = JSON.parse(JSON.stringify(sampleBidRequest)); @@ -259,7 +259,7 @@ describe('RockYouAdapter', () => { expect(videoData.h).to.equal(56); }); - it('propagates the mediaTypes object in the built request', () => { + it('propagates the mediaTypes object in the built request', function () { let localBidRequest = JSON.parse(JSON.stringify(sampleBidRequest)); localBidRequest.mediaTypes = { video: {} }; @@ -278,8 +278,8 @@ describe('RockYouAdapter', () => { }); }); - describe('response interpreter', () => { - it('returns an empty array when no bids present', () => { + describe('response interpreter', function () { + it('returns an empty array when no bids present', function () { // an empty JSON body indicates no ad was found let result = spec.interpretResponse({ body: '' }, {}) @@ -287,13 +287,13 @@ describe('RockYouAdapter', () => { expect(result).to.eql([]); }); - it('gracefully fails when a non-JSON body is present', () => { + it('gracefully fails when a non-JSON body is present', function () { let result = spec.interpretResponse({ body: 'THIS IS NOT ' }, {}) expect(result).to.eql([]); }); - it('returns a valid bid response on sucessful banner request', () => { + it('returns a valid bid response on sucessful banner request', function () { let incomingRequestId = 'XXtestingXX'; let responsePrice = 3.14 @@ -365,7 +365,7 @@ describe('RockYouAdapter', () => { expect(processedBid.currency).to.equal(responseCurrency); }); - it('returns an valid bid response on sucessful video request', () => { + it('returns an valid bid response on sucessful video request', function () { let incomingRequestId = 'XXtesting-275XX'; let responsePrice = 6 @@ -438,7 +438,7 @@ describe('RockYouAdapter', () => { expect(processedBid.vastXml).to.equal(responseCreative); }); - it('generates event callbacks as expected', () => { + it('generates event callbacks as expected', function () { let tally = {}; let renderer = { handleVideoEvent: (eventObject) => { @@ -466,7 +466,7 @@ describe('RockYouAdapter', () => { expect(tally['ended']).to.equal(2); }); - it('generates a renderer that will hide on complete', () => { + it('generates a renderer that will hide on complete', function () { let elementName = 'test_element_id'; let selector = `#${elementName}`; diff --git a/test/spec/modules/roxotAnalyticsAdapter_spec.js b/test/spec/modules/roxotAnalyticsAdapter_spec.js index f2db77892f6..cd48cb7f37d 100644 --- a/test/spec/modules/roxotAnalyticsAdapter_spec.js +++ b/test/spec/modules/roxotAnalyticsAdapter_spec.js @@ -1,61 +1,450 @@ import roxotAnalytic from 'modules/roxotAnalyticsAdapter'; -import { expect } from 'chai'; +import {expect} from 'chai'; + let events = require('src/events'); -let adaptermanager = require('src/adaptermanager'); let constants = require('src/constants.json'); describe('Roxot Prebid Analytic', function () { let xhr; - before(() => { + let requests; + + let roxotConfigServerUrl = 'config-server'; + let roxotEventServerUrl = 'event-server'; + let publisherId = 'test_roxot_prebid_analytics_publisher_id'; + + let auctionId = '0ea14159-2058-4b87-a966-9d7652176a56'; + let timeout = 3000; + let auctionStartTimestamp = Date.now(); + let bidder = 'rubicon'; + + let bidAdUnit = 'div_with_bid'; + let noBidAdUnit = 'div_no_bid'; + let bidAfterTimeoutAdUnit = 'div_after_timeout'; + + let auctionInit = { + timestamp: auctionStartTimestamp, + auctionId: auctionId, + timeout: timeout + }; + + let bidRequested = { + auctionId: auctionId, + auctionStart: auctionStartTimestamp, + bidderCode: bidder, + bidderRequestId: '10340af0c7dc72', + bids: [ + { + adUnitCode: bidAdUnit, + auctionId: auctionId, + bidId: '298bf14ecbafb', + bidder: bidder, + bidderRequestId: '10340af0c7dc72', + sizes: [[300, 250]], + startTime: auctionStartTimestamp + 50, + transactionId: '7aafa3ee-a80a-46d7-a4a0-cbcba463d97a' + }, + { + adUnitCode: bidAfterTimeoutAdUnit, + auctionId: auctionId, + bidId: '36c6375e2dceba', + bidder: bidder, + bidderRequestId: '10340af0c7dc72', + sizes: [[300, 250]], + startTime: auctionStartTimestamp + 70, + transactionId: 'cf627df3-5828-4d3e-9dd0-c1733d328142' + }, + { + adUnitCode: noBidAdUnit, + auctionId: auctionId, + bidId: '36c6375e2dce21', + bidder: bidder, + bidderRequestId: '10340af0c7dc72', + sizes: [[300, 250]], + startTime: auctionStartTimestamp + 90, + transactionId: 'cf627df3-5828-4d3e-9dd0-c1737aafa3ee' + } + ], + doneCbCallCount: 1, + start: auctionStartTimestamp, + timeout: timeout + }; + + let bidAdjustmentWithBid = { + ad: 'html', + adId: '298bf14ecbafb', + adUnitCode: bidAdUnit, + auctionId: auctionId, + bidder: bidder, + bidderCode: bidder, + cpm: 1.01, + creativeId: '2249:92806132', + currency: 'USD', + height: 250, + mediaType: 'banner', + requestId: '298bf14ecbafb', + requestTimestamp: auctionStartTimestamp + 50, + responseTimestamp: auctionStartTimestamp + 50 + 421, + size: '300x250', + source: 'client', + status: 'rendered', + statusMessage: 'Bid available', + timeToRespond: 421, + ttl: 300, + width: 300 + }; + + let bidAdjustmentAfterTimeout = { + ad: 'html', + adId: '36c6375e2dceba', + adUnitCode: bidAfterTimeoutAdUnit, + auctionId: auctionId, + bidder: bidder, + bidderCode: bidder, + cpm: 2.02, + creativeId: '2249:92806132', + currency: 'USD', + height: 250, + mediaType: 'banner', + requestId: '36c6375e2dceba', + requestTimestamp: auctionStartTimestamp + 70, + responseTimestamp: auctionStartTimestamp + 70 + 6141, + size: '300x250', + source: 'client', + status: 'rendered', + statusMessage: 'Bid available', + timeToRespond: 6141, + ttl: 300, + width: 300 + }; + + let bidAdjustmentNoBid = { + ad: 'html', + adId: '36c6375e2dce21', + adUnitCode: noBidAdUnit, + auctionId: auctionId, + bidder: bidder, + bidderCode: bidder, + cpm: 0, + creativeId: '2249:92806132', + currency: 'USD', + height: 0, + mediaType: 'banner', + requestId: '36c6375e2dce21', + requestTimestamp: auctionStartTimestamp + 90, + responseTimestamp: auctionStartTimestamp + 90 + 215, + size: '300x250', + source: 'client', + status: 'rendered', + statusMessage: 'Bid available', + timeToRespond: 215, + ttl: 300, + width: 0 + }; + + let auctionEnd = { + auctionId: auctionId + }; + + let bidTimeout = [ + { + adUnitCode: bidAfterTimeoutAdUnit, + auctionId: auctionId, + bidId: '389444beed7361', + bidder: bidder, + timeout: timeout + } + ]; + + let bidResponseWithBid = bidAdjustmentWithBid; + let bidResponseAfterTimeout = bidAdjustmentAfterTimeout; + let bidResponseNoBid = bidAdjustmentNoBid; + let bidderDone = bidRequested; + let bidWon = bidAdjustmentWithBid; + + before(function () { xhr = sinon.useFakeXMLHttpRequest(); - }) - after(() => { - roxotAnalytic.disableAnalytics(); + xhr.onCreate = request => requests.push(request); + }); + after(function () { xhr.restore(); }); - describe('enableAnalytics', function () { - beforeEach(() => { - sinon.spy(roxotAnalytic, 'track'); + describe('correct build and send events', function () { + beforeEach(function () { + requests = []; sinon.stub(events, 'getEvents').returns([]); }); - - afterEach(() => { - roxotAnalytic.track.restore(); + afterEach(function () { + roxotAnalytic.disableAnalytics(); events.getEvents.restore(); }); - it('should catch all events', function () { - adaptermanager.registerAnalyticsAdapter({ - code: 'roxot', - adapter: roxotAnalytic + it('should send prepared events to backend', function () { + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: { + publisherId: publisherId, + configServer: roxotConfigServerUrl, + server: roxotEventServerUrl + } }); - adaptermanager.enableAnalytics({ + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal('//' + roxotConfigServerUrl + '/c?publisherId=' + publisherId + '&host=localhost'); + requests[0].respond(200, {'Content-Type': 'application/json'}, '{"a": 1, "i": 1, "bat": 1}'); + + events.emit(constants.EVENTS.AUCTION_INIT, auctionInit); + events.emit(constants.EVENTS.BID_REQUESTED, bidRequested); + events.emit(constants.EVENTS.BID_ADJUSTMENT, bidAdjustmentWithBid); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponseWithBid); + events.emit(constants.EVENTS.BID_ADJUSTMENT, bidAdjustmentNoBid); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponseNoBid); + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + events.emit(constants.EVENTS.BID_ADJUSTMENT, bidAdjustmentAfterTimeout); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponseAfterTimeout); + events.emit(constants.EVENTS.BIDDER_DONE, bidderDone); + events.emit(constants.EVENTS.BID_WON, bidWon); + + expect(requests.length).to.equal(4); + + expect(requests[1].url).to.equal('//' + roxotEventServerUrl + '/a?publisherId=' + publisherId + '&host=localhost'); + expect(requests[2].url).to.equal('//' + roxotEventServerUrl + '/bat?publisherId=' + publisherId + '&host=localhost'); + expect(requests[3].url).to.equal('//' + roxotEventServerUrl + '/i?publisherId=' + publisherId + '&host=localhost'); + + let auction = JSON.parse(requests[1].requestBody); + expect(auction).to.include.all.keys('event', 'eventName', 'options', 'data'); + expect(auction.event).to.equal('a'); + + expect(auction.data).to.include.all.keys('id', 'start', 'finish', 'timeout', 'adUnits'); + expect(auction.data.id).to.equal(auctionId); + expect(auction.data.timeout).to.equal(timeout); + + expect(auction.data.adUnits).to.include.all.keys(bidAdUnit, bidAfterTimeoutAdUnit, noBidAdUnit); + expect(auction.data.adUnits[bidAdUnit].bidders).to.have.property(bidder); + expect(auction.data.adUnits[bidAfterTimeoutAdUnit].bidders).to.have.property(bidder); + expect(auction.data.adUnits[noBidAdUnit].bidders).to.have.property(bidder); + + expect(auction.data.adUnits[bidAdUnit].bidders[bidder].status).to.equal('bid'); + expect(auction.data.adUnits[bidAfterTimeoutAdUnit].bidders[bidder].status).to.equal('timeout'); + expect(auction.data.adUnits[noBidAdUnit].bidders[bidder].status).to.equal('noBid'); + + let bidAfterTimeout = JSON.parse(requests[2].requestBody); + expect(bidAfterTimeout).to.include.all.keys('event', 'eventName', 'options', 'data'); + expect(bidAfterTimeout.event).to.equal('bat'); + + expect(bidAfterTimeout.data).to.include.all.keys('start', 'finish', 'mediaType', 'adUnit', 'bidder', 'cpm', 'size', 'auction'); + expect(bidAfterTimeout.data.adUnit).to.equal(bidAfterTimeoutAdUnit); + expect(bidAfterTimeout.data.bidder).to.equal(bidder); + expect(bidAfterTimeout.data.cpm).to.equal(bidAdjustmentAfterTimeout.cpm); + + let impression = JSON.parse(requests[3].requestBody); + expect(impression).to.include.all.keys('event', 'eventName', 'options', 'data'); + expect(impression.event).to.equal('i'); + + expect(impression.data).to.include.all.keys('mediaType', 'adUnit', 'bidder', 'cpm', 'size', 'auction', 'isNew'); + expect(impression.data.adUnit).to.equal(bidAdUnit); + expect(impression.data.bidder).to.equal(bidder); + expect(impression.data.cpm).to.equal(bidAdjustmentWithBid.cpm); + }); + }); + + describe('support ad unit filter', function () { + beforeEach(function () { + requests = []; + sinon.stub(events, 'getEvents').returns([]); + }); + afterEach(function () { + roxotAnalytic.disableAnalytics(); + events.getEvents.restore(); + }); + it('should not send event for blocked ad unit', function () { + roxotAnalytic.enableAnalytics({ provider: 'roxot', options: { - publisherIds: ['test_roxot_prebid_analytid_publisher_id'] + publisherId: publisherId, + configServer: roxotConfigServerUrl, + server: roxotEventServerUrl, + adUnits: [noBidAdUnit, bidAfterTimeoutAdUnit] } }); - events.emit(constants.EVENTS.AUCTION_INIT, {}); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_REQUESTED, {}); - events.emit(constants.EVENTS.BID_RESPONSE, {}); - events.emit(constants.EVENTS.BID_WON, {}); + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal('//' + roxotConfigServerUrl + '/c?publisherId=' + publisherId + '&host=localhost'); + requests[0].respond(200, {'Content-Type': 'application/json'}, '{"a": 1, "i": 1, "bat": 1}'); + + events.emit(constants.EVENTS.AUCTION_INIT, auctionInit); + events.emit(constants.EVENTS.BID_REQUESTED, bidRequested); + events.emit(constants.EVENTS.BID_ADJUSTMENT, bidAdjustmentWithBid); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponseWithBid); + events.emit(constants.EVENTS.BID_ADJUSTMENT, bidAdjustmentNoBid); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponseNoBid); + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + events.emit(constants.EVENTS.BID_ADJUSTMENT, bidAdjustmentAfterTimeout); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponseAfterTimeout); + events.emit(constants.EVENTS.BIDDER_DONE, bidderDone); + events.emit(constants.EVENTS.BID_WON, bidWon); - sinon.assert.callCount(roxotAnalytic.track, 5); + expect(requests.length).to.equal(3); + + expect(requests[1].url).to.equal('//' + roxotEventServerUrl + '/a?publisherId=' + publisherId + '&host=localhost'); + expect(requests[2].url).to.equal('//' + roxotEventServerUrl + '/bat?publisherId=' + publisherId + '&host=localhost'); + + let auction = JSON.parse(requests[1].requestBody); + expect(auction.data.adUnits).to.include.all.keys(noBidAdUnit, bidAfterTimeoutAdUnit); + expect(auction.data.adUnits).to.not.include.all.keys(bidAdUnit); }); }); - describe('build utm tag data', () => { - beforeEach(() => { + + describe('should correct parse config', function () { + beforeEach(function () { + requests = []; + sinon.stub(events, 'getEvents').returns([]); + }); + + afterEach(function () { + roxotAnalytic.disableAnalytics(); + events.getEvents.restore(); + }); + + it('correct parse publisher config', function () { + let publisherOptions = { + publisherId: publisherId, + configServer: roxotConfigServerUrl, + server: roxotEventServerUrl, + anything: 'else', + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + expect(roxotAnalytic.getOptions().options).to.deep.equal(publisherOptions); + }); + + it('support deprecated options', function () { + let publisherOptions = { + publisherIds: [publisherId], + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + expect(roxotAnalytic.getOptions().options).to.deep.equal(publisherOptions); + expect(roxotAnalytic.getOptions().publisherId).to.equal(publisherId); + }); + + it('support default end-points', function () { + let publisherOptions = { + publisherId: publisherId, + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + expect(roxotAnalytic.getOptions().configServer).to.equal('pa.rxthdr.com/v3'); + expect(roxotAnalytic.getOptions().server).to.equal('pa.rxthdr.com/v3'); + }); + + it('support custom config end-point', function () { + let publisherOptions = { + publisherId: publisherId, + configServer: roxotConfigServerUrl + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + expect(roxotAnalytic.getOptions().configServer).to.equal(roxotConfigServerUrl); + expect(roxotAnalytic.getOptions().server).to.equal('pa.rxthdr.com/v3'); + }); + + it('support custom config and event end-point', function () { + let publisherOptions = { + publisherId: publisherId, + server: roxotEventServerUrl + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + expect(roxotAnalytic.getOptions().configServer).to.equal(roxotEventServerUrl); + expect(roxotAnalytic.getOptions().server).to.equal(roxotEventServerUrl); + }); + + it('support different config and event end-points', function () { + let publisherOptions = { + publisherId: publisherId, + configServer: roxotConfigServerUrl, + server: roxotEventServerUrl + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + expect(roxotAnalytic.getOptions().configServer).to.equal(roxotConfigServerUrl); + expect(roxotAnalytic.getOptions().server).to.equal(roxotEventServerUrl); + }); + + it('support adUnit filter', function () { + let publisherOptions = { + publisherId: publisherId, + adUnits: ['div1', 'div2'] + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + expect(roxotAnalytic.getOptions().adUnits).to.deep.equal(['div1', 'div2']); + }); + + it('support fail loading server config', function () { + let publisherOptions = { + publisherId: publisherId + }; + + roxotAnalytic.enableAnalytics({ + provider: 'roxot', + options: publisherOptions + }); + + requests[0].respond(500); + + expect(roxotAnalytic.getOptions().serverConfig).to.deep.equal({a: 1, i: 1, bat: 1, isError: 1}); + }); + }); + + describe('build utm tag data', function () { + beforeEach(function () { localStorage.setItem('roxot_analytics_utm_source', 'utm_source'); localStorage.setItem('roxot_analytics_utm_medium', 'utm_medium'); localStorage.setItem('roxot_analytics_utm_campaign', ''); localStorage.setItem('roxot_analytics_utm_term', ''); localStorage.setItem('roxot_analytics_utm_content', ''); - localStorage.setItem('roxot_analytics_utm_timeout', Date.now()); + localStorage.setItem('roxot_analytics_utm_ttl', Date.now()); + }); + afterEach(function () { + localStorage.removeItem('roxot_analytics_utm_source'); + localStorage.removeItem('roxot_analytics_utm_medium'); + localStorage.removeItem('roxot_analytics_utm_campaign'); + localStorage.removeItem('roxot_analytics_utm_term'); + localStorage.removeItem('roxot_analytics_utm_content'); + localStorage.removeItem('roxot_analytics_utm_ttl'); }); - it('should build utm data from local storage', () => { + it('should build utm data from local storage', function () { let utmTagData = roxotAnalytic.buildUtmTagData(); expect(utmTagData.utm_source).to.equal('utm_source'); expect(utmTagData.utm_medium).to.equal('utm_medium'); diff --git a/test/spec/modules/rtbdemandBidAdapter_spec.js b/test/spec/modules/rtbdemandBidAdapter_spec.js index 20d3e410aee..25178c21d88 100644 --- a/test/spec/modules/rtbdemandBidAdapter_spec.js +++ b/test/spec/modules/rtbdemandBidAdapter_spec.js @@ -2,16 +2,16 @@ import { expect } from 'chai'; import { spec } from 'modules/rtbdemandBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('rtbdemandAdapter', () => { +describe('rtbdemandAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'rtbdemand', 'params': { @@ -26,11 +26,11 @@ describe('rtbdemandAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return true when required params found', () => { + it('should return true when required params found', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -39,7 +39,7 @@ describe('rtbdemandAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -49,7 +49,7 @@ describe('rtbdemandAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidderRequest = { bidderCode: 'rtbdemand', auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', @@ -89,7 +89,7 @@ describe('rtbdemandAdapter', () => { timeout: 5000 }; - it('should add source and verison to the tag', () => { + it('should add source and verison to the tag', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const payload = request.data; expect(payload.from).to.exist; @@ -106,14 +106,14 @@ describe('rtbdemandAdapter', () => { expect(payload.tmax).to.exist; }); - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.url).to.equal('//bidding.rtbdemand.com/hb'); expect(request.method).to.equal('GET'); }); }) - describe('interpretResponse', () => { + describe('interpretResponse', function () { let response = { 'id': '543210', 'seatbid': [ { @@ -128,7 +128,7 @@ describe('rtbdemandAdapter', () => { } ] }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { let expectedResponse = [ { requestId: 'bidId-123456-1', @@ -147,7 +147,7 @@ describe('rtbdemandAdapter', () => { expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = { 'id': '543210', 'seatbid': [ ] @@ -158,10 +158,10 @@ describe('rtbdemandAdapter', () => { }); }); - describe('user sync', () => { + describe('user sync', function () { const syncUrl = '//bidding.rtbdemand.com/delivery/matches.php?type=iframe'; - it('should register the sync iframe', () => { + it('should register the sync iframe', function () { expect(spec.getUserSyncs({})).to.be.undefined; expect(spec.getUserSyncs({iframeEnabled: false})).to.be.undefined; const options = spec.getUserSyncs({iframeEnabled: true}); diff --git a/test/spec/modules/rtbdemandAdkBidAdapter_spec.js b/test/spec/modules/rtbdemandadkBidAdapter_spec.js similarity index 82% rename from test/spec/modules/rtbdemandAdkBidAdapter_spec.js rename to test/spec/modules/rtbdemandadkBidAdapter_spec.js index 2bd4ea4ce98..c1fbd35d6c9 100644 --- a/test/spec/modules/rtbdemandAdkBidAdapter_spec.js +++ b/test/spec/modules/rtbdemandadkBidAdapter_spec.js @@ -1,8 +1,8 @@ import {expect} from 'chai'; -import {spec} from 'modules/rtbdemandAdkBidAdapter'; +import {spec} from 'modules/rtbdemandadkBidAdapter'; import * as utils from 'src/utils'; -describe('RtbdemandAdk adapter', () => { +describe('rtbdemandadk adapter', function () { const bid1_zone1 = { bidder: 'rtbdemandadk', bidId: 'Bid_01', @@ -107,29 +107,29 @@ describe('RtbdemandAdk adapter', () => { } }; - describe('input parameters validation', () => { - it('empty request shouldn\'t generate exception', () => { + describe('input parameters validation', function () { + it('empty request shouldn\'t generate exception', function () { expect(spec.isBidRequestValid({ bidderCode: 'rtbdemandadk' })).to.be.equal(false); }); - it('request without zone shouldn\'t issue a request', () => { + it('request without zone shouldn\'t issue a request', function () { expect(spec.isBidRequestValid(bid_without_zone)).to.be.equal(false); }); - it('request without host shouldn\'t issue a request', () => { + it('request without host shouldn\'t issue a request', function () { expect(spec.isBidRequestValid(bid_without_host)).to.be.equal(false); }); - it('empty request shouldn\'t generate exception', () => { + it('empty request shouldn\'t generate exception', function () { expect(spec.isBidRequestValid(bid_with_wrong_zoneId)).to.be.equal(false); }); }); - describe('banner request building', () => { + describe('banner request building', function () { let bidRequest; - before(() => { + before(function () { let wmock = sinon.stub(utils, 'getTopWindowLocation').callsFake(() => ({ protocol: 'https:', hostname: 'example.com', @@ -144,33 +144,33 @@ describe('RtbdemandAdk adapter', () => { dntmock.restore(); }); - it('should be a first-price auction', () => { + it('should be a first-price auction', function () { expect(bidRequest).to.have.property('at', 1); }); - it('should have banner object', () => { + it('should have banner object', function () { expect(bidRequest.imp[0]).to.have.property('banner'); }); - it('should have w/h', () => { + it('should have w/h', function () { expect(bidRequest.imp[0].banner).to.have.property('format'); expect(bidRequest.imp[0].banner.format).to.be.eql([{w: 300, h: 250}, {w: 300, h: 200}]); }); - it('should respect secure connection', () => { + it('should respect secure connection', function () { expect(bidRequest.imp[0]).to.have.property('secure', 1); }); - it('should have tagid', () => { + it('should have tagid', function () { expect(bidRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); }); - it('should create proper site block', () => { + it('should create proper site block', function () { expect(bidRequest.site).to.have.property('domain', 'example.com'); expect(bidRequest.site).to.have.property('page', 'https://example.com/index.html'); }); - it('should fill device with caller macro', () => { + it('should fill device with caller macro', function () { expect(bidRequest).to.have.property('device'); expect(bidRequest.device).to.have.property('ip', 'caller'); expect(bidRequest.device).to.have.property('ua', 'caller'); @@ -178,37 +178,37 @@ describe('RtbdemandAdk adapter', () => { }); }); - describe('video request building', () => { + describe('video request building', function () { let bidRequest; - before(() => { + before(function () { let request = spec.buildRequests([bid_video])[0]; bidRequest = JSON.parse(request.data.r); }); - it('should have video object', () => { + it('should have video object', function () { expect(bidRequest.imp[0]).to.have.property('video'); }); - it('should have h/w', () => { + it('should have h/w', function () { expect(bidRequest.imp[0].video).to.have.property('w', 640); expect(bidRequest.imp[0].video).to.have.property('h', 480); }); - it('should have tagid', () => { + it('should have tagid', function () { expect(bidRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); }); }); - describe('requests routing', () => { - it('should issue a request for each host', () => { + describe('requests routing', function () { + it('should issue a request for each host', function () { let pbRequests = spec.buildRequests([bid1_zone1, bid3_host2]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.have.string(`//${bid1_zone1.params.host}/`); expect(pbRequests[1].url).to.have.string(`//${bid3_host2.params.host}/`); }); - it('should issue a request for each zone', () => { + it('should issue a request for each zone', function () { let pbRequests = spec.buildRequests([bid1_zone1, bid2_zone2]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].data.zone).to.be.equal(bid1_zone1.params.zoneId); @@ -216,8 +216,8 @@ describe('RtbdemandAdk adapter', () => { }); }); - describe('responses processing', () => { - it('should return fully-initialized banner bid-response', () => { + describe('responses processing', function () { + it('should return fully-initialized banner bid-response', function () { let request = spec.buildRequests([bid1_zone1])[0]; let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; expect(resp).to.have.property('requestId', 'Bid_01'); @@ -232,7 +232,7 @@ describe('RtbdemandAdk adapter', () => { expect(resp.ad).to.have.string(''); }); - it('should return fully-initialized video bid-response', () => { + it('should return fully-initialized video bid-response', function () { let request = spec.buildRequests([bid_video])[0]; let resp = spec.interpretResponse({body: videoBidResponse}, request)[0]; expect(resp).to.have.property('requestId', 'Bid_Video'); @@ -243,20 +243,20 @@ describe('RtbdemandAdk adapter', () => { expect(resp.height).to.equal(480); }); - it('should add nurl as pixel for banner response', () => { + it('should add nurl as pixel for banner response', function () { let request = spec.buildRequests([bid1_zone1])[0]; let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; let expectedNurl = bidResponse1.seatbid[0].bid[0].nurl + '&px=1'; expect(resp.ad).to.have.string(expectedNurl); }); - it('should handle bidresponse with user-sync only', () => { + it('should handle bidresponse with user-sync only', function () { let request = spec.buildRequests([bid1_zone1])[0]; let resp = spec.interpretResponse({body: usersyncOnlyResponse}, request); expect(resp).to.have.length(0); }); - it('should perform usersync', () => { + it('should perform usersync', function () { let syncs = spec.getUserSyncs({iframeEnabled: false}, [{body: bidResponse1}]); expect(syncs).to.have.length(0); syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: bidResponse1}]); diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index 69bd3f40f72..707a5f91bec 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -1,32 +1,17 @@ import { expect } from 'chai'; -import { spec } from 'modules/rtbhouseBidAdapter'; +import { OPENRTB, spec } from 'modules/rtbhouseBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -const REGIONS = ['prebid-eu', 'prebid-us', 'prebid-asia']; -const ENDPOINT_URL = 'creativecdn.com/bidder/prebid/bids'; - -/** - * Helpers - */ - -function buildEndpointUrl(region) { - return 'https://' + region + '.' + ENDPOINT_URL; -} - -/** - * endof Helpers - */ - describe('RTBHouseAdapter', () => { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'rtbhouse', 'params': { @@ -40,11 +25,11 @@ describe('RTBHouseAdapter', () => { 'auctionId': '1d1a030790a475' }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -54,52 +39,323 @@ describe('RTBHouseAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { - 'bidder': 'rtbhouse', - 'params': { - 'publisherId': 'PREBID_TEST', - 'region': 'prebid-eu', - 'test': 1 - }, + 'bidder': 'rtbhouse', + 'params': { + 'publisherId': 'PREBID_TEST', + 'region': 'prebid-eu', + 'test': 1 + }, 'adUnitCode': 'adunit-code', 'sizes': [[300, 250], [300, 600]], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' + 'auctionId': '1d1a030790a475', + 'transactionId': 'example-transaction-id', } ]; + const bidderRequest = { + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'http://example.com', + 'stack': ['http://example.com'] + } + }; it('should build test param into the request', () => { - let builtTestRequest = spec.buildRequests(bidRequests).data; - expect(JSON.parse(builtTestRequest).test).to.equal(1); + let builtTestRequest = spec.buildRequests(bidRequests, bidderRequest).data; + expect(JSON.parse(builtTestRequest).test).to.equal(1); }); - it('sends bid request to ENDPOINT via POST', () => { + it('should build valid OpenRTB banner object', () => { + const request = JSON.parse(spec.buildRequests(bidRequests, bidderRequest).data); + const imp = request.imp[0]; + expect(imp.banner).to.deep.equal({ + w: 300, + h: 250, + format: [{ + w: 300, + h: 250 + }, { + w: 300, + h: 600 + }] + }) + }); + + it('sends bid request to ENDPOINT via POST', function () { let bidRequest = Object.assign([], bidRequests); delete bidRequest[0].params.test; - - const request = spec.buildRequests(bidRequest); - expect(request.url).to.equal(buildEndpointUrl(bidRequest[0].params.region)); + const request = spec.buildRequests(bidRequest, bidderRequest); + expect(request.url).to.equal('https://prebid-eu.creativecdn.com/bidder/prebid/bids'); expect(request.method).to.equal('POST'); }); - }) - describe('interpretResponse', () => { + it('should not populate GDPR if for non-EEA users', function () { + let bidRequest = Object.assign([], bidRequests); + delete bidRequest[0].params.test; + const request = spec.buildRequests(bidRequest, bidderRequest); + let data = JSON.parse(request.data); + expect(data).to.not.have.property('regs'); + expect(data).to.not.have.property('user'); + }); + + it('should populate GDPR and consent string if available for EEA users', function () { + let bidRequest = Object.assign([], bidRequests); + delete bidRequest[0].params.test; + const request = spec.buildRequests( + bidRequest, + Object.assign({}, bidderRequest, { + gdprConsent: { + gdprApplies: true, + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' + } + }) + ); + let data = JSON.parse(request.data); + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.user.ext.consent).to.equal('BOJ8RZsOJ8RZsABAB8AAAAAZ-A'); + }); + + it('should populate GDPR and empty consent string if available for EEA users without consent string but with consent', function () { + let bidRequest = Object.assign([], bidRequests); + delete bidRequest[0].params.test; + const request = spec.buildRequests( + bidRequest, + Object.assign({}, bidderRequest, { + gdprConsent: { + gdprApplies: true + } + }) + ); + let data = JSON.parse(request.data); + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.user.ext.consent).to.equal(''); + }); + + it('should include banner imp in request', () => { + const bidRequest = Object.assign([], bidRequests); + const request = spec.buildRequests(bidRequest, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].banner).to.not.be.empty; + }); + + it('should include source.tid in request', () => { + const bidRequest = Object.assign([], bidRequests); + const request = spec.buildRequests(bidRequest, bidderRequest); + const data = JSON.parse(request.data); + expect(data.source.tid).to.equal('example-transaction-id'); + }); + + it('should include bidfloor in request if available', () => { + const bidRequest = Object.assign([], bidRequests); + bidRequest[0].params.bidfloor = 0.01; + const request = spec.buildRequests(bidRequest, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].bidfloor).to.equal(0.01) + }); + + describe('native imp', () => { + function basicRequest(extension) { + return Object.assign({ + bidder: 'bidder', + adUnitCode: 'adunit-code', + bidId: '1', + params: { + publisherId: 'PREBID_TEST', + region: 'prebid-eu', + test: 1 + } + }, extension); + } + + function buildImp(request) { + const resultRequest = spec.buildRequests([request], bidderRequest); + return JSON.parse(resultRequest.data).imp[0]; + } + + it('should extract native params when single mediaType', () => { + const imp = buildImp(basicRequest({ + mediaType: 'native', + nativeParams: { + title: { + required: true, + len: 100 + } + } + })); + expect(imp.native.request.assets[0]).to.deep.equal({ + id: OPENRTB.NATIVE.ASSET_ID.TITLE, + required: 1, + title: { + len: 100 + } + }) + }); + + it('should extract native params when many mediaTypes', () => { + const imp = buildImp(basicRequest({ + mediaTypes: { + native: { + title: { + len: 100 + } + } + } + })); + expect(imp.native.request.assets[0]).to.deep.equal({ + id: OPENRTB.NATIVE.ASSET_ID.TITLE, + required: 0, + title: { + len: 100 + } + }) + }); + + it('should not contain banner in imp', () => { + const imp = buildImp(basicRequest({ + mediaTypes: { + native: { + title: { + required: true + } + } + } + })); + expect(imp.banner).to.be.undefined; + }); + + describe('image sizes', () => { + it('should parse single image size', () => { + const imp = buildImp(basicRequest({ + mediaTypes: { + native: { + image: { + sizes: [300, 250] + } + } + } + })); + expect(imp.native.request.assets[0]).to.deep.equal({ + id: OPENRTB.NATIVE.ASSET_ID.IMAGE, + required: 0, + img: { + w: 300, + h: 250, + type: OPENRTB.NATIVE.IMAGE_TYPE.MAIN, + } + }) + }); + + it('should parse multiple image sizes', () => { + const imp = buildImp(basicRequest({ + mediaTypes: { + native: { + image: { + sizes: [[300, 250], [100, 100]] + } + } + } + })); + expect(imp.native.request.assets[0]).to.deep.equal({ + id: OPENRTB.NATIVE.ASSET_ID.IMAGE, + required: 0, + img: { + w: 300, + h: 250, + type: OPENRTB.NATIVE.IMAGE_TYPE.MAIN, + } + }) + }) + }); + + it('should parse aspect ratios with min_width', () => { + const imp = buildImp(basicRequest({ + mediaTypes: { + native: { + icon: { + aspect_ratios: [{ + min_width: 300, + ratio_width: 2, + ratio_height: 3, + }] + } + } + } + })); + expect(imp.native.request.assets[0]).to.deep.equal({ + id: OPENRTB.NATIVE.ASSET_ID.ICON, + required: 0, + img: { + type: OPENRTB.NATIVE.IMAGE_TYPE.ICON, + wmin: 300, + hmin: 450, + } + }) + }); + + it('should parse aspect ratios without min_width', () => { + const imp = buildImp(basicRequest({ + mediaTypes: { + native: { + icon: { + aspect_ratios: [{ + ratio_width: 2, + ratio_height: 3, + }] + } + } + } + })); + expect(imp.native.request.assets[0]).to.deep.equal({ + id: OPENRTB.NATIVE.ASSET_ID.ICON, + required: 0, + img: { + type: OPENRTB.NATIVE.IMAGE_TYPE.ICON, + wmin: 100, + hmin: 150, + } + }) + }); + + it('should handle all native assets', () => { + const imp = buildImp(basicRequest({ + mediaTypes: { + native: { + title: {}, + image: {}, + icon: {}, + sponsoredBy: {}, + body: {}, + cta: {}, + } + } + })); + expect(imp.native.request.assets.length).to.equal(6); + imp.native.request.assets.forEach(asset => { + expect(asset.id).to.be.at.least(1) + }) + }); + }); + }); + + describe('interpretResponse', function () { let response = [{ - 'id': 'bidder_imp_identifier', - 'impid': '552b8922e28f27', - 'price': 0.5, - 'adid': 'Ad_Identifier', - 'adm': '', - 'adomain': ['rtbhouse.com'], - 'cid': 'Ad_Identifier', - 'w': 300, - 'h': 250 + 'id': 'bidder_imp_identifier', + 'impid': '552b8922e28f27', + 'price': 0.5, + 'adid': 'Ad_Identifier', + 'adm': '', + 'adomain': ['rtbhouse.com'], + 'cid': 'Ad_Identifier', + 'w': 300, + 'h': 250 }]; - it('should get correct bid response', () => { + it('should get correct bid response', function () { let expectedResponse = [ { 'requestId': '552b8922e28f27', @@ -115,15 +371,76 @@ describe('RTBHouseAdapter', () => { } ]; let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}); + let result = spec.interpretResponse({body: response}, {bidderRequest}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = ''; let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}); + let result = spec.interpretResponse({body: response}, {bidderRequest}); expect(result.length).to.equal(0); }); + + describe('native', () => { + const adm = { + native: { + ver: 1.1, + link: { + url: 'http://example.com' + }, + imptrackers: [ + 'http://example.com/imptracker' + ], + assets: [{ + id: OPENRTB.NATIVE.ASSET_ID.TITLE, + required: 1, + title: { + text: 'Title text' + } + }, { + id: OPENRTB.NATIVE.ASSET_ID.IMAGE, + required: 1, + img: { + url: 'http://example.com/image.jpg', + w: 150, + h: 50 + } + }, { + id: OPENRTB.NATIVE.ASSET_ID.BODY, + required: 0, + data: { + value: 'Body text' + } + }], + } + }; + const response = [{ + 'id': 'id', + 'impid': 'impid', + 'price': 1, + 'adid': 'adid', + 'adm': JSON.stringify(adm), + 'adomain': ['rtbhouse.com'], + 'cid': 'cid', + 'w': 1, + 'h': 1 + }]; + + it('should contain native assets in valid format', () => { + const bids = spec.interpretResponse({body: response}, {}); + expect(bids[0].native).to.deep.equal({ + title: 'Title text', + clickUrl: encodeURIComponent('http://example.com'), + impressionTrackers: ['http://example.com/imptracker'], + image: { + url: encodeURIComponent('http://example.com/image.jpg'), + width: 150, + height: 50 + }, + body: 'Body text' + }); + }); + }); }); }); diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 38f8b726cd1..3452ae747b9 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -1,8 +1,12 @@ -import adaptermanager from 'src/adaptermanager'; -import rubiconAnalyticsAdapter, { SEND_TIMEOUT } from 'modules/rubiconAnalyticsAdapter'; +import rubiconAnalyticsAdapter, { SEND_TIMEOUT, parseBidResponse } from 'modules/rubiconAnalyticsAdapter'; import CONSTANTS from 'src/constants.json'; import { config } from 'src/config'; +import { + setConfig, + addBidResponseHook, +} from 'modules/currency'; + let Ajv = require('ajv'); let schema = require('./rubiconAnalyticsSchema.json'); let ajv = new Ajv({ @@ -27,6 +31,7 @@ const { AUCTION_END, BID_REQUESTED, BID_RESPONSE, + BIDDER_DONE, BID_WON, BID_TIMEOUT, SET_TARGETING @@ -39,7 +44,8 @@ const BID = { 'height': 480, 'mediaType': 'video', 'statusMessage': 'Bid available', - 'adId': '2ecff0db240757', + 'bidId': '2ecff0db240757', + 'adId': 'fake_ad_id', 'source': 'client', 'requestId': '2ecff0db240757', 'currency': 'USD', @@ -78,14 +84,14 @@ const BID = { const BID2 = Object.assign({}, BID, { adUnitCode: '/19968336/header-bid-tag1', - adId: '3bd4ebb1c900e2', + bidId: '3bd4ebb1c900e2', + adId: 'fake_ad_id', requestId: '3bd4ebb1c900e2', width: 728, height: 90, mediaType: 'banner', cpm: 1.52, source: 'server', - serverResponseTimeMs: 42, rubiconTargeting: { 'rpfl_elemid': '/19968336/header-bid-tag1', 'rpfl_14062': '2_tier0100' @@ -105,9 +111,58 @@ const MOCK = { [BID2.adUnitCode]: BID2.adserverTargeting }, AUCTION_INIT: { - 'timestamp': 1519767010567, 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'timeout': 3000 + 'timestamp': 1519767010567, + 'auctionStatus': 'inProgress', + 'adUnits': [ { + 'code': '/19968336/header-bid-tag1', + 'sizes': [[640, 480]], + 'bids': [ { + 'bidder': 'rubicon', + 'params': { + 'accountId': 1001, 'siteId': 113932, 'zoneId': 535512 + } + } ], + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' + } + ], + 'adUnitCodes': ['/19968336/header-bid-tag1'], + 'bidderRequests': [ { + 'bidderCode': 'rubicon', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ { + 'bidder': 'rubicon', + 'params': { + 'accountId': 1001, 'siteId': 113932, 'zoneId': 535512 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 480]] + } + }, + 'adUnitCode': '/19968336/header-bid-tag1', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'src': 'client', + 'bidRequestsCount': 1 + } + ], + 'timeout': 3000, + 'refererInfo': { + 'referer': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] + } + } + ], + 'bidsReceived': [], + 'winningBids': [], + 'timeout': 3000, + 'config': { + 'accountId': 1001, 'endpoint': '//localhost:9999/event' + } }, BID_REQUESTED: { 'bidder': 'rubicon', @@ -117,7 +172,7 @@ const MOCK = { { 'bidder': 'rubicon', 'params': { - 'accountId': '14062', + 'accountId': '1001', 'siteId': '70608', 'zoneId': '335918', 'userId': '12346', @@ -190,6 +245,15 @@ const MOCK = { 'status': 'rendered' }) ], + BIDDER_DONE: { + 'bidderCode': 'rubicon', + 'bids': [ + BID, + Object.assign({}, BID2, { + 'serverResponseTimeMs': 42, + }) + ] + }, BID_TIMEOUT: [ { 'bidId': '2ecff0db240757', @@ -226,6 +290,9 @@ const ANALYTICS_MESSAGE = { } ], 'status': 'success', + 'accountId': 1001, + 'siteId': 70608, + 'zoneId': 335918, 'adserverTargeting': { 'hb_bidder': 'rubicon', 'hb_adid': '2ecff0db240757', @@ -241,7 +308,7 @@ const ANALYTICS_MESSAGE = { 'source': 'client', 'clientLatencyMillis': 3214, 'params': { - 'accountId': '14062', + 'accountId': '1001', 'siteId': '70608', 'zoneId': '335918' }, @@ -322,8 +389,10 @@ const ANALYTICS_MESSAGE = { 'clientLatencyMillis': 3214, 'samplingFactor': 1, 'accountId': 1001, + 'siteId': 70608, + 'zoneId': 335918, 'params': { - 'accountId': '14062', + 'accountId': '1001', 'siteId': '70608', 'zoneId': '335918' }, @@ -384,7 +453,8 @@ const ANALYTICS_MESSAGE = { }, 'bidwonStatus': 'success' } - ] + ], + 'wrapperName': '10000_fakewrapper_test' }; function performStandardAuction() { @@ -392,20 +462,21 @@ function performStandardAuction() { events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); events.emit(AUCTION_END, MOCK.AUCTION_END); events.emit(SET_TARGETING, MOCK.SET_TARGETING); events.emit(BID_WON, MOCK.BID_WON[0]); events.emit(BID_WON, MOCK.BID_WON[1]); } -describe('rubicon analytics adapter', () => { +describe('rubicon analytics adapter', function () { let sandbox; let xhr; let requests; let oldScreen; let clock; - beforeEach(() => { + beforeEach(function () { sandbox = sinon.sandbox.create(); xhr = sandbox.useFakeXMLHttpRequest(); @@ -422,16 +493,19 @@ describe('rubicon analytics adapter', () => { s2sConfig: { timeout: 1000, accountId: 10000, + }, + rubicon: { + wrapperName: '10000_fakewrapper_test' } }) }); - afterEach(() => { + afterEach(function () { sandbox.restore(); config.resetConfig(); }); - it('should require accountId', () => { + it('should require accountId', function () { sandbox.stub(utils, 'logError'); rubiconAnalyticsAdapter.enableAnalytics({ @@ -443,7 +517,7 @@ describe('rubicon analytics adapter', () => { expect(utils.logError.called).to.equal(true); }); - it('should require endpoint', () => { + it('should require endpoint', function () { sandbox.stub(utils, 'logError'); rubiconAnalyticsAdapter.enableAnalytics({ @@ -455,18 +529,18 @@ describe('rubicon analytics adapter', () => { expect(utils.logError.called).to.equal(true); }); - describe('sampling', () => { - beforeEach(() => { + describe('sampling', function () { + beforeEach(function () { sandbox.stub(Math, 'random').returns(0.08); sandbox.stub(utils, 'logError'); }); - afterEach(() => { + afterEach(function () { rubiconAnalyticsAdapter.disableAnalytics(); }); - describe('with options.samplingFactor', () => { - it('should sample', () => { + describe('with options.samplingFactor', function () { + it('should sample', function () { rubiconAnalyticsAdapter.enableAnalytics({ options: { endpoint: '//localhost:9999/event', @@ -480,7 +554,7 @@ describe('rubicon analytics adapter', () => { expect(requests.length).to.equal(1); }); - it('should unsample', () => { + it('should unsample', function () { rubiconAnalyticsAdapter.enableAnalytics({ options: { endpoint: '//localhost:9999/event', @@ -494,7 +568,7 @@ describe('rubicon analytics adapter', () => { expect(requests.length).to.equal(0); }); - it('should throw errors for invalid samplingFactor', () => { + it('should throw errors for invalid samplingFactor', function () { rubiconAnalyticsAdapter.enableAnalytics({ options: { endpoint: '//localhost:9999/event', @@ -509,8 +583,8 @@ describe('rubicon analytics adapter', () => { expect(utils.logError.called).to.equal(true); }); }); - describe('with options.sampling', () => { - it('should sample', () => { + describe('with options.sampling', function () { + it('should sample', function () { rubiconAnalyticsAdapter.enableAnalytics({ options: { endpoint: '//localhost:9999/event', @@ -524,7 +598,7 @@ describe('rubicon analytics adapter', () => { expect(requests.length).to.equal(1); }); - it('should unsample', () => { + it('should unsample', function () { rubiconAnalyticsAdapter.enableAnalytics({ options: { endpoint: '//localhost:9999/event', @@ -538,7 +612,7 @@ describe('rubicon analytics adapter', () => { expect(requests.length).to.equal(0); }); - it('should throw errors for invalid samplingFactor', () => { + it('should throw errors for invalid samplingFactor', function () { rubiconAnalyticsAdapter.enableAnalytics({ options: { endpoint: '//localhost:9999/event', @@ -555,21 +629,21 @@ describe('rubicon analytics adapter', () => { }); }); - describe('when handling events', () => { - beforeEach(() => { + describe('when handling events', function () { + beforeEach(function () { rubiconAnalyticsAdapter.enableAnalytics({ options: { endpoint: '//localhost:9999/event', - accountId: '1001' + accountId: 1001 } }); }); - afterEach(() => { + afterEach(function () { rubiconAnalyticsAdapter.disableAnalytics(); }); - it('should build a batched message from prebid events', () => { + it('should build a batched message from prebid events', function () { performStandardAuction(); expect(requests.length).to.equal(1); @@ -583,11 +657,62 @@ describe('rubicon analytics adapter', () => { expect(message).to.deep.equal(ANALYTICS_MESSAGE); }); - it('should send batched message without BID_WON if necessary and further BID_WON events individually', () => { + it('should pick the highest cpm bid if more than one bid per bidRequestId', function () { + // Only want one bid request in our mock auction + let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); + bidRequested.bids.shift(); + let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); + auctionInit.adUnits.shift(); + + // clone the mock bidResponse and duplicate + let duplicateResponse1 = utils.deepClone(BID2); + duplicateResponse1.cpm = 1.0; + duplicateResponse1.adserverTargeting.hb_pb = '1.0'; + duplicateResponse1.adserverTargeting.hb_adid = '1111'; + let duplicateResponse2 = utils.deepClone(BID2); + duplicateResponse2.cpm = 5.5; + duplicateResponse2.adserverTargeting.hb_pb = '5.5'; + duplicateResponse2.adserverTargeting.hb_adid = '5555'; + let duplicateResponse3 = utils.deepClone(BID2); + duplicateResponse3.cpm = 0.1; + duplicateResponse3.adserverTargeting.hb_pb = '0.1'; + duplicateResponse3.adserverTargeting.hb_adid = '3333'; + + const setTargeting = { + [duplicateResponse2.adUnitCode]: duplicateResponse2.adserverTargeting + }; + + const bidWon = Object.assign({}, duplicateResponse2, { + 'status': 'rendered' + }); + + // spoof the auction with just our duplicates + events.emit(AUCTION_INIT, auctionInit); + events.emit(BID_REQUESTED, bidRequested); + events.emit(BID_RESPONSE, duplicateResponse1); + events.emit(BID_RESPONSE, duplicateResponse2); + events.emit(BID_RESPONSE, duplicateResponse3); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, setTargeting); + events.emit(BID_WON, bidWon); + + let message = JSON.parse(requests[0].requestBody); + validate(message); + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(5.5); + expect(message.auctions[0].adUnits[0].adserverTargeting.hb_pb).to.equal('5.5'); + expect(message.auctions[0].adUnits[0].adserverTargeting.hb_adid).to.equal('5555'); + expect(message.bidsWon.length).to.equal(1); + expect(message.bidsWon[0].bidResponse.bidPriceUSD).to.equal(5.5); + expect(message.bidsWon[0].adserverTargeting.hb_pb).to.equal('5.5'); + expect(message.bidsWon[0].adserverTargeting.hb_adid).to.equal('5555'); + }); + + it('should send batched message without BID_WON if necessary and further BID_WON events individually', function () { events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); events.emit(AUCTION_END, MOCK.AUCTION_END); events.emit(SET_TARGETING, MOCK.SET_TARGETING); events.emit(BID_WON, MOCK.BID_WON[0]); @@ -611,7 +736,7 @@ describe('rubicon analytics adapter', () => { expect(message.bidsWon[0]).to.deep.equal(ANALYTICS_MESSAGE.bidsWon[1]); }); - it('should properly mark bids as timed out', () => { + it('should properly mark bids as timed out', function () { events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); @@ -628,5 +753,60 @@ describe('rubicon analytics adapter', () => { expect(timedOutBid.error.code).to.equal('timeout-error'); expect(timedOutBid).to.not.have.property('bidResponse'); }); + + it('should successfully convert bid price to USD in parseBidResponse', function () { + // Set the rates + setConfig({ + adServerCurrency: 'JPY', + rates: { + USD: { + JPY: 100 + } + } + }); + + // set our bid response to JPY + const bidCopy = utils.deepClone(BID2); + bidCopy.currency = 'JPY'; + bidCopy.cpm = 100; + + // Now add the bidResponse hook which hooks on the currenct conversion function onto the bid response + let innerBid; + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bidCopy); + + // Use the rubi analytics parseBidResponse Function to get the resulting cpm from the bid response! + const bidResponseObj = parseBidResponse(innerBid); + expect(bidResponseObj).to.have.property('bidPriceUSD'); + expect(bidResponseObj.bidPriceUSD).to.equal(1.0); + }); + }); + + describe('config with integration type', () => { + it('should use the integration type provided in the config instead of the default', () => { + sandbox.stub(config, 'getConfig').callsFake(function (key) { + const config = { + 'rubicon.int_type': 'testType' + }; + return config[key]; + }); + + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(1); + const request = requests[0]; + const message = JSON.parse(request.requestBody); + expect(message.integration).to.equal('testType'); + + rubiconAnalyticsAdapter.disableAnalytics(); + }); }); }); diff --git a/test/spec/modules/rubiconAnalyticsSchema.json b/test/spec/modules/rubiconAnalyticsSchema.json index cc4ad20db19..686aced840f 100644 --- a/test/spec/modules/rubiconAnalyticsSchema.json +++ b/test/spec/modules/rubiconAnalyticsSchema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "title": "Prebid Auctions", "description": "A batched data object describing the lifecycle of an auction or multiple auction across a single page view.", "type": "object", @@ -113,6 +113,18 @@ "items": { "$ref": "#/definitions/bid" } + }, + "accountId": { + "type": "number", + "description": "The Rubicon AccountId associated with this adUnit - Removed if null" + }, + "siteId": { + "type": "number", + "description": "The Rubicon siteId associated with this adUnit - Removed if null" + }, + "zoneId": { + "type": "number", + "description": "The Rubicon zoneId associated with this adUnit - Removed if null" } } } @@ -166,11 +178,22 @@ "success", "error" ] + }, + "siteId": { + "type": "number", + "description": "The Rubicon siteId associated with this adUnit - Removed if null" + }, + "zoneId": { + "type": "number", + "description": "The Rubicon zoneId associated with this adUnit - Removed if null" } } } ] } + }, + "wrapperName": { + "type": "string" } }, "definitions": { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index f0ffee12806..8d65e1e97b4 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1,19 +1,131 @@ import {expect} from 'chai'; -import adapterManager from 'src/adaptermanager'; -import {spec, masSizeOrdering, resetUserSync} from 'modules/rubiconBidAdapter'; +import adapterManager from 'src/adapterManager'; +import {spec, getPriceGranularity, masSizeOrdering, resetUserSync, hasVideoMediaType, FASTLANE_ENDPOINT} from 'modules/rubiconBidAdapter'; import {parse as parseQuery} from 'querystring'; import {newBidder} from 'src/adapters/bidderFactory'; import {userSync} from 'src/userSync'; import {config} from 'src/config'; import * as utils from 'src/utils'; +import find from 'core-js/library/fn/array/find'; var CONSTANTS = require('src/constants.json'); const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid -describe('the rubicon adapter', () => { +describe('the rubicon adapter', function () { let sandbox, - bidderRequest; + bidderRequest, + sizeMap; + + /** + * @typedef {Object} sizeMapConverted + * @property {string} sizeId + * @property {string} size + * @property {Array.} sizeAsArray + * @property {number} width + * @property {number} height + */ + + /** + * @param {Array.} sizesMapConverted + * @param {Object} bid + * @return {sizeMapConverted} + */ + function getSizeIdForBid(sizesMapConverted, bid) { + return find(sizesMapConverted, item => (item.width === bid.width && item.height === bid.height)); + } + + /** + * @param {Array.} ads + * @param {sizeMapConverted} size + * @return {Object} + */ + function getResponseAdBySize(ads, size) { + return find(ads, item => item.size_id === size.sizeId); + } + + /** + * @param {Array.} bidRequests + * @param {sizeMapConverted} size + * @return {BidRequest} + */ + function getBidRequestBySize(bidRequests, size) { + return find(bidRequests, item => item.sizes[0][0] === size.width && item.sizes[0][1] === size.height); + } + + /** + * @typedef {Object} overrideProps + * @property {string} status + * @property {number} cpm + * @property {number} zone_id + * @property {number} ad_id + * @property {string} creative_id + * @property {string} targeting_key - rpfl_{id} + */ + /** + * @param {number} i - index + * @param {string} sizeId - id that maps to size + * @param {Array.} [indexOverMap] + * @return {{status: string, cpm: number, zone_id: *, size_id: *, impression_id: *, ad_id: *, creative_id: string, type: string, targeting: *[]}} + */ + function createResponseAdByIndex(i, sizeId, indexOverMap) { + const overridePropMap = (indexOverMap && indexOverMap[i] && typeof indexOverMap[i] === 'object') ? indexOverMap[i] : {}; + const overrideProps = Object.keys(overridePropMap).reduce((aggregate, key) => { + aggregate[key] = overridePropMap[key]; + return aggregate; + }, {}); + + const getProp = (propName, defaultValue) => { + return (overrideProps[propName]) ? overridePropMap[propName] : defaultValue; + }; + + return { + 'status': getProp('status', 'ok'), + 'cpm': getProp('cpm', i / 100), + 'zone_id': getProp('zone_id', i + 1), + 'size_id': sizeId, + 'impression_id': getProp('impression_id', `1-${i}`), + 'ad_id': getProp('ad_id', i + 1), + 'advertiser': i + 1, + 'network': i + 1, + 'creative_id': getProp('creative_id', `crid-${i}`), + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': i + 1, + 'targeting': [ + { + 'key': getProp('targeting_key', `rpfl_${i}`), + 'values': [ '43_tier_all_test' ] + } + ] + }; + } + + /** + * @param {number} i + * @param {Array.} size + * @return {{ params: {accountId: string, siteId: string, zoneId: string }, adUnitCode: string, code: string, sizes: *[], bidId: string, bidderRequestId: string }} + */ + function createBidRequestByIndex(i, size) { + return { + bidder: 'rubicon', + params: { + accountId: '14062', + siteId: '70608', + zoneId: (i + 1).toString(), + userId: '12346', + position: 'atf', + referrer: 'localhost' + }, + adUnitCode: `/19968336/header-bid-tag-${i}`, + code: `div-${i}`, + sizes: [size], + bidId: i.toString(), + bidderRequestId: i.toString(), + auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + }; + } /** * @param {boolean} [gdprApplies] @@ -37,44 +149,31 @@ describe('the rubicon adapter', () => { let bid = bidderRequest.bids[0]; bid.mediaTypes = { video: { - context: 'instream' + context: 'instream', + mimes: ['video/mp4', 'video/x-flv'], + api: [2], + minduration: 15, + playerSize: [640, 480], + maxduration: 30, + startdelay: 0, + playbackmethod: [2], + linearity: 1, + skip: 1, + skipafter: 15, + pos: 1, + protocols: [1, 2, 3, 4, 5, 6] } }; bid.params.video = { 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'playerHeight': 320, + 'skip': 1, + 'skipafter': 15, + 'playerHeight': 480, 'playerWidth': 640, 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } }; } - function createLegacyVideoBidderRequest() { - createGdprBidderRequest(true); - - let bid = bidderRequest.bids[0]; - // Legacy property (Prebid <1.0) - bid.mediaType = 'video'; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'playerHeight': 320, - 'playerWidth': 640, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - bid.params.secure = false; - } - function createVideoBidderRequestNoVideo() { let bid = bidderRequest.bids[0]; bid.mediaTypes = { @@ -85,68 +184,39 @@ describe('the rubicon adapter', () => { bid.params.video = ''; } - function createLegacyVideoBidderRequestNoVideo() { - let bid = bidderRequest.bids[0]; - bid.mediaType = 'video'; - bid.params.video = ''; - } - function createVideoBidderRequestOutstream() { let bid = bidderRequest.bids[0]; bid.mediaTypes = { video: { - context: 'outstream' + context: 'outstream', + mimes: ['video/mp4', 'video/x-flv'], + api: [2], + minduration: 15, + playerSize: [640, 480], + maxduration: 30, + startdelay: 0, + playbackmethod: [2], + linearity: 1, + skip: 1, + skipafter: 15, + pos: 1, + protocols: [1, 2, 3, 4, 5, 6] }, }; + bid.params.accountId = 14062; + bid.params.siteId = 70608; + bid.params.zoneId = 335918; bid.params.video = { 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, + 'skip': 1, + 'skipafter': 15, 'playerHeight': 320, 'playerWidth': 640, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - } - - function createVideoBidderRequestNoPlayer() { - let bid = bidderRequest.bids[0]; - bid.mediaTypes = { - video: { - context: 'instream' - }, - }; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - } - - function createLegacyVideoBidderRequestNoPlayer() { - let bid = bidderRequest.bids[0]; - bid.mediaType = 'video'; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } + 'size_id': 203 }; } - beforeEach(() => { + beforeEach(function () { sandbox = sinon.sandbox.create(); bidderRequest = { @@ -163,12 +233,13 @@ describe('the rubicon adapter', () => { userId: '12346', keywords: ['a', 'b', 'c'], inventory: { - rating: '5-star', - prodtype: 'tech' + rating: '5-star', // This actually should not be sent to frank!! causes 400 + prodtype: ['tech', 'mobile'] }, visitor: { ucat: 'new', - lastsearch: 'iphone' + lastsearch: 'iphone', + likes: ['sports', 'video games'] }, position: 'atf', referrer: 'localhost', @@ -187,19 +258,45 @@ describe('the rubicon adapter', () => { auctionStart: 1472239426000, timeout: 5000 }; + + sizeMap = [ + {sizeId: 1, size: '468x60'}, + {sizeId: 2, size: '728x90'}, + {sizeId: 5, size: '120x90'}, + {sizeId: 8, size: '120x600'}, + {sizeId: 9, size: '160x600'}, + {sizeId: 10, size: '300x600'}, + {sizeId: 13, size: '200x200'}, + {sizeId: 14, size: '250x250'}, + {sizeId: 15, size: '300x250'}, + {sizeId: 16, size: '336x280'}, + {sizeId: 19, size: '300x100'}, + {sizeId: 31, size: '980x120'}, + {sizeId: 32, size: '250x360'} + // Create convenience properties for [sizeAsArray, width, height] by parsing the size string + ].map(item => { + const sizeAsArray = item.size.split('x').map(s => parseInt(s)); + return { + sizeId: item.sizeId, + size: item.size, + sizeAsArray: sizeAsArray.slice(), + width: sizeAsArray[0], + height: sizeAsArray[1] + }; + }); }); - afterEach(() => { + afterEach(function () { sandbox.restore(); }); - describe('MAS mapping / ordering', () => { - it('should sort values without any MAS priority sizes in regular ascending order', () => { + describe('MAS mapping / ordering', function () { + it('should sort values without any MAS priority sizes in regular ascending order', function () { let ordering = masSizeOrdering([126, 43, 65, 16]); expect(ordering).to.deep.equal([16, 43, 65, 126]); }); - it('should sort MAS priority sizes in the proper order w/ rest ascending', () => { + it('should sort MAS priority sizes in the proper order w/ rest ascending', function () { let ordering = masSizeOrdering([43, 9, 65, 15, 16, 126]); expect(ordering).to.deep.equal([15, 9, 16, 43, 65, 126]); @@ -211,10 +308,10 @@ describe('the rubicon adapter', () => { }); }); - describe('buildRequests implementation', () => { - describe('for requests', () => { - describe('to fastlane', () => { - it('should make a well-formed request objects', () => { + describe('buildRequests implementation', function () { + describe('for requests', function () { + describe('to fastlane', function () { + it('should make a well-formed request objects', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -238,11 +335,11 @@ describe('the rubicon adapter', () => { 'kw': 'a,b,c', 'tg_v.ucat': 'new', 'tg_v.lastsearch': 'iphone', + 'tg_v.likes': 'sports,video games', 'tg_i.rating': '5-star', - 'tg_i.prodtype': 'tech', - 'rf': 'localhost', - 'p_geo.latitude': '40.7608', - 'p_geo.longitude': '111.8910' + 'tg_i.prodtype': 'tech,mobile', + 'tg_fl.eid': 'div-1', + 'rf': 'localhost' }; // test that all values above are both present and correct @@ -256,7 +353,40 @@ describe('the rubicon adapter', () => { }); }); - it('should make a well-formed request object without latLong', () => { + it('should not send p_pos to AE if not params.position specified', function() { + var noposRequest = utils.deepClone(bidderRequest); + delete noposRequest.bids[0].params.position; + + let [request] = spec.buildRequests(noposRequest.bids, noposRequest); + let data = parseQuery(request.data); + + expect(data['site_id']).to.equal('70608'); + expect(data['p_pos']).to.equal(undefined); + }); + + it('should not send p_pos to AE if not params.position is invalid', function() { + var badposRequest = utils.deepClone(bidderRequest); + badposRequest.bids[0].params.position = 'bad'; + + let [request] = spec.buildRequests(badposRequest.bids, badposRequest); + let data = parseQuery(request.data); + + expect(data['site_id']).to.equal('70608'); + expect(data['p_pos']).to.equal(undefined); + }); + + it('ad engine query params should be ordered correctly', function () { + sandbox.stub(Math, 'random').callsFake(() => 0.1); + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + + const referenceOrdering = ['account_id', 'site_id', 'zone_id', 'size_id', 'alt_size_ids', 'p_pos', 'rf', 'p_geo.latitude', 'p_geo.longitude', 'kw', 'tg_v.ucat', 'tg_v.lastsearch', 'tg_v.likes', 'tg_i.rating', 'tg_i.prodtype', 'tk_flint', 'x_source.tid', 'p_screen_res', 'rp_floor', 'rp_secure', 'tk_user_key', 'tg_fl.eid', 'slots', 'rand']; + + request.data.split('&').forEach((item, i) => { + expect(item.split('=')[0]).to.equal(referenceOrdering[i]); + }); + }); + + it('should make a well-formed request object without latLong', function () { let expectedQuery = { 'account_id': '14062', 'site_id': '70608', @@ -274,8 +404,9 @@ describe('the rubicon adapter', () => { 'kw': 'a,b,c', 'tg_v.ucat': 'new', 'tg_v.lastsearch': 'iphone', + 'tg_v.likes': 'sports,video games', 'tg_i.rating': '5-star', - 'tg_i.prodtype': 'tech', + 'tg_i.prodtype': 'tech,mobile', 'rf': 'localhost', 'p_geo.latitude': undefined, 'p_geo.longitude': undefined @@ -284,8 +415,8 @@ describe('the rubicon adapter', () => { sandbox.stub(Math, 'random').callsFake(() => 0.1); delete bidderRequest.bids[0].params.latLong; - [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - data = parseQuery(request.data); + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = parseQuery(request.data); expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); @@ -300,8 +431,8 @@ describe('the rubicon adapter', () => { }); bidderRequest.bids[0].params.latLong = []; - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + data = parseQuery(request.data); expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); @@ -316,13 +447,32 @@ describe('the rubicon adapter', () => { }); }); - it('page_url should use params.referrer, config.getConfig("pageUrl"), utils.getTopWindowUrl() in that order', () => { - sandbox.stub(utils, 'getTopWindowUrl').callsFake(() => 'http://www.prebid.org'); + it('should add referer info to request data', function () { + let refererInfo = { + referer: 'http://www.prebid.org', + reachedTop: true, + numIframes: 1, + stack: [ + 'http://www.prebid.org/page.html', + 'http://www.prebid.org/iframe1.html', + ] + }; + bidderRequest = Object.assign({refererInfo}, bidderRequest); + delete bidderRequest.bids[0].params.referrer; + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + + expect(parseQuery(request.data).rf).to.exist; + expect(parseQuery(request.data).rf).to.equal('http://www.prebid.org'); + }); + + it('page_url should use params.referrer, config.getConfig("pageUrl"), bidderRequest.refererInfo in that order', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(parseQuery(request.data).rf).to.equal('localhost'); delete bidderRequest.bids[0].params.referrer; + let refererInfo = { referer: 'http://www.prebid.org' }; + bidderRequest = Object.assign({refererInfo}, bidderRequest); [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(parseQuery(request.data).rf).to.equal('http://www.prebid.org'); @@ -338,11 +488,11 @@ describe('the rubicon adapter', () => { bidderRequest.bids[0].params.secure = true; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(parseQuery(request.data).rf).to.equal('http://www.rubiconproject.com'); + expect(parseQuery(request.data).rf).to.equal('https://www.rubiconproject.com'); }); - it('should use rubicon sizes if present (including non-mappable sizes)', () => { - var sizesBidderRequest = clone(bidderRequest); + it('should use rubicon sizes if present (including non-mappable sizes)', function () { + var sizesBidderRequest = utils.deepClone(bidderRequest); sizesBidderRequest.bids[0].params.sizes = [55, 57, 59, 801]; let [request] = spec.buildRequests(sizesBidderRequest.bids, sizesBidderRequest); @@ -352,8 +502,8 @@ describe('the rubicon adapter', () => { expect(data['alt_size_ids']).to.equal('57,59,801'); }); - it('should not validate bid request if no valid sizes', () => { - var sizesBidderRequest = clone(bidderRequest); + it('should not validate bid request if no valid sizes', function () { + var sizesBidderRequest = utils.deepClone(bidderRequest); sizesBidderRequest.bids[0].sizes = [[621, 250], [300, 251]]; let result = spec.isBidRequestValid(sizesBidderRequest.bids[0]); @@ -361,8 +511,8 @@ describe('the rubicon adapter', () => { expect(result).to.equal(false); }); - it('should not validate bid request if no account id is present', () => { - var noAccountBidderRequest = clone(bidderRequest); + it('should not validate bid request if no account id is present', function () { + var noAccountBidderRequest = utils.deepClone(bidderRequest); delete noAccountBidderRequest.bids[0].params.accountId; let result = spec.isBidRequestValid(noAccountBidderRequest.bids[0]); @@ -370,8 +520,8 @@ describe('the rubicon adapter', () => { expect(result).to.equal(false); }); - it('should allow a floor override', () => { - var floorBidderRequest = clone(bidderRequest); + it('should allow a floor override', function () { + var floorBidderRequest = utils.deepClone(bidderRequest); floorBidderRequest.bids[0].params.floor = 2; let [request] = spec.buildRequests(floorBidderRequest.bids, floorBidderRequest); @@ -380,7 +530,7 @@ describe('the rubicon adapter', () => { expect(data['rp_floor']).to.equal('2'); }); - it('should send digitrust params', () => { + it('should send digitrust params', function () { window.DigiTrust = { getUser: function () { } @@ -414,7 +564,7 @@ describe('the rubicon adapter', () => { delete window.DigiTrust; }); - it('should not send digitrust params when DigiTrust not loaded', () => { + it('should not send digitrust params when DigiTrust not loaded', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -426,7 +576,7 @@ describe('the rubicon adapter', () => { }); }); - it('should not send digitrust params due to optout', () => { + it('should not send digitrust params due to optout', function () { window.DigiTrust = { getUser: function () { } @@ -455,7 +605,7 @@ describe('the rubicon adapter', () => { delete window.DigiTrust; }); - it('should not send digitrust params due to failure', () => { + it('should not send digitrust params due to failure', function () { window.DigiTrust = { getUser: function () { } @@ -484,19 +634,18 @@ describe('the rubicon adapter', () => { delete window.DigiTrust; }); - describe('digiTrustId config', () => { - var origGetConfig; - beforeEach(() => { + describe('digiTrustId config', function () { + beforeEach(function () { window.DigiTrust = { getUser: sandbox.spy() }; }); - afterEach(() => { + afterEach(function () { delete window.DigiTrust; }); - it('should send digiTrustId config params', () => { + it('should send digiTrustId config params', function () { sandbox.stub(config, 'getConfig').callsFake((key) => { var config = { digiTrustId: { @@ -529,7 +678,7 @@ describe('the rubicon adapter', () => { expect(window.DigiTrust.getUser.notCalled).to.equal(true); }); - it('should not send digiTrustId config params due to optout', () => { + it('should not send digiTrustId config params due to optout', function () { sandbox.stub(config, 'getConfig').callsFake((key) => { var config = { digiTrustId: { @@ -558,7 +707,7 @@ describe('the rubicon adapter', () => { expect(window.DigiTrust.getUser.notCalled).to.equal(true); }); - it('should not send digiTrustId config params due to failure', () => { + it('should not send digiTrustId config params due to failure', function () { sandbox.stub(config, 'getConfig').callsFake((key) => { var config = { digiTrustId: { @@ -587,7 +736,7 @@ describe('the rubicon adapter', () => { expect(window.DigiTrust.getUser.notCalled).to.equal(true); }); - it('should not send digiTrustId config params if they do not exist', () => { + it('should not send digiTrustId config params if they do not exist', function () { sandbox.stub(config, 'getConfig').callsFake((key) => { var config = {}; return config[key]; @@ -608,8 +757,8 @@ describe('the rubicon adapter', () => { }); }); - describe('GDPR consent config', () => { - it('should send "gdpr" and "gdpr_consent", when gdprConsent defines consentString and gdprApplies', () => { + describe('GDPR consent config', function () { + it('should send "gdpr" and "gdpr_consent", when gdprConsent defines consentString and gdprApplies', function () { createGdprBidderRequest(true); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -618,7 +767,7 @@ describe('the rubicon adapter', () => { expect(data['gdpr_consent']).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); }); - it('should send only "gdpr_consent", when gdprConsent defines only consentString', () => { + it('should send only "gdpr_consent", when gdprConsent defines only consentString', function () { createGdprBidderRequest(); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -627,7 +776,7 @@ describe('the rubicon adapter', () => { expect(data['gdpr']).to.equal(undefined); }); - it('should not send GDPR params if gdprConsent is not defined', () => { + it('should not send GDPR params if gdprConsent is not defined', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -635,7 +784,7 @@ describe('the rubicon adapter', () => { expect(data['gdpr_consent']).to.equal(undefined); }); - it('should set "gdpr" value as 1 or 0, using "gdprApplies" value of either true/false', () => { + it('should set "gdpr" value as 1 or 0, using "gdprApplies" value of either true/false', function () { createGdprBidderRequest(true); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -647,74 +796,326 @@ describe('the rubicon adapter', () => { expect(data['gdpr']).to.equal('0'); }); }); - }); - describe('for video requests', () => { - it('should make a well-formed video request with legacy mediaType config', () => { - createLegacyVideoBidderRequest(); + describe('first party data', function () { + it('should not have any tg_v or tg_i params if all are undefined', function () { + let params = { + inventory: { + rating: null, + prodtype: undefined + }, + visitor: { + ucat: undefined, + lastsearch: null, + likes: undefined + }, + }; - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); + // Overwrite the bidder request params with the above ones + Object.assign(bidderRequest.bids[0].params, params); - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let post = request.data; + // get the built request + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = parseQuery(request.data); + + // make sure that no tg_v or tg_i keys are present in the request + let matchingExp = RegExp('^tg_(i|v)\..*$') + Object.keys(data).forEach(key => { + expect(key).to.not.match(matchingExp); + }); + }); + + it('should contain valid params when some are undefined', function () { + let params = { + inventory: { + rating: undefined, + prodtype: ['tech', 'mobile'] + }, + visitor: { + ucat: null, + lastsearch: 'iphone', + likes: undefined + }, + }; + let undefinedKeys = ['tg_i.rating', 'tg_v.ucat', 'tg_v.likes'] + let expectedQuery = { + 'tg_v.lastsearch': 'iphone', + 'tg_i.prodtype': 'tech,mobile', + } + + // Overwrite the bidder request params with the above ones + Object.assign(bidderRequest.bids[0].params, params); + + // get the built request + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = parseQuery(request.data); + + // make sure none of the undefined keys are in query + undefinedKeys.forEach(key => { + expect(typeof data[key]).to.equal('undefined'); + }); - let url = request.url; - - expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - - expect(post).to.have.property('page_url').that.is.a('string'); - expect(post.resolution).to.match(/\d+x\d+/); - expect(post.account_id).to.equal('14062'); - expect(post.integration).to.equal(INTEGRATION); - expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b'); - expect(post).to.have.property('timeout').that.is.a('number'); - expect(post.timeout < 5000).to.equal(true); - expect(post.stash_creatives).to.equal(true); - expect(post.rp_secure).to.equal(false); - expect(post.gdpr_consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.gdpr).to.equal(1); - - expect(post).to.have.property('ae_pass_through_parameters'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skip') - .that.equals('1'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skipdelay') - .that.equals('15'); - - expect(post).to.have.property('slots') - .with.length.of(1); - - let slot = post.slots[0]; - - expect(slot.site_id).to.equal('70608'); - expect(slot.zone_id).to.equal('335918'); - expect(slot.position).to.equal('atf'); - expect(slot.floor).to.equal(0.01); - expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.language).to.equal('en'); - expect(slot.width).to.equal(640); - expect(slot.height).to.equal(320); - expect(slot.size_id).to.equal(201); - - expect(slot).to.have.property('inventory').that.is.an('object'); - expect(slot.inventory).to.have.property('rating').that.equals('5-star'); - expect(slot.inventory).to.have.property('prodtype').that.equals('tech'); - - expect(slot).to.have.property('keywords') - .that.is.an('array') - .of.length(3) - .that.deep.equals(['a', 'b', 'c']); - - expect(slot).to.have.property('visitor').that.is.an('object'); - expect(slot.visitor).to.have.property('ucat').that.equals('new'); - expect(slot.visitor).to.have.property('lastsearch').that.equals('iphone'); + // make sure the expected and defined ones do show up still + Object.keys(expectedQuery).forEach(key => { + let value = expectedQuery[key]; + expect(data[key]).to.equal(value); + }); + }); }); - it('should make a well-formed video request', () => { + describe('singleRequest config', function () { + it('should group all bid requests with the same site id', function () { + sandbox.stub(Math, 'random').callsFake(() => 0.1); + + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'rubicon.singleRequest': true + }; + return config[key]; + }); + + const expectedQuery = { + 'account_id': '14062', + 'site_id': '70608', + 'zone_id': '335918', + 'size_id': '15', + 'alt_size_ids': '43', + 'p_pos': 'atf', + 'rp_floor': '0.01', + 'rp_secure': /[01]/, + 'rand': '0.1', + 'tk_flint': INTEGRATION, + 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'p_screen_res': /\d+x\d+/, + 'tk_user_key': '12346', + 'kw': 'a,b,c', + 'tg_v.ucat': 'new', + 'tg_v.lastsearch': 'iphone', + 'tg_v.likes': 'sports,video games', + 'tg_i.rating': '5-star', + 'tg_i.prodtype': 'tech,mobile', + 'tg_fl.eid': 'div-1', + 'rf': 'localhost' + }; + + const bidCopy = utils.deepClone(bidderRequest.bids[0]); + bidCopy.params.siteId = '70608'; + bidCopy.params.zoneId = '1111'; + bidderRequest.bids.push(bidCopy); + + const bidCopy2 = utils.deepClone(bidderRequest.bids[0]); + bidCopy2.params.siteId = '99999'; + bidCopy2.params.zoneId = '2222'; + bidderRequest.bids.push(bidCopy2); + + const bidCopy3 = utils.deepClone(bidderRequest.bids[0]); + bidCopy3.params.siteId = '99999'; + bidCopy3.params.zoneId = '3333'; + bidderRequest.bids.push(bidCopy3); + + const serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); + + // array length should match the num of unique 'siteIds' + expect(serverRequests).to.be.a('array'); + expect(serverRequests).to.have.lengthOf(2); + + // collect all bidRequests so order can be checked against the url param slot order + const bidRequests = serverRequests.reduce((aggregator, item) => aggregator.concat(item.bidRequest), []); + let bidRequestIndex = 0; + + serverRequests.forEach(item => { + expect(item).to.be.a('object'); + expect(item).to.have.property('method'); + expect(item).to.have.property('url'); + expect(item).to.have.property('data'); + expect(item).to.have.property('bidRequest'); + + expect(item.method).to.equal('GET'); + expect(item.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); + expect(item.data).to.be.a('string'); + + // 'bidRequest' type must be 'array' if SRA enabled + expect(item.bidRequest).to.be.a('array').to.have.lengthOf(2); + + item.bidRequest.forEach((bidRequestItem, i, array) => { + expect(bidRequestItem).to.be.a('object'); + // every 'siteId' values need to match + expect(bidRequestItem.params.siteId).to.equal(array[0].params.siteId); + }); + + const data = parseQuery(item.data); + + Object.keys(expectedQuery).forEach(key => { + expect(data).to.have.property(key); + + // extract semicolon delineated values + const params = data[key].split(';'); + + // skip value test for site and zone ids + if (key !== 'site_id' && key !== 'zone_id') { + if (expectedQuery[key] instanceof RegExp) { + params.forEach(paramItem => { + expect(paramItem).to.match(expectedQuery[key]); + }); + } else { + expect(params).to.contain(expectedQuery[key]); + } + } + + // check parsed url data list order with requestBid list, items must have same index in both lists + if (key === 'zone_id') { + params.forEach((p) => { + expect(bidRequests[bidRequestIndex]).to.be.a('object'); + expect(bidRequests[bidRequestIndex].params).to.be.a('object'); + + // 'zone_id' is used to verify so each bid must have a unique 'zone_id' + expect(p).to.equal(bidRequests[bidRequestIndex].params.zoneId); + + // increment to next bidRequest index having verified that item positions match in url params and bidRequest lists + bidRequestIndex++; + }); + } + }); + }); + }); + + it('should not send more than 10 bids in a request (split into separate requests with <= 10 bids each)', function () { + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'rubicon.singleRequest': true + }; + return config[key]; + }); + + let serverRequests; + let data; + + // TEST '10' BIDS, add 9 to 1 existing bid + for (let i = 0; i < 9; i++) { + let bidCopy = utils.deepClone(bidderRequest.bids[0]); + bidCopy.params.zoneId = `${i}0000`; + bidderRequest.bids.push(bidCopy); + } + serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); + // '10' bids per SRA request: so there should be 1 request + expect(serverRequests.length).to.equal(1); + // and that one request should have data from 10 bids + expect(serverRequests[0].bidRequest).to.have.lengthOf(10); + // check that slots param value matches + expect(serverRequests[0].data.indexOf('&slots=10&') !== -1).to.equal(true); + // check that zone_id has 10 values (since all zone_ids are unique all should exist in get param) + data = parseQuery(serverRequests[0].data); + expect(data).to.be.a('object'); + expect(data).to.have.property('zone_id'); + expect(data.zone_id.split(';')).to.have.lengthOf(10); + + // TEST '100' BIDS, add 90 to the previously added 10 + for (let i = 0; i < 90; i++) { + let bidCopy = utils.deepClone(bidderRequest.bids[0]); + bidCopy.params.zoneId = `${(i + 10)}0000`; + bidderRequest.bids.push(bidCopy); + } + serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); + // '100' bids: should be '10' SRA requests + expect(serverRequests.length).to.equal(10); + // check that each request has 10 items + serverRequests.forEach((serverRequest) => { + // and that one request should have data from 10 bids + expect(serverRequest.bidRequest).to.have.lengthOf(10); + // check that slots param value matches + expect(serverRequest.data.indexOf('&slots=10&') !== -1).to.equal(true); + }); + }); + + it('should not group bid requests if singleRequest does not equal true', function () { + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'rubicon.singleRequest': false + }; + return config[key]; + }); + + const bidCopy = utils.deepClone(bidderRequest.bids[0]); + bidderRequest.bids.push(bidCopy); + + const bidCopy2 = utils.deepClone(bidderRequest.bids[0]); + bidCopy2.params.siteId = '32001'; + bidderRequest.bids.push(bidCopy2); + + const bidCopy3 = utils.deepClone(bidderRequest.bids[0]); + bidCopy3.params.siteId = '32001'; + bidderRequest.bids.push(bidCopy3); + + let serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(serverRequests).that.is.an('array').of.length(4); + }); + + it('should not group video bid requests', function () { + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'rubicon.singleRequest': true + }; + return config[key]; + }); + + const bidCopy = utils.deepClone(bidderRequest.bids[0]); + bidderRequest.bids.push(bidCopy); + + const bidCopy2 = utils.deepClone(bidderRequest.bids[0]); + bidCopy2.params.siteId = '32001'; + bidderRequest.bids.push(bidCopy2); + + const bidCopy3 = utils.deepClone(bidderRequest.bids[0]); + bidCopy3.params.siteId = '32001'; + bidderRequest.bids.push(bidCopy3); + + const bidCopy4 = utils.deepClone(bidderRequest.bids[0]); + bidCopy4.mediaTypes = { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [2, 5], + maxduration: 30, + linearity: 1, + api: [2] + } + }; + bidCopy4.params.video = { + 'language': 'en', + 'p_aso.video.ext.skip': true, + 'p_aso.video.ext.skipdelay': 15, + 'playerHeight': 320, + 'playerWidth': 640, + 'size_id': 201, + 'aeParams': { + 'p_aso.video.ext.skip': '1', + 'p_aso.video.ext.skipdelay': '15' + } + }; + bidderRequest.bids.push(bidCopy4); + + let serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(serverRequests).that.is.an('array').of.length(3); + }); + }); + + describe('user id config', function() { + it('should send tpid_tdid when userId defines tdid', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + tdid: 'abcd-efgh-ijkl-mnop-1234' + }; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['tpid_tdid']).to.equal('abcd-efgh-ijkl-mnop-1234'); + }); + }) + }); + + describe('for video requests', function () { + it('should make a well-formed video request', function () { createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => @@ -724,260 +1125,368 @@ describe('the rubicon adapter', () => { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; - let url = request.url; - - expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - - expect(post).to.have.property('page_url').that.is.a('string'); - expect(post.resolution).to.match(/\d+x\d+/); - expect(post.account_id).to.equal('14062'); - expect(post.integration).to.equal(INTEGRATION); - expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b'); - expect(post).to.have.property('timeout').that.is.a('number'); - expect(post.timeout < 5000).to.equal(true); - expect(post.stash_creatives).to.equal(true); - expect(post.rp_secure).to.equal(true); - expect(post.gdpr_consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.gdpr).to.equal(1); - - expect(post).to.have.property('ae_pass_through_parameters'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skip') - .that.equals('1'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skipdelay') - .that.equals('15'); - - expect(post).to.have.property('slots') - .with.length.of(1); - - let slot = post.slots[0]; - - expect(slot.site_id).to.equal('70608'); - expect(slot.zone_id).to.equal('335918'); - expect(slot.position).to.equal('atf'); - expect(slot.floor).to.equal(0.01); - expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.language).to.equal('en'); - expect(slot.width).to.equal(640); - expect(slot.height).to.equal(320); - expect(slot.size_id).to.equal(201); - - expect(slot).to.have.property('inventory').that.is.an('object'); - expect(slot.inventory).to.have.property('rating').that.equals('5-star'); - expect(slot.inventory).to.have.property('prodtype').that.equals('tech'); - - expect(slot).to.have.property('keywords') - .that.is.an('array') - .of.length(3) - .that.deep.equals(['a', 'b', 'c']); - - expect(slot).to.have.property('visitor').that.is.an('object'); - expect(slot.visitor).to.have.property('ucat').that.equals('new'); - expect(slot.visitor).to.have.property('lastsearch').that.equals('iphone'); + expect(post).to.have.property('imp') + // .with.length.of(1); + let imp = post.imp[0]; + expect(imp.id).to.equal(bidderRequest.bids[0].adUnitCode); + expect(imp.exp).to.equal(300); + expect(imp.video.w).to.equal(640); + expect(imp.video.h).to.equal(480); + expect(imp.video.pos).to.equal(1); + expect(imp.video.context).to.equal('instream'); + expect(imp.video.minduration).to.equal(15); + expect(imp.video.maxduration).to.equal(30); + expect(imp.video.startdelay).to.equal(0); + expect(imp.video.skip).to.equal(1); + expect(imp.video.skipafter).to.equal(15); + expect(imp.ext.rubicon.video.playerWidth).to.equal(640); + expect(imp.ext.rubicon.video.playerHeight).to.equal(480); + expect(imp.ext.rubicon.video.size_id).to.equal(201); + expect(imp.ext.rubicon.video.language).to.equal('en'); + // Also want it to be in post.site.content.language + expect(post.site.content.language).to.equal('en'); + expect(imp.ext.rubicon.video.skip).to.equal(1); + expect(imp.ext.rubicon.video.skipafter).to.equal(15); + expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + expect(post.regs.ext.gdpr).to.equal(1); + expect(post).to.have.property('ext').that.is.an('object'); + expect(post.ext.prebid.targeting.includewinners).to.equal(true); + expect(post.ext.prebid).to.have.property('cache').that.is.an('object') + expect(post.ext.prebid.cache).to.have.property('vastxml').that.is.an('object') + expect(post.ext.prebid.cache.vastxml).to.have.property('returnCreative').that.is.an('boolean') + expect(post.ext.prebid.cache.vastxml.returnCreative).to.equal(false) }); - it('should send request with proper ad position', () => { + it('should send correct bidfloor to PBS', function() { createVideoBidderRequest(); - var positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'atf'; - let [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - let post = request.data; - let slot = post.slots[0]; - expect(slot.position).to.equal('atf'); + bidderRequest.bids[0].params.floor = 0.1; + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(0.1); - positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'btf'; - [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; + bidderRequest.bids[0].params.floor = 5.5; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(5.5); - expect(slot.position).to.equal('btf'); + bidderRequest.bids[0].params.floor = '1.7'; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(1.7); - positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'unknown'; - [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; + bidderRequest.bids[0].params.floor = 0; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(0); - expect(slot.position).to.equal('unknown'); + bidderRequest.bids[0].params.floor = undefined; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0]).to.not.haveOwnProperty('bidfloor'); + + bidderRequest.bids[0].params.floor = null; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0]).to.not.haveOwnProperty('bidfloor'); + }); + + it('should send request with proper ad position', function () { + createVideoBidderRequest(); + let positionBidderRequest = utils.deepClone(bidderRequest); + positionBidderRequest.bids[0].mediaTypes.video.pos = 1; + let [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); + expect(request.data.imp[0].video.pos).to.equal(1); - positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = '123'; + positionBidderRequest = utils.deepClone(bidderRequest); + positionBidderRequest.bids[0].params.position = undefined; + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; + expect(request.data.imp[0].video.pos).to.equal(undefined); - expect(slot.position).to.equal('unknown'); + positionBidderRequest = utils.deepClone(bidderRequest); + positionBidderRequest.bids[0].params.position = 'atf' + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; + [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); + expect(request.data.imp[0].video.pos).to.equal(1); - positionBidderRequest = clone(bidderRequest); - delete positionBidderRequest.bids[0].params.position; - expect(positionBidderRequest.bids[0].params.position).to.equal(undefined); + positionBidderRequest = utils.deepClone(bidderRequest); + positionBidderRequest.bids[0].params.position = 'btf'; + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; + expect(request.data.imp[0].video.pos).to.equal(3); - expect(slot.position).to.equal('unknown'); + positionBidderRequest = utils.deepClone(bidderRequest); + positionBidderRequest.bids[0].params.position = 'foobar'; + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; + [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); + expect(request.data.imp[0].video.pos).to.equal(undefined); }); - it('should allow a floor price override', () => { - createVideoBidderRequest(); + it('should properly enforce video.context to be either instream or outstream', function () { + let bid = bidderRequest.bids[0]; + bid.mediaTypes = { + video: { + context: 'instream', + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [2, 5], + maxduration: 30, + linearity: 1, + api: [2] + } + } + bid.params.video = {}; sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); - var floorBidderRequest = clone(bidderRequest); + const bidRequestCopy = utils.deepClone(bidderRequest.bids[0]); + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(true); - // enter an explicit floor price // - floorBidderRequest.bids[0].params.floor = 3.25; + // change context to outstream, still true + bidRequestCopy.mediaTypes.video.context = 'outstream'; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(true); - let [request] = spec.buildRequests(floorBidderRequest.bids, floorBidderRequest); - let post = request.data; + // change context to random, false now + bidRequestCopy.mediaTypes.video.context = 'random'; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - let floor = post.slots[0].floor; + // change context to undefined, still false + bidRequestCopy.mediaTypes.video.context = undefined; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - expect(floor).to.equal(3.25); + // remove context, still false + delete bidRequestCopy.mediaTypes.video.context; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); }); - it('should validate bid request with invalid video if a mediaTypes banner property is defined', () => { - const bidRequest = { - mediaTypes: { - video: { - context: 'instream' - }, - banner: { - sizes: [[300, 250]] - } - }, - params: { - accountId: 1001, - video: {} - }, - sizes: [[300, 250]] - } - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); + it('should enforce the new required mediaTypes.video params', function () { + createVideoBidderRequest(); - it('should not validate bid request when a invalid video object and no banner object is passed in', () => { - createVideoBidderRequestNoVideo(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); - const bidRequestCopy = clone(bidderRequest.bids[0]); - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - - bidRequestCopy.params.video = {}; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(true); - bidRequestCopy.params.video = undefined; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + // change mimes to a non array, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.mimes = 'video/mp4'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidRequestCopy.params.video = 123; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + // delete mimes, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.mimes; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidRequestCopy.params.video = {size_id: undefined}; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + // change protocols to an int not array of ints, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.protocols = 1; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - delete bidRequestCopy.params.video; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - }); + // delete protocols, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.protocols; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - it('should not validate bid request when an invalid video object is passed in with legacy config mediaType', () => { - createLegacyVideoBidderRequestNoVideo(); - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); + // change maxduration to an string, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.maxduration = 'string'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - const bidderRequestCopy = clone(bidderRequest); - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + // delete maxduration, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.maxduration; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidderRequestCopy.bids[0].params.video = {}; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + // change linearity to an string, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.linearity = 'string'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidderRequestCopy.bids[0].params.video = undefined; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + // delete linearity, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.linearity; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidderRequestCopy.bids[0].params.video = NaN; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + // change api to an string, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.api = 'string'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - delete bidderRequestCopy.bids[0].params.video; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + // delete api, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.api; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); }); - it('should not validate bid request when video is outstream', () => { + it('bid request is valid when video context is outstream', function () { createVideoBidderRequestOutstream(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + const bidRequestCopy = utils.deepClone(bidderRequest); + + let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(true); + expect(request.data.imp[0].ext.rubicon.video.size_id).to.equal(203); }); - it('should get size from bid.sizes too', () => { - createVideoBidderRequestNoPlayer(); + it('should send banner request when outstream or instream video included but no rubicon video obect is present', function () { + // add banner and video mediaTypes + bidderRequest.mediaTypes = { + banner: { + sizes: [[300, 250]] + }, + video: { + context: 'outstream' + } + }; + // no video object in rubicon params, so we should see one call made for banner + sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); - const bidRequestCopy = clone(bidderRequest); + let requests = spec.buildRequests(bidderRequest.bids, bidderRequest); - let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); + + bidderRequest.mediaTypes.video.context = 'instream'; - expect(request.data.slots[0].width).to.equal(300); - expect(request.data.slots[0].height).to.equal(250); + requests = spec.buildRequests(bidderRequest.bids, bidderRequest); + + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); }); - it('should get size from bid.sizes too with legacy config mediaType', () => { - createLegacyVideoBidderRequestNoPlayer(); + it('should send request as banner when invalid video bid in multiple mediaType bidRequest', function () { + createVideoBidderRequestNoVideo(); + + let bid = bidderRequest.bids[0]; + bid.mediaTypes.banner = { + sizes: [[300, 250]] + }; + sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); - const bidRequestCopy = clone(bidderRequest); + const bidRequestCopy = utils.deepClone(bidderRequest); - let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); + let requests = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); + }); + + it('should include coppa flag in video bid request', () => { + createVideoBidderRequest(); + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + 'coppa': true + }; + return config[key]; + }); + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.regs.coppa).to.equal(1); + }); + }); + + describe('combineSlotUrlParams', function () { + it('should combine an array of slot url params', function () { + expect(spec.combineSlotUrlParams([])).to.deep.equal({}); + + expect(spec.combineSlotUrlParams([{p1: 'foo', p2: 'test', p3: ''}])).to.deep.equal({p1: 'foo', p2: 'test', p3: ''}); + + expect(spec.combineSlotUrlParams([{}, {p1: 'foo', p2: 'test'}])).to.deep.equal({p1: ';foo', p2: ';test'}); + + expect(spec.combineSlotUrlParams([{}, {}, {p1: 'foo', p2: ''}, {}])).to.deep.equal({p1: ';;foo;', p2: ''}); + + expect(spec.combineSlotUrlParams([{}, {p1: 'foo'}, {p1: ''}])).to.deep.equal({p1: ';foo;'}); + + expect(spec.combineSlotUrlParams([ + {p1: 'foo', p2: 'test'}, + {p2: 'test', p3: 'bar'}, + {p1: 'bar', p2: 'test', p4: 'bar'} + ])).to.deep.equal({p1: 'foo;;bar', p2: 'test', p3: ';bar;', p4: ';;bar'}); + + expect(spec.combineSlotUrlParams([ + {p1: 'foo', p2: 'test', p3: 'baz'}, + {p1: 'foo', p2: 'bar'}, + {p2: 'test'} + ])).to.deep.equal({p1: 'foo;foo;', p2: 'test;bar;test', p3: 'baz;;'}); + }); + }); + + describe('createSlotParams', function () { + it('should return a valid slot params object', function () { + let expectedQuery = { + 'account_id': '14062', + 'site_id': '70608', + 'zone_id': '335918', + 'size_id': 15, + 'alt_size_ids': '43', + 'p_pos': 'atf', + 'rp_floor': 0.01, + 'rp_secure': /[01]/, + 'tk_flint': INTEGRATION, + 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'p_screen_res': /\d+x\d+/, + 'tk_user_key': '12346', + 'kw': 'a,b,c', + 'tg_v.ucat': 'new', + 'tg_v.lastsearch': 'iphone', + 'tg_v.likes': 'sports,video games', + 'tg_i.rating': '5-star', + 'tg_i.prodtype': 'tech,mobile', + 'tg_fl.eid': 'div-1', + 'rf': 'localhost' + }; + + const slotParams = spec.createSlotParams(bidderRequest.bids[0], bidderRequest); - expect(request.data.slots[0].width).to.equal(300); - expect(request.data.slots[0].height).to.equal(250); + // test that all values above are both present and correct + Object.keys(expectedQuery).forEach(key => { + const value = expectedQuery[key]; + if (value instanceof RegExp) { + expect(slotParams[key]).to.match(value); + } else { + expect(slotParams[key]).to.equal(value); + } + }); }); }); - describe('hasVideoMediaType', () => { - it('should return true if mediaType is video and size_id is set', () => { + describe('hasVideoMediaType', function () { + it('should return true if mediaType is video and size_id is set', function () { createVideoBidderRequest(); - const legacyVideoTypeBidRequest = spec.hasVideoMediaType(bidderRequest.bids[0]); + const legacyVideoTypeBidRequest = hasVideoMediaType(bidderRequest.bids[0]); expect(legacyVideoTypeBidRequest).is.equal(true); }); - it('should return false if mediaType is video and size_id is not defined', () => { - expect(spec.hasVideoMediaType({ - bid: 99, - mediaType: 'video', - params: { - video: {} - } - })).is.equal(false); + it('should return false if trying to use legacy mediaType with video', function () { + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes; + bidderRequest.bids[0].mediaType = 'video'; + const legacyVideoTypeBidRequest = hasVideoMediaType(bidderRequest.bids[0]); + expect(legacyVideoTypeBidRequest).is.equal(false); }); - it('should return false if bidRequest.mediaType is not equal to video', () => { - expect(spec.hasVideoMediaType({ + it('should return false if bidRequest.mediaType is not equal to video', function () { + expect(hasVideoMediaType({ mediaType: 'banner' })).is.equal(false); }); - it('should return false if bidRequest.mediaType is not defined', () => { - expect(spec.hasVideoMediaType({})).is.equal(false); + it('should return false if bidRequest.mediaType is not defined', function () { + expect(hasVideoMediaType({})).is.equal(false); }); - it('should return true if bidRequest.mediaTypes.video.context is instream and size_id is defined', () => { - expect(spec.hasVideoMediaType({ + it('should return true if bidRequest.mediaTypes.video.context is instream and size_id is defined', function () { + expect(hasVideoMediaType({ mediaTypes: { video: { context: 'instream' @@ -991,8 +1500,8 @@ describe('the rubicon adapter', () => { })).is.equal(true); }); - it('should return false if bidRequest.mediaTypes.video.context is instream but size_id is not defined', () => { - expect(spec.hasVideoMediaType({ + it('should return false if bidRequest.mediaTypes.video.context is instream but size_id is not defined', function () { + expect(spec.isBidRequestValid({ mediaTypes: { video: { context: 'instream' @@ -1006,9 +1515,9 @@ describe('the rubicon adapter', () => { }); }); - describe('interpretResponse', () => { - describe('for fastlane', () => { - it('should handle a success response and sort by cpm', () => { + describe('interpretResponse', function () { + describe('for fastlane', function () { + it('should handle a success response and sort by cpm', function () { let response = { 'status': 'ok', 'account_id': 14062, @@ -1103,7 +1612,130 @@ describe('the rubicon adapter', () => { expect(bids[1].rubiconTargeting.rpfl_14062).to.equal('15_tier_all_test'); }); - it('should be fine with a CPM of 0', () => { + it('should use "network-advertiser" if no creative_id', function () { + let response = { + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ + 43, 10, 2 + ], + 'tracking': '', + 'inventory': {} + }; + + response.ads = [ + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374c', + 'size_id': '15', + 'ad_id': '6', + 'advertiser': 7, + 'network': 8, + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.811, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '15_tier_all_test' + ] + } + ] + } + ]; + + let bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + expect(bids[0].creativeId).to.equal('8-7'); + + response.ads = [ + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', + 'size_id': '43', + 'ad_id': '7', + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.911, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '43_tier_all_test' + ] + } + ] + } + ]; + + bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + expect(bids[0].creativeId).to.equal('-'); + + response.ads = [ + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', + 'size_id': '10', + 'ad_id': '7', + 'network': 8, + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.911, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '10_tier_all_test' + ] + } + ] + } + ]; + + bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + expect(bids[0].creativeId).to.equal('8-'); + + response.ads = [ + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', + 'size_id': '2', + 'ad_id': '7', + 'advertiser': 7, + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.911, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '2_tier_all_test' + ] + } + ] + } + ]; + + bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + expect(bids[0].creativeId).to.equal('-7'); + }); + + it('should be fine with a CPM of 0', function () { let response = { 'status': 'ok', 'account_id': 14062, @@ -1130,7 +1762,7 @@ describe('the rubicon adapter', () => { expect(bids[0].cpm).to.be.equal(0); }); - it('should handle an error with no ads returned', () => { + it('should handle an error with no ads returned', function () { let response = { 'status': 'ok', 'account_id': 14062, @@ -1152,7 +1784,7 @@ describe('the rubicon adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should handle an error', () => { + it('should handle an error', function () { let response = { 'status': 'ok', 'account_id': 14062, @@ -1176,7 +1808,7 @@ describe('the rubicon adapter', () => { expect(bids).to.be.lengthOf(0); }); - it('should handle an error because of malformed json response', () => { + it('should handle an error because of malformed json response', function () { let response = '{test{'; let bids = spec.interpretResponse({body: response}, { @@ -1185,39 +1817,217 @@ describe('the rubicon adapter', () => { expect(bids).to.be.lengthOf(0); }); + + it('should handle a bidRequest argument of type Array', function () { + let response = { + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ + 43 + ], + 'tracking': '', + 'inventory': {}, + 'ads': [{ + 'status': 'ok', + 'cpm': 0, + 'size_id': 15 + }] + }; + + let bids = spec.interpretResponse({ body: response }, { + bidRequest: [utils.deepClone(bidderRequest.bids[0])] + }); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.be.equal(0); + }); + + describe('singleRequest enabled', function () { + it('handles bidRequest of type Array and returns associated adUnits', function () { + const overrideMap = []; + overrideMap[0] = { impression_id: '1' }; + + const stubAds = []; + for (let i = 0; i < 10; i++) { + stubAds.push(createResponseAdByIndex(i, sizeMap[i].sizeId, overrideMap)); + } + + const stubBids = []; + for (let i = 0; i < 10; i++) { + stubBids.push(createBidRequestByIndex(i, sizeMap[i].sizeAsArray.slice())); + } + + const bids = spec.interpretResponse({ + body: { + 'status': 'ok', + 'site_id': '1100', + 'account_id': 14062, + 'zone_id': 2100, + 'size_id': '1', + 'tracking': '', + 'inventory': {}, + 'ads': stubAds + }}, { bidRequest: stubBids }); + expect(bids).to.be.a('array').with.lengthOf(10); + + bids.forEach((bid) => { + expect(bid).to.be.a('object'); + expect(bid).to.have.property('cpm').that.is.a('number'); + expect(bid).to.have.property('width').that.is.a('number'); + expect(bid).to.have.property('height').that.is.a('number'); + + // verify that result bid 'sizeId' links to a size from the sizeMap + const size = getSizeIdForBid(sizeMap, bid); + expect(size).to.be.a('object'); + + // use 'size' to verify that result bid links to the 'response.ad' passed to function + const associateAd = getResponseAdBySize(stubAds, size); + expect(associateAd).to.be.a('object'); + expect(associateAd).to.have.property('creative_id').that.is.a('string'); + + // use 'size' to verify that result bid links to the 'bidRequest' passed to function + const associateBidRequest = getBidRequestBySize(stubBids, size); + expect(associateBidRequest).to.be.a('object'); + expect(associateBidRequest).to.have.property('bidId').that.is.a('string'); + + // verify all bid properties set using 'ad' and 'bidRequest' match + // 'ad.creative_id === bid.creativeId' + expect(bid.requestId).to.equal(associateBidRequest.bidId); + // 'bid.requestId === bidRequest.bidId' + expect(bid.creativeId).to.equal(associateAd.creative_id); + }); + }); + + it('handles incorrect adUnits length by returning all bids with matching ads', function () { + const overrideMap = []; + overrideMap[0] = { impression_id: '1' }; + + const stubAds = []; + for (let i = 0; i < 6; i++) { + stubAds.push(createResponseAdByIndex(i, sizeMap[i].sizeId, overrideMap)); + } + + const stubBids = []; + for (let i = 0; i < 10; i++) { + stubBids.push(createBidRequestByIndex(i, sizeMap[i].sizeAsArray.slice())); + } + + const bids = spec.interpretResponse({ + body: { + 'status': 'ok', + 'site_id': '1100', + 'account_id': 14062, + 'zone_id': 2100, + 'size_id': '1', + 'tracking': '', + 'inventory': {}, + 'ads': stubAds + }}, { bidRequest: stubBids }); + + // no bids expected because response didn't match requested bid number + expect(bids).to.be.a('array').with.lengthOf(6); + }); + + it('skips adUnits with error status and returns all bids with ok status', function () { + const stubAds = []; + // Create overrides to break associations between bids and ads + // Each override should cause one less bid to be returned by interpretResponse + const overrideMap = []; + overrideMap[0] = { impression_id: '1' }; + overrideMap[2] = { status: 'error' }; + overrideMap[4] = { status: 'error' }; + overrideMap[7] = { status: 'error' }; + overrideMap[8] = { status: 'error' }; + + for (let i = 0; i < 10; i++) { + stubAds.push(createResponseAdByIndex(i, sizeMap[i].sizeId, overrideMap)); + } + + const stubBids = []; + for (let i = 0; i < 10; i++) { + stubBids.push(createBidRequestByIndex(i, sizeMap[i].sizeAsArray.slice())); + } + + const bids = spec.interpretResponse({ + body: { + 'status': 'error', + 'site_id': '1100', + 'account_id': 14062, + 'zone_id': 2100, + 'size_id': '1', + 'tracking': '', + 'inventory': {}, + 'ads': stubAds + }}, { bidRequest: stubBids }); + expect(bids).to.be.a('array').with.lengthOf(6); + + bids.forEach((bid) => { + expect(bid).to.be.a('object'); + expect(bid).to.have.property('cpm').that.is.a('number'); + expect(bid).to.have.property('width').that.is.a('number'); + expect(bid).to.have.property('height').that.is.a('number'); + + // verify that result bid 'sizeId' links to a size from the sizeMap + const size = getSizeIdForBid(sizeMap, bid); + expect(size).to.be.a('object'); + + // use 'size' to verify that result bid links to the 'response.ad' passed to function + const associateAd = getResponseAdBySize(stubAds, size); + expect(associateAd).to.be.a('object'); + expect(associateAd).to.have.property('creative_id').that.is.a('string'); + expect(associateAd).to.have.property('status').that.is.a('string'); + expect(associateAd.status).to.equal('ok'); + + // use 'size' to verify that result bid links to the 'bidRequest' passed to function + const associateBidRequest = getBidRequestBySize(stubBids, size); + expect(associateBidRequest).to.be.a('object'); + expect(associateBidRequest).to.have.property('bidId').that.is.a('string'); + + // verify all bid properties set using 'ad' and 'bidRequest' match + // 'ad.creative_id === bid.creativeId' + expect(bid.requestId).to.equal(associateBidRequest.bidId); + // 'bid.requestId === bidRequest.bidId' + expect(bid.creativeId).to.equal(associateAd.creative_id); + }); + }); + }); }); - describe('for video', () => { - beforeEach(() => { + describe('for video', function () { + beforeEach(function () { createVideoBidderRequest(); }); - it('should register a successful bid', () => { + it('should register a successful bid', function () { let response = { - 'status': 'ok', - 'ads': { - '/19968336/header-bid-tag-0': [ - { - 'status': 'ok', - 'cpm': 1, - 'tier': 'tier0200', - 'targeting': { - 'rpfl_8000': '201_tier0200', - 'rpfl_elemid': '/19968336/header-bid-tag-0' + cur: 'USD', + seatbid: [{ + bid: [{ + id: '0', + impid: 'instream_video1', + price: 2, + crid: '4259970', + ext: { + bidder: { + rp: { + mime: 'application/javascript', + size_id: 201 + } }, - 'impression_id': 'a40fe16e-d08d-46a9-869d-2e1573599e0c', - 'site_id': 88888, - 'zone_id': 54321, - 'creative_type': 'video', - 'creative_depot_url': 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml', - 'ad_id': 999999, - 'creative_id': 'crid-999999', - 'size_id': 201, - 'advertiser': 12345 + prebid: { + targeting: { + hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' + }, + type: 'video' + } } - ] - }, - 'account_id': 7780 + }], + group: 0, + seat: 'rubicon' + }], }; let bids = spec.interpretResponse({body: response}, { @@ -1226,29 +2036,42 @@ describe('the rubicon adapter', () => { expect(bids).to.be.lengthOf(1); - expect(bids[0].creativeId).to.equal('crid-999999'); - expect(bids[0].cpm).to.equal(1); + expect(bids[0].creativeId).to.equal('4259970'); + expect(bids[0].cpm).to.equal(2); expect(bids[0].ttl).to.equal(300); expect(bids[0].netRevenue).to.equal(false); - expect(bids[0].vastUrl).to.equal( - 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml' - ); - expect(bids[0].impression_id).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); + expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); expect(bids[0].mediaType).to.equal('video'); - expect(bids[0].videoCacheKey).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); + expect(bids[0].bidderCode).to.equal('rubicon'); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].width).to.equal(640); + expect(bids[0].height).to.equal(480); + }); + }); + + describe('config with integration type', () => { + it('should use the integration type provided in the config instead of the default', () => { + sandbox.stub(config, 'getConfig').callsFake(function (key) { + const config = { + 'rubicon.int_type': 'testType' + }; + return config[key]; + }); + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(parseQuery(request.data).tk_flint).to.equal('testType_v$prebid.version$'); }); }); }); }); - describe('user sync', () => { + describe('user sync', function () { const emilyUrl = 'https://eus.rubiconproject.com/usync.html'; - beforeEach(() => { + beforeEach(function () { resetUserSync(); }); - it('should register the Emily iframe', () => { + it('should register the Emily iframe', function () { let syncs = spec.getUserSyncs({ iframeEnabled: true }); @@ -1256,7 +2079,7 @@ describe('the rubicon adapter', () => { expect(syncs).to.deep.equal({type: 'iframe', url: emilyUrl}); }); - it('should not register the Emily iframe more than once', () => { + it('should not register the Emily iframe more than once', function () { let syncs = spec.getUserSyncs({ iframeEnabled: true }); @@ -1267,7 +2090,7 @@ describe('the rubicon adapter', () => { expect(syncs).to.equal(undefined); }); - it('should pass gdpr params if consent is true', () => { + it('should pass gdpr params if consent is true', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' })).to.deep.equal({ @@ -1275,7 +2098,7 @@ describe('the rubicon adapter', () => { }); }); - it('should pass gdpr params if consent is false', () => { + it('should pass gdpr params if consent is false', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' })).to.deep.equal({ @@ -1283,7 +2106,7 @@ describe('the rubicon adapter', () => { }); }); - it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', () => { + it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: 'foo' })).to.deep.equal({ @@ -1291,13 +2114,13 @@ describe('the rubicon adapter', () => { }); }); - it('should pass no params if gdpr consentString is not defined', () => { + it('should pass no params if gdpr consentString is not defined', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {})).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` }); }); - it('should pass no params if gdpr consentString is a number', () => { + it('should pass no params if gdpr consentString is a number', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: 0 })).to.deep.equal({ @@ -1305,7 +2128,7 @@ describe('the rubicon adapter', () => { }); }); - it('should pass no params if gdpr consentString is null', () => { + it('should pass no params if gdpr consentString is null', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: null })).to.deep.equal({ @@ -1313,7 +2136,7 @@ describe('the rubicon adapter', () => { }); }); - it('should pass no params if gdpr consentString is a object', () => { + it('should pass no params if gdpr consentString is a object', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: {} })).to.deep.equal({ @@ -1321,14 +2144,44 @@ describe('the rubicon adapter', () => { }); }); - it('should pass no params if gdpr is not defined', () => { + it('should pass no params if gdpr is not defined', function () { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined)).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` }); }); }); -}); -function clone(obj) { - return JSON.parse(JSON.stringify(obj)); -} + describe('get price granularity', function() { + it('should return correct buckets for all price granularity values', function() { + const CUSTOM_PRICE_BUCKET_ITEM = {min: 0, max: 5, increment: 0.5}; + + const mockConfig = { + priceGranularity: undefined, + customPriceBucket: { + buckets: [CUSTOM_PRICE_BUCKET_ITEM] + } + }; + sandbox.stub(config, 'getConfig').callsFake(key => { + return mockConfig[key]; + }); + + [ + {key: 'low', val: {max: 5.00, increment: 0.50}}, + {key: 'medium', val: {max: 20.00, increment: 0.10}}, + {key: 'high', val: {max: 20.00, increment: 0.01}}, + {key: 'auto', val: {max: 5.00, increment: 0.05}}, + {key: 'dense', val: {max: 3.00, increment: 0.01}}, + {key: 'custom', val: CUSTOM_PRICE_BUCKET_ITEM}, + + ].forEach(kvPair => { + mockConfig.priceGranularity = kvPair.key; + const result = getPriceGranularity(config); + expect(typeof result).to.equal('object'); + expect(result).to.haveOwnProperty('ranges'); + expect(Array.isArray(result.ranges)).to.equal(true); + expect(result.ranges.length).to.be.greaterThan(0) + expect(result.ranges[0]).to.deep.equal(kvPair.val); + }); + }); + }); +}); diff --git a/test/spec/modules/s2sTesting_spec.js b/test/spec/modules/s2sTesting_spec.js index 33552011aa1..52377dcabf2 100644 --- a/test/spec/modules/s2sTesting_spec.js +++ b/test/spec/modules/s2sTesting_spec.js @@ -1,45 +1,29 @@ -import { getSourceBidderMap, calculateBidSources, getSource } from 'modules/s2sTesting'; +import s2sTesting from 'modules/s2sTesting'; import { config } from 'src/config'; -import find from 'core-js/library/fn/array/find'; - -var events = require('src/events'); -var CONSTANTS = require('src/constants.json'); -const BID_ADJUSTMENT = CONSTANTS.EVENTS.BID_ADJUSTMENT; var expect = require('chai').expect; describe('s2sTesting', function () { - let mathRandomStub; - let randomNumber = 0; - - beforeEach(() => { - mathRandomStub = sinon.stub(Math, 'random').callsFake(() => { return randomNumber; }); - }); - - afterEach(() => { - mathRandomStub.restore(); - }); - - describe('getSource', () => { + describe('s2sTesting.getSource', function () { // helper function to set random number and get the source function getExpectedSource(randNumber, sourceWeights, sources) { // set random number for testing - randomNumber = randNumber; - return getSource(sourceWeights, sources); + s2sTesting.globalRand = randNumber; + return s2sTesting.getSource(sourceWeights, sources); } - it('returns undefined if no sources', () => { + it('returns undefined if no sources', function () { expect(getExpectedSource(0, {})).to.be.undefined; expect(getExpectedSource(0.5, {})).to.be.undefined; expect(getExpectedSource(0.9999, {})).to.be.undefined; }); - it('returns undefined if no weights', () => { + it('returns undefined if no weights', function () { expect(getExpectedSource(0, {server: 0, client: 0})).to.be.undefined; expect(getExpectedSource(0.5, {client: 0})).to.be.undefined; }); - it('gets the expected source from 3 sources', () => { + it('gets the expected source from 3 sources', function () { var sources = ['server', 'client', 'both']; expect(getExpectedSource(0, {server: 1, client: 1, both: 2}, sources)).to.equal('server'); expect(getExpectedSource(0.2499999, {server: 1, client: 1, both: 2}, sources)).to.equal('server'); @@ -49,7 +33,7 @@ describe('s2sTesting', function () { expect(getExpectedSource(0.99999, {server: 1, client: 1, both: 2}, sources)).to.equal('both'); }); - it('gets the expected source from 2 sources', () => { + it('gets the expected source from 2 sources', function () { expect(getExpectedSource(0, {server: 2, client: 3})).to.equal('server'); expect(getExpectedSource(0.39999, {server: 2, client: 3})).to.equal('server'); expect(getExpectedSource(0.4, {server: 2, client: 3})).to.equal('client'); @@ -61,19 +45,19 @@ describe('s2sTesting', function () { expect(getExpectedSource(0.9, {server: 2, client: 3}, sources)).to.equal('client'); }); - it('gets the expected source from 1 source', () => { + it('gets the expected source from 1 source', function () { expect(getExpectedSource(0, {client: 2})).to.equal('client'); expect(getExpectedSource(0.5, {client: 2})).to.equal('client'); expect(getExpectedSource(0.99999, {client: 2})).to.equal('client'); }); - it('ignores an invalid source', () => { + it('ignores an invalid source', function () { expect(getExpectedSource(0, {client: 2, cache: 2})).to.equal('client'); expect(getExpectedSource(0.3333, {server: 1, cache: 1, client: 2})).to.equal('server'); expect(getExpectedSource(0.34, {server: 1, cache: 1, client: 2})).to.equal('client'); }); - it('ignores order of sources', () => { + it('ignores order of sources', function () { var sources = ['server', 'client', 'both']; expect(getExpectedSource(0, {client: 1, server: 1, both: 2}, sources)).to.equal('server'); expect(getExpectedSource(0.2499999, {both: 2, client: 1, server: 1}, sources)).to.equal('server'); @@ -82,68 +66,68 @@ describe('s2sTesting', function () { expect(getExpectedSource(0.5, {both: 2, server: 1, client: 1}, sources)).to.equal('both'); }); - it('accepts an array of sources', () => { + it('accepts an array of sources', function () { expect(getExpectedSource(0.3333, {second: 2, first: 1}, ['first', 'second'])).to.equal('first'); expect(getExpectedSource(0.34, {second: 2, first: 1}, ['first', 'second'])).to.equal('second'); expect(getExpectedSource(0.9999, {second: 2, first: 1}, ['first', 'second'])).to.equal('second'); }); }); - describe('getSourceBidderMap', () => { - describe('setting source through s2sConfig', () => { - beforeEach(() => { + describe('s2sTesting.getSourceBidderMap', function () { + describe('setting source through s2sConfig', function () { + beforeEach(function () { // set random number for testing - randomNumber = 0.7; + s2sTesting.globalRand = 0.7; }); - it('does not work if testing is "false"', () => { + it('does not work if testing is "false"', function () { config.setConfig({s2sConfig: { bidders: ['rubicon'], testing: false, bidderControl: {rubicon: {bidSource: {server: 1, client: 1}}} }}); - expect(getSourceBidderMap()).to.eql({ + expect(s2sTesting.getSourceBidderMap()).to.eql({ server: [], client: [] }); }); - it('sets one client bidder', () => { + it('sets one client bidder', function () { config.setConfig({s2sConfig: { bidders: ['rubicon'], testing: true, bidderControl: {rubicon: {bidSource: {server: 1, client: 1}}} }}); - expect(getSourceBidderMap()).to.eql({ + expect(s2sTesting.getSourceBidderMap()).to.eql({ server: [], client: ['rubicon'] }); }); - it('sets one server bidder', () => { + it('sets one server bidder', function () { config.setConfig({s2sConfig: { bidders: ['rubicon'], testing: true, bidderControl: {rubicon: {bidSource: {server: 4, client: 1}}} }}); - expect(getSourceBidderMap()).to.eql({ + expect(s2sTesting.getSourceBidderMap()).to.eql({ server: ['rubicon'], client: [] }); }); - it('defaults to server', () => { + it('defaults to server', function () { config.setConfig({s2sConfig: { bidders: ['rubicon'], testing: true }}); - expect(getSourceBidderMap()).to.eql({ + expect(s2sTesting.getSourceBidderMap()).to.eql({ server: ['rubicon'], client: [] }); }); - it('sets two bidders', () => { + it('sets two bidders', function () { config.setConfig({s2sConfig: { bidders: ['rubicon', 'appnexus'], testing: true, @@ -151,27 +135,71 @@ describe('s2sTesting', function () { rubicon: {bidSource: {server: 3, client: 1}}, appnexus: {bidSource: {server: 1, client: 1}} }}}); - var serverClientBidders = getSourceBidderMap(); + var serverClientBidders = s2sTesting.getSourceBidderMap(); expect(serverClientBidders.server).to.eql(['rubicon']); expect(serverClientBidders.client).to.have.members(['appnexus']); }); + + it('sends both bidders to same source when weights are the same', function () { + s2sTesting.globalRand = 0.01; + + config.setConfig({s2sConfig: { + bidders: ['rubicon', 'appnexus'], + testing: true, + bidderControl: { + rubicon: {bidSource: {server: 1, client: 99}}, + appnexus: {bidSource: {server: 1, client: 99}} + }}}); + expect(s2sTesting.getSourceBidderMap()).to.eql({ + client: ['rubicon', 'appnexus'], + server: [] + }); + expect(s2sTesting.getSourceBidderMap()).to.eql({ + client: ['rubicon', 'appnexus'], + server: [] + }); + expect(s2sTesting.getSourceBidderMap()).to.eql({ + client: ['rubicon', 'appnexus'], + server: [] + }); + + config.setConfig({s2sConfig: { + bidders: ['rubicon', 'appnexus'], + testing: true, + bidderControl: { + rubicon: {bidSource: {server: 99, client: 1}}, + appnexus: {bidSource: {server: 99, client: 1}} + }}}); + expect(s2sTesting.getSourceBidderMap()).to.eql({ + server: ['rubicon', 'appnexus'], + client: [] + }); + expect(s2sTesting.getSourceBidderMap()).to.eql({ + server: ['rubicon', 'appnexus'], + client: [] + }); + expect(s2sTesting.getSourceBidderMap()).to.eql({ + server: ['rubicon', 'appnexus'], + client: [] + }); + }); }); - describe('setting source through adUnits', () => { - beforeEach(() => { + describe('setting source through adUnits', function () { + beforeEach(function () { // reset s2sconfig bid sources config.setConfig({s2sConfig: {testing: true}}); // set random number for testing - randomNumber = 0.7; + s2sTesting.globalRand = 0.7; }); - it('sets one bidder source from one adUnit', () => { + it('sets one bidder source from one adUnit', function () { var adUnits = [ {bids: [ {bidder: 'rubicon', bidSource: {server: 4, client: 1}} ]} ]; - expect(getSourceBidderMap(adUnits)).to.eql({ + expect(s2sTesting.getSourceBidderMap(adUnits)).to.eql({ server: ['rubicon'], client: [] }); @@ -184,7 +212,7 @@ describe('s2sTesting', function () { {bidder: 'rubicon', bidSource: {server: 1, client: 1}} ]} ]; - expect(getSourceBidderMap(adUnits)).to.eql({ + expect(s2sTesting.getSourceBidderMap(adUnits)).to.eql({ server: [], client: ['rubicon'] }); @@ -193,13 +221,13 @@ describe('s2sTesting', function () { expect(adUnits[0].bids[0].finalSource).to.equal('client'); }); - it('defaults to client if no bidSource', () => { + it('defaults to client if no bidSource', function () { var adUnits = [ {bids: [ {bidder: 'rubicon', bidSource: {}} ]} ]; - expect(getSourceBidderMap(adUnits)).to.eql({ + expect(s2sTesting.getSourceBidderMap(adUnits)).to.eql({ server: [], client: ['rubicon'] }); @@ -208,14 +236,14 @@ describe('s2sTesting', function () { expect(adUnits[0].bids[0].finalSource).to.equal('client'); }); - it('sets multiple bidders sources from one adUnit', () => { + it('sets multiple bidders sources from one adUnit', function () { var adUnits = [ {bids: [ {bidder: 'rubicon', bidSource: {server: 2, client: 1}}, {bidder: 'appnexus', bidSource: {server: 3, client: 1}} ]} ]; - var serverClientBidders = getSourceBidderMap(adUnits); + var serverClientBidders = s2sTesting.getSourceBidderMap(adUnits); expect(serverClientBidders.server).to.eql(['appnexus']); expect(serverClientBidders.client).to.have.members(['rubicon']); // should have saved the source on the bid @@ -225,7 +253,7 @@ describe('s2sTesting', function () { expect(adUnits[0].bids[1].finalSource).to.equal('server'); }); - it('sets multiple bidders sources from multiple adUnits', () => { + it('sets multiple bidders sources from multiple adUnits', function () { var adUnits = [ {bids: [ {bidder: 'rubicon', bidSource: {server: 2, client: 1}}, @@ -236,7 +264,7 @@ describe('s2sTesting', function () { {bidder: 'bidder3', bidSource: {client: 1}} ]} ]; - var serverClientBidders = getSourceBidderMap(adUnits); + var serverClientBidders = s2sTesting.getSourceBidderMap(adUnits); expect(serverClientBidders.server).to.have.members(['rubicon']); expect(serverClientBidders.server).to.not.have.members(['appnexus', 'bidder3']); expect(serverClientBidders.client).to.have.members(['rubicon', 'appnexus', 'bidder3']); @@ -251,7 +279,7 @@ describe('s2sTesting', function () { expect(adUnits[1].bids[1].finalSource).to.equal('client'); }); - it('should reuse calculated sources', () => { + it('should reuse calculated sources', function () { var adUnits = [ {bids: [ {bidder: 'rubicon', calcSource: 'client', bidSource: {server: 4, client: 1}}, @@ -259,7 +287,7 @@ describe('s2sTesting', function () { {bidder: 'bidder3', calcSource: 'server', bidSource: {client: 1}} ]} ]; - var serverClientBidders = getSourceBidderMap(adUnits); + var serverClientBidders = s2sTesting.getSourceBidderMap(adUnits); expect(serverClientBidders.server).to.have.members(['appnexus', 'bidder3']); expect(serverClientBidders.server).to.not.have.members(['rubicon']); @@ -275,15 +303,15 @@ describe('s2sTesting', function () { }); }); - describe('setting source through s2sconfig and adUnits', () => { - beforeEach(() => { + describe('setting source through s2sconfig and adUnits', function () { + beforeEach(function () { // reset s2sconfig bid sources config.setConfig({s2sConfig: {testing: true}}); // set random number for testing - randomNumber = 0.7; + s2sTesting.globalRand = 0.7; }); - it('should get sources from both', () => { + it('should get sources from both', function () { // set rubicon: server and appnexus: client var adUnits = [ {bids: [ @@ -302,7 +330,7 @@ describe('s2sTesting', function () { } }}); - var serverClientBidders = getSourceBidderMap(adUnits); + var serverClientBidders = s2sTesting.getSourceBidderMap(adUnits); expect(serverClientBidders.server).to.have.members(['rubicon', 'appnexus']); expect(serverClientBidders.client).to.have.members(['rubicon', 'appnexus']); }); diff --git a/test/spec/modules/saraBidAdapter_spec.js b/test/spec/modules/saraBidAdapter_spec.js index 1b5d75170ae..6614ec65265 100644 --- a/test/spec/modules/saraBidAdapter_spec.js +++ b/test/spec/modules/saraBidAdapter_spec.js @@ -5,13 +5,13 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('Sara Adapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'sara', 'params': { @@ -24,11 +24,11 @@ describe('Sara Adapter', function () { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -38,7 +38,7 @@ describe('Sara Adapter', function () { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { function parseRequest(url) { const res = {}; url.split('&').forEach((it) => { @@ -83,7 +83,7 @@ describe('Sara Adapter', function () { } ]; - it('should attach valid params to the tag', () => { + it('should attach valid params to the tag', function () { const request = spec.buildRequests([bidRequests[0]]); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -92,7 +92,7 @@ describe('Sara Adapter', function () { expect(payload).to.have.property('auids', '5'); }); - it('auids must not be duplicated', () => { + it('auids must not be duplicated', function () { const request = spec.buildRequests(bidRequests); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -101,7 +101,7 @@ describe('Sara Adapter', function () { expect(payload).to.have.property('auids', '5,6'); }); - it('pt parameter must be "gross" if params.priceType === "gross"', () => { + it('pt parameter must be "gross" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; const request = spec.buildRequests(bidRequests); expect(request.data).to.be.an('string'); @@ -112,7 +112,7 @@ describe('Sara Adapter', function () { delete bidRequests[1].params.priceType; }); - it('pt parameter must be "net" or "gross"', () => { + it('pt parameter must be "net" or "gross"', function () { bidRequests[1].params.priceType = 'some'; const request = spec.buildRequests(bidRequests); expect(request.data).to.be.an('string'); @@ -124,7 +124,7 @@ describe('Sara Adapter', function () { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 4, 'h': 250, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 5, 'h': 90, 'w': 728}], 'seat': '1'}, @@ -135,7 +135,7 @@ describe('Sara Adapter', function () { {'seat': '1'}, ]; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const bidRequests = [ { 'bidder': 'sara', @@ -169,7 +169,7 @@ describe('Sara Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('should get correct multi bid response', () => { + it('should get correct multi bid response', function () { const bidRequests = [ { 'bidder': 'sara', @@ -249,7 +249,7 @@ describe('Sara Adapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('handles wrong and nobid responses', () => { + it('handles wrong and nobid responses', function () { const bidRequests = [ { 'bidder': 'sara', diff --git a/test/spec/modules/scaleableAnalyticsAdapter_spec.js b/test/spec/modules/scaleableAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..300f72751a4 --- /dev/null +++ b/test/spec/modules/scaleableAnalyticsAdapter_spec.js @@ -0,0 +1,136 @@ +import scaleableAnalytics from 'modules/scaleableAnalyticsAdapter'; +import { expect } from 'chai'; +import events from 'src/events'; +import CONSTANTS from 'src/constants.json'; +import adapterManager from 'src/adapterManager'; + +const BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; +const AUCTION_INIT = CONSTANTS.EVENTS.AUCTION_INIT; +const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; +const BID_WON = CONSTANTS.EVENTS.BID_WON; +const AUCTION_END = CONSTANTS.EVENTS.AUCTION_END; + +describe('Scaleable Analytics Adapter', function() { + const MOCK_DATA = { + adUnitCode: '12345', + site: '5c4fab7a829e955d6c265e72', + bidResponse: { + adUnitCode: '12345', + bidderCode: 'test-code', + cpm: 3.14, + timeToRespond: 285 + }, + bidTimeout: [ + { + adUnitCode: '67890', + bidder: 'test-code' + } + ] + }; + + MOCK_DATA.expectedBidResponse = { + event: 'bids', + bids: [{ + code: MOCK_DATA.bidResponse.bidderCode, + cpm: MOCK_DATA.bidResponse.cpm, + ttr: MOCK_DATA.bidResponse.timeToRespond + }], + adunit: MOCK_DATA.adUnitCode, + site: MOCK_DATA.site + }; + + MOCK_DATA.expectedBidTimeout = { + event: 'bids', + bids: [], + timeouts: [MOCK_DATA.bidTimeout[0].bidder], + adunit: MOCK_DATA.bidTimeout[0].adUnitCode, + site: MOCK_DATA.site + }; + + let xhr; + let requests; + + before(function() { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + }); + + after(function() { + xhr.restore(); + }); + + describe('Event Handling', function() { + beforeEach(function() { + requests = []; + sinon.stub(events, 'getEvents').returns([]); + + scaleableAnalytics.enableAnalytics({ + provider: 'scaleable', + options: { + site: MOCK_DATA.site + } + }); + }); + + afterEach(function() { + events.getEvents.restore(); + scaleableAnalytics.disableAnalytics(); + }); + + it('should handle the auction init event', function(done) { + events.emit(AUCTION_INIT, { + adUnitCodes: [MOCK_DATA.adUnitCode] + }); + + const result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({ + event: 'request', + site: MOCK_DATA.site, + adunit: MOCK_DATA.adUnitCode + }); + + done(); + }); + + it('should handle the bid response event', function() { + events.emit(BID_RESPONSE, MOCK_DATA.bidResponse); + + const actual = scaleableAnalytics.getAuctionData(); + + expect(actual[MOCK_DATA.adUnitCode]).to.deep.equal(MOCK_DATA.expectedBidResponse); + }); + + it('should handle the bid timeout event', function() { + events.emit(BID_TIMEOUT, MOCK_DATA.bidTimeout); + + const actual = scaleableAnalytics.getAuctionData(); + + expect(actual[MOCK_DATA.bidTimeout[0].adUnitCode]).to.deep.equal(MOCK_DATA.expectedBidTimeout); + }); + + it('should handle the bid won event', function(done) { + events.emit(BID_WON, MOCK_DATA.bidResponse); + + const result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({ + adunit: MOCK_DATA.adUnitCode, + code: MOCK_DATA.bidResponse.bidderCode, + cpm: MOCK_DATA.bidResponse.cpm, + ttr: MOCK_DATA.bidResponse.timeToRespond, + event: 'win', + site: MOCK_DATA.site + }); + + done(); + }); + + it('should handle the auction end event', function(done) { + events.emit(AUCTION_END, {}); + + const result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal(MOCK_DATA.expectedBidResponse); + + done(); + }); + }); +}); diff --git a/test/spec/modules/schain_spec.js b/test/spec/modules/schain_spec.js new file mode 100644 index 00000000000..8f5104f1822 --- /dev/null +++ b/test/spec/modules/schain_spec.js @@ -0,0 +1,286 @@ +import {isValidSchainConfig, isSchainObjectValid, copySchainObjectInAdunits} from '../../../modules/schain'; +import { expect } from 'chai'; + +describe('#isValidSchainConfig: module config validation', function() { + it('if config is undefined or not an objct then return false', function() { + expect(isValidSchainConfig()).to.false; + expect(isValidSchainConfig('')).to.false; + expect(isValidSchainConfig([])).to.false; + expect(isValidSchainConfig(12)).to.false; + expect(isValidSchainConfig(3.14)).to.false; + }) + + it('if config is an object then return true', function() { + expect(isValidSchainConfig({})).to.true; + }) +}); + +describe('#isSchainObjectValid: schain object validation', function() { + let schainConfig; + + beforeEach(function() { + schainConfig = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + } + ] + }; + }); + + it('Return true for correct config', function() { + expect(isSchainObjectValid(schainConfig, true)).to.true; + }); + + it('Return false for string config', function() { + schainConfig = JSON.stringify(schainConfig); + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if complete param is not an Integer', function() { + schainConfig.complete = 1; // integer + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.complete = '1'; // string + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.complete = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.complete = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.complete; // undefined + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.complete = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.complete = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if version param is not a String', function() { + schainConfig.ver = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.ver = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.ver = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.ver; // undefined + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.ver = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.ver = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if ext param is not an Object', function() { + schainConfig.ext = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.ext = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.ext = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.true; + delete schainConfig.ext; // undefined // param is optional thus this will result true + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.ext = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.ext = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes param is not an Array', function() { + // by default schainConfig.nodes is array + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.nodes; // undefined + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes[].asi is not a String', function() { + schainConfig.nodes[0].asi = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].asi = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].asi = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.nodes[0].asi; // undefined + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].asi = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].asi = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes[].sid is not a String', function() { + schainConfig.nodes[1].sid = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].sid = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].sid = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.nodes[0].sid; // undefined + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].sid = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].sid = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes[].hp is not an Integer', function() { + schainConfig.nodes[0].hp = '1'; // string + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].hp = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].hp = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.nodes[0].hp; // undefined + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].hp = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].hp = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes[].rid is not a String', function() { + schainConfig.nodes[1].rid = 'rid value'; // string + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes[1].rid = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].rid = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].rid = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.nodes[1].rid; // undefined // param is optional thus this will result true + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes[1].rid = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].rid = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes[].name is not a String', function() { + schainConfig.nodes[0].name = 'name value'; // string + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes[0].name = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].name = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].name = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.nodes[0].name; // undefined // param is optional thus this will result true + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes[0].name = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].name = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes[].domain is not a String', function() { + schainConfig.nodes[1].domain = 'domain value'; // string + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes[1].domain = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].domain = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].domain = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.false; + delete schainConfig.nodes[1].domain; // undefined // param is optional thus this will result true + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes[1].domain = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[1].domain = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Returns false if nodes[].ext param is not an Object', function() { + schainConfig.nodes[0].ext = 1; // Integer + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].ext = 1.1; // float + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].ext = {}; // object + expect(isSchainObjectValid(schainConfig, true)).to.true; + delete schainConfig.nodes[0].ext; // undefined // param is optional thus this will result true + expect(isSchainObjectValid(schainConfig, true)).to.true; + schainConfig.nodes[0].ext = true; // boolean + expect(isSchainObjectValid(schainConfig, true)).to.false; + schainConfig.nodes[0].ext = []; // array + expect(isSchainObjectValid(schainConfig, true)).to.false; + }); + + it('Relaxed mode: Returns true even for invalid config if second argument is set to false', function() { + schainConfig = { + 'ver': 1.0, // invalid + 'complete': '1', // invalid + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': 1, // invalid + 'hp': '1' // invalid + }, + + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + } + ] + }; + expect(isSchainObjectValid(schainConfig, false)).to.true; + + schainConfig = {}; + expect(isSchainObjectValid(schainConfig, false)).to.true; + }) +}); + +describe('Passing schain object to adUnits', function() { + let schainConfig; + + beforeEach(function() { + schainConfig = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + } + ] + }; + }); + + it('schain object should be applied to all adUnits', function() { + let adUnits = [ + { + bids: [{}, {}] + }, + { + bids: [{}, {}] + } + ]; + copySchainObjectInAdunits(adUnits, schainConfig); + expect(adUnits[0].bids[0].schain).to.equal(schainConfig); + expect(adUnits[0].bids[1].schain).to.equal(schainConfig); + expect(adUnits[1].bids[0].schain).to.equal(schainConfig); + expect(adUnits[1].bids[1].schain).to.equal(schainConfig); + }); +}); diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js new file mode 100644 index 00000000000..4bd4b599c55 --- /dev/null +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -0,0 +1,396 @@ +import { expect } from 'chai' +import { spec, getTimeoutUrl } from 'modules/seedtagBidAdapter' + +function getSlotConfigs(mediaTypes, params) { + return { + params: params, + sizes: [[300, 250], [300, 600]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + bidRequestsCount: 1, + bidder: 'seedtag', + mediaTypes: mediaTypes, + src: 'client', + transactionId: 'd704d006-0d6e-4a09-ad6c-179e7e758096' + } +} + +describe('Seedtag Adapter', function() { + describe('isBidRequestValid method', function() { + const PUBLISHER_ID = '0000-0000-01' + const ADUNIT_ID = '000000' + describe('returns true', function() { + describe('when banner slot config has all mandatory params', () => { + describe('and placement has the correct value', function() { + const createBannerSlotConfig = placement => { + return getSlotConfigs( + { banner: {} }, + { + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, + placement + } + ) + } + const placements = ['banner', 'video', 'inImage', 'inScreen'] + placements.forEach(placement => { + it('should be ' + placement, function() { + const isBidRequestValid = spec.isBidRequestValid( + createBannerSlotConfig(placement) + ) + expect(isBidRequestValid).to.equal(true) + }) + }) + }) + }) + describe('when video slot has all mandatory params.', function() { + it('should return true, when video mediatype object are correct.', function() { + const slotConfig = getSlotConfigs( + { + video: { + context: 'instream', + playerSize: [[600, 200]] + } + }, + { + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, + placement: 'video' + } + ) + const isBidRequestValid = spec.isBidRequestValid(slotConfig) + expect(isBidRequestValid).to.equal(true) + }) + }) + }) + describe('returns false', function() { + describe('when params are not correct', function() { + function createSlotconfig(params) { + return getSlotConfigs({ banner: {} }, params) + } + it('does not have the PublisherToken.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + adUnitId: '000000', + placement: 'banner' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have the AdUnitId.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + publisherId: '0000-0000-01', + placement: 'banner' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have the placement.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + publisherId: '0000-0000-01', + adUnitId: '000000' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have a the correct placement.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + publisherId: '0000-0000-01', + adUnitId: '000000', + placement: 'another_thing' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + }) + describe('when video mediaType object is not correct.', function() { + function createVideoSlotconfig(mediaType) { + return getSlotConfigs(mediaType, { + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, + placement: 'video' + }) + } + it('is a void object', function() { + const isBidRequestValid = spec.isBidRequestValid( + createVideoSlotconfig({ video: {} }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have playerSize.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createVideoSlotconfig({ video: { context: 'instream' } }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('is not instream ', function() { + const isBidRequestValid = spec.isBidRequestValid( + createVideoSlotconfig({ + video: { + context: 'outstream', + playerSize: [[600, 200]] + } + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + }) + }) + }) + + describe('buildRequests method', function() { + const bidderRequest = { + refererInfo: { referer: 'referer' }, + timeout: 1000 + } + const mandatoryParams = { + publisherId: '0000-0000-01', + adUnitId: '000000', + placement: 'banner' + } + const inStreamParams = Object.assign({}, mandatoryParams, { + video: { + mimes: 'mp4' + } + }) + const validBidRequests = [ + getSlotConfigs({ banner: {} }, mandatoryParams), + getSlotConfigs( + { video: { context: 'instream', playerSize: [[300, 200]] } }, + inStreamParams + ) + ] + it('Url params should be correct ', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + expect(request.method).to.equal('POST') + expect(request.url).to.equal('https://s.seedtag.com/c/hb/bid') + }) + + it('Common data request should be correct', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.url).to.equal('referer') + expect(data.publisherToken).to.equal('0000-0000-01') + expect(typeof data.version).to.equal('string') + }) + + describe('adPosition param', function() { + it('should sended when publisher set adPosition param', function() { + const params = Object.assign({}, mandatoryParams, { + adPosition: 1 + }) + const validBidRequests = [getSlotConfigs({ banner: {} }, params)] + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.bidRequests[0].adPosition).to.equal(1) + }) + it('should not sended when publisher has not set adPosition param', function() { + const validBidRequests = [ + getSlotConfigs({ banner: {} }, mandatoryParams) + ] + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.bidRequests[0].adPosition).to.equal(undefined) + }) + }) + + describe('GDPR params', function() { + describe('when there arent consent management platform', function() { + it('cmp should be false', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(false) + }) + }) + describe('when there are consent management platform', function() { + it('cmps should be true and ga should not sended, when gdprApplies is undefined', function() { + bidderRequest['gdprConsent'] = { + gdprApplies: undefined, + consentString: 'consentString' + } + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(true) + expect(Object.keys(data).indexOf('data')).to.equal(-1) + expect(data.cd).to.equal('consentString') + }) + it('cmps should be true and all gdpr parameters should be sended, when there are gdprApplies', function() { + bidderRequest['gdprConsent'] = { + gdprApplies: true, + consentString: 'consentString' + } + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(true) + expect(data.ga).to.equal(true) + expect(data.cd).to.equal('consentString') + }) + }) + }) + + describe('BidRequests params', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + const bidRequests = data.bidRequests + it('should request a Banner', function() { + const bannerBid = bidRequests[0] + expect(bannerBid.id).to.equal('30b31c1838de1e') + expect(bannerBid.transactionId).to.equal( + 'd704d006-0d6e-4a09-ad6c-179e7e758096' + ) + expect(bannerBid.supplyTypes[0]).to.equal('display') + expect(bannerBid.adUnitId).to.equal('000000') + expect(bannerBid.sizes[0][0]).to.equal(300) + expect(bannerBid.sizes[0][1]).to.equal(250) + expect(bannerBid.sizes[1][0]).to.equal(300) + expect(bannerBid.sizes[1][1]).to.equal(600) + }) + it('should request an InStream Video', function() { + const videoBid = bidRequests[1] + expect(videoBid.id).to.equal('30b31c1838de1e') + expect(videoBid.transactionId).to.equal( + 'd704d006-0d6e-4a09-ad6c-179e7e758096' + ) + expect(videoBid.supplyTypes[0]).to.equal('video') + expect(videoBid.adUnitId).to.equal('000000') + expect(videoBid.videoParams.mimes).to.equal('mp4') + expect(videoBid.videoParams.w).to.equal(300) + expect(videoBid.videoParams.h).to.equal(200) + expect(videoBid.sizes[0][0]).to.equal(300) + expect(videoBid.sizes[0][1]).to.equal(250) + expect(videoBid.sizes[1][0]).to.equal(300) + expect(videoBid.sizes[1][1]).to.equal(600) + }) + }) + }) + + describe('interpret response method', function() { + it('should return a void array, when the server response are not correct.', function() { + const request = { data: JSON.stringify({}) } + const serverResponse = { + body: {} + } + const bids = spec.interpretResponse(serverResponse, request) + expect(typeof bids).to.equal('object') + expect(bids.length).to.equal(0) + }) + it('should return a void array, when the server response have not got bids.', function() { + const request = { data: JSON.stringify({}) } + const serverResponse = { body: { bids: [] } } + const bids = spec.interpretResponse(serverResponse, request) + expect(typeof bids).to.equal('object') + expect(bids.length).to.equal(0) + }) + describe('when the server response return a bid', function() { + describe('the bid is a banner', function() { + it('should return a banner bid', function() { + const request = { data: JSON.stringify({}) } + const serverResponse = { + body: { + bids: [ + { + bidId: '2159a54dc2566f', + price: 0.5, + currency: 'USD', + content: 'content', + width: 728, + height: 90, + mediaType: 'display', + ttl: 360 + } + ], + cookieSync: { url: '' } + } + } + const bids = spec.interpretResponse(serverResponse, request) + expect(bids.length).to.equal(1) + expect(bids[0].requestId).to.equal('2159a54dc2566f') + expect(bids[0].cpm).to.equal(0.5) + expect(bids[0].width).to.equal(728) + expect(bids[0].height).to.equal(90) + expect(bids[0].currency).to.equal('USD') + expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].ad).to.equal('content') + }) + }) + describe('the bid is a video', function() { + it('should return a instream bid', function() { + const request = { data: JSON.stringify({}) } + const serverResponse = { + body: { + bids: [ + { + bidId: '2159a54dc2566f', + price: 0.5, + currency: 'USD', + content: 'content', + width: 728, + height: 90, + mediaType: 'video', + ttl: 360 + } + ], + cookieSync: { url: '' } + } + } + const bids = spec.interpretResponse(serverResponse, request) + expect(bids.length).to.equal(1) + expect(bids[0].requestId).to.equal('2159a54dc2566f') + expect(bids[0].cpm).to.equal(0.5) + expect(bids[0].width).to.equal(728) + expect(bids[0].height).to.equal(90) + expect(bids[0].currency).to.equal('USD') + expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].vastXml).to.equal('content') + }) + }) + }) + }) + + describe('user syncs method', function() { + it('should return empty array, when iframe sync option are disabled.', function() { + const syncOption = { iframeEnabled: false } + const serverResponses = [{ body: { cookieSync: 'someUrl' } }] + const cookieSyncArray = spec.getUserSyncs(syncOption, serverResponses) + expect(cookieSyncArray.length).to.equal(0) + }) + it('should return empty array, when the server response are wrong.', function() { + const syncOption = { iframeEnabled: true } + const serverResponses = [{ body: {} }] + const cookieSyncArray = spec.getUserSyncs(syncOption, serverResponses) + expect(cookieSyncArray.length).to.equal(0) + }) + it('should return empty array, when the server response are void.', function() { + const syncOption = { iframeEnabled: true } + const serverResponses = [{ body: { cookieSync: '' } }] + const cookieSyncArray = spec.getUserSyncs(syncOption, serverResponses) + expect(cookieSyncArray.length).to.equal(0) + }) + it('should return a array with the cookie sync, when the server response with a cookie sync.', function() { + const syncOption = { iframeEnabled: true } + const serverResponses = [{ body: { cookieSync: 'someUrl' } }] + const cookieSyncArray = spec.getUserSyncs(syncOption, serverResponses) + expect(cookieSyncArray.length).to.equal(1) + expect(cookieSyncArray[0].type).to.equal('iframe') + expect(cookieSyncArray[0].url).to.equal('someUrl') + }) + }) + + describe('onTimeout', function () { + it('should return the correct endpoint', function () { + const params = { publisherId: '0000', adUnitId: '11111' } + const timeoutData = [{ params: [ params ] }]; + const timeoutUrl = getTimeoutUrl(timeoutData); + expect(timeoutUrl).to.equal( + 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + + params.publisherId + + '&adUnitId=' + + params.adUnitId + ) + }) + }) +}) diff --git a/test/spec/modules/sekindoUMBidAdapter_spec.js b/test/spec/modules/sekindoUMBidAdapter_spec.js index a5731da0789..b699015bb3e 100644 --- a/test/spec/modules/sekindoUMBidAdapter_spec.js +++ b/test/spec/modules/sekindoUMBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec } from 'modules/sekindoUMBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -describe('sekindoUMAdapter', () => { +describe('sekindoUMAdapter', function () { const adapter = newBidder(spec); const bannerParams = { @@ -29,34 +29,34 @@ describe('sekindoUMAdapter', () => { 'mediaType': 'banner' }; - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - it('should return true when required params found', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { bidRequests.mediaType = 'banner'; bidRequests.params = bannerParams; expect(spec.isBidRequestValid(bidRequests)).to.equal(true); }); - it('should return false when required video params are missing', () => { + it('should return false when required video params are missing', function () { bidRequests.mediaType = 'video'; bidRequests.params = bannerParams; expect(spec.isBidRequestValid(bidRequests)).to.equal(false); }); - it('should return true when required Video params found', () => { + it('should return true when required Video params found', function () { bidRequests.mediaType = 'video'; bidRequests.params = videoParams; expect(spec.isBidRequestValid(bidRequests)).to.equal(true); }); }); - describe('buildRequests', () => { - it('banner data should be a query string and method = GET', () => { + describe('buildRequests', function () { + it('banner data should be a query string and method = GET', function () { bidRequests.mediaType = 'banner'; bidRequests.params = bannerParams; const request = spec.buildRequests([bidRequests]); @@ -64,7 +64,15 @@ describe('sekindoUMAdapter', () => { expect(request[0].method).to.equal('GET'); }); - it('video data should be a query string and method = GET', () => { + it('with gdprConsent, banner data should be a query string and method = GET', function () { + bidRequests.mediaType = 'banner'; + bidRequests.params = bannerParams; + const request = spec.buildRequests([bidRequests], {'gdprConsent': {'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', 'vendorData': {}, 'gdprApplies': true}}); + expect(request[0].data).to.be.a('string'); + expect(request[0].method).to.equal('GET'); + }); + + it('video data should be a query string and method = GET', function () { bidRequests.mediaType = 'video'; bidRequests.params = videoParams; const request = spec.buildRequests([bidRequests]); @@ -73,8 +81,8 @@ describe('sekindoUMAdapter', () => { }); }); - describe('interpretResponse', () => { - it('banner should get correct bid response', () => { + describe('interpretResponse', function () { + it('banner should get correct bid response', function () { let response = { 'headers': function(header) { return 'dummy header'; @@ -102,7 +110,7 @@ describe('sekindoUMAdapter', () => { expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); }); - it('vastXml video should get correct bid response', () => { + it('vastXml video should get correct bid response', function () { let response = { 'headers': function(header) { return 'dummy header'; @@ -130,7 +138,7 @@ describe('sekindoUMAdapter', () => { expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); }); - it('vastUrl video should get correct bid response', () => { + it('vastUrl video should get correct bid response', function () { let response = { 'headers': function(header) { return 'dummy header'; diff --git a/test/spec/modules/serverbidBidAdapter_spec.js b/test/spec/modules/serverbidBidAdapter_spec.js index d3dc64ae6df..8949463e151 100644 --- a/test/spec/modules/serverbidBidAdapter_spec.js +++ b/test/spec/modules/serverbidBidAdapter_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/serverbidBidAdapter'; - -var bidFactory = require('src/bidfactory.js'); +import { createBid } from 'src/bidfactory'; const ENDPOINT = 'https://e.serverbid.com/api/v2'; const SMARTSYNC_CALLBACK = 'serverbidCallBids'; @@ -100,11 +99,11 @@ const RESPONSE = { } }; -describe('Serverbid BidAdapter', () => { +describe('Serverbid BidAdapter', function () { let bidRequests; let adapter = spec; - beforeEach(() => { + beforeEach(function () { bidRequests = [ { bidder: 'serverbid', @@ -122,8 +121,8 @@ describe('Serverbid BidAdapter', () => { ]; }); - describe('bid request validation', () => { - it('should accept valid bid requests', () => { + describe('bid request validation', function () { + it('should accept valid bid requests', function () { let bid = { bidder: 'serverbid', params: { @@ -134,7 +133,7 @@ describe('Serverbid BidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should accept valid bid requests with extra fields', () => { + it('should accept valid bid requests with extra fields', function () { let bid = { bidder: 'serverbid', params: { @@ -146,7 +145,7 @@ describe('Serverbid BidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should reject bid requests without siteId', () => { + it('should reject bid requests without siteId', function () { let bid = { bidder: 'serverbid', params: { @@ -156,7 +155,7 @@ describe('Serverbid BidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should reject bid requests without networkId', () => { + it('should reject bid requests without networkId', function () { let bid = { bidder: 'serverbid', params: { @@ -167,45 +166,45 @@ describe('Serverbid BidAdapter', () => { }); }); - describe('buildRequests validation', () => { - it('creates request data', () => { + describe('buildRequests validation', function () { + it('creates request data', function () { let request = spec.buildRequests(bidRequests); expect(request).to.exist.and.to.be.a('object'); }); - it('request to serverbid should contain a url', () => { + it('request to serverbid should contain a url', function () { let request = spec.buildRequests(bidRequests); expect(request.url).to.have.string('serverbid.com'); }); - it('requires valid bids to make request', () => { + it('requires valid bids to make request', function () { let request = spec.buildRequests([]); expect(request.bidRequest).to.be.empty; }); - it('sends bid request to ENDPOINT via POST', () => { + it('sends bid request to ENDPOINT via POST', function () { let request = spec.buildRequests(bidRequests); expect(request.method).to.have.string('POST'); }); }); - describe('interpretResponse validation', () => { - it('response should have valid bidderCode', () => { + describe('interpretResponse validation', function () { + it('response should have valid bidderCode', function () { let bidRequest = spec.buildRequests(REQUEST.bidRequest); - let bid = bidFactory.createBid(1, bidRequest.bidRequest[0]); + let bid = createBid(1, bidRequest.bidRequest[0]); expect(bid.bidderCode).to.equal('serverbid'); }); - it('response should include objects for all bids', () => { + it('response should include objects for all bids', function () { let bids = spec.interpretResponse(RESPONSE, REQUEST); expect(bids.length).to.equal(2); }); - it('registers bids', () => { + it('registers bids', function () { let bids = spec.interpretResponse(RESPONSE, REQUEST); bids.forEach(b => { expect(b).to.have.property('cpm'); @@ -223,29 +222,29 @@ describe('Serverbid BidAdapter', () => { }); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {'decisions': null}}) let bids = spec.interpretResponse(EMPTY_RESP, REQUEST); expect(bids).to.be.empty; }); - it('handles no server response', () => { + it('handles no server response', function () { let bids = spec.interpretResponse(null, REQUEST); expect(bids).to.be.empty; }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs', function () { let syncOptions = {'iframeEnabled': true}; - it('handles empty sync options', () => { + it('handles empty sync options', function () { let opts = spec.getUserSyncs({}); - expect(opts).to.be.empty; + expect(opts).to.be.undefined; }); - it('should return a sync url if iframe syncs are enabled', () => { + it('should return a sync url if iframe syncs are enabled', function () { let opts = spec.getUserSyncs(syncOptions); expect(opts.length).to.equal(1); diff --git a/test/spec/modules/serverbidServerBidAdapter_spec.js b/test/spec/modules/serverbidServerBidAdapter_spec.js index 29d35b921d6..1190648bb84 100644 --- a/test/spec/modules/serverbidServerBidAdapter_spec.js +++ b/test/spec/modules/serverbidServerBidAdapter_spec.js @@ -184,51 +184,55 @@ const REQUEST_TWO_UNITS = { ] }; -describe('ServerBid S2S Adapter', () => { +describe('ServerBid S2S Adapter', function () { let adapter, addBidResponse = sinon.spy(), done = sinon.spy(); - beforeEach(() => adapter = new Adapter()); + beforeEach(function () { + adapter = new Adapter() + }); - afterEach(() => { + afterEach(function () { addBidResponse.resetHistory(); done.resetHistory(); }); - describe('request function', () => { + describe('request function', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = request => requests.push(request); }); - afterEach(() => xhr.restore()); + afterEach(function () { + xhr.restore(); + }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('response handler', () => { + describe('response handler', function () { let server; - beforeEach(() => { + beforeEach(function () { server = sinon.fakeServer.create(); sinon.stub(utils, 'getBidRequest').returns({ bidId: '123' }); }); - afterEach(() => { + afterEach(function () { server.restore(); utils.getBidRequest.restore(); }); - it('registers bids', () => { + it('registers bids', function () { server.respondWith(JSON.stringify(RESPONSE)); config.setConfig(CONFIG_ARG); @@ -239,10 +243,10 @@ describe('ServerBid S2S Adapter', () => { const response = addBidResponse.firstCall.args[1]; expect(response).to.have.property('statusMessage', 'Bid available'); expect(response).to.have.property('cpm', 0.5); - expect(response).to.have.property('adId', '123'); + expect(response).to.have.property('requestId', '123'); }); - it('registers no-bid response when ad unit not set', () => { + it('registers no-bid response when ad unit not set', function () { server.respondWith(JSON.stringify(RESPONSE_NO_BID_NO_UNIT)); config.setConfig(CONFIG_ARG); @@ -257,10 +261,10 @@ describe('ServerBid S2S Adapter', () => { expect(response).to.have.property('statusMessage', 'Bid returned empty or error response'); const bid_request_passed = addBidResponse.firstCall.args[1]; - expect(bid_request_passed).to.have.property('adId', '123'); + expect(bid_request_passed).to.have.property('requestId', '123'); }); - it('registers no-bid response when ad unit is set', () => { + it('registers no-bid response when ad unit is set', function () { server.respondWith(JSON.stringify(RESPONSE_NO_BID_NO_UNIT)); config.setConfig(CONFIG_ARG); @@ -275,7 +279,7 @@ describe('ServerBid S2S Adapter', () => { expect(response).to.have.property('statusMessage', 'Bid returned empty or error response'); }); - it('registers no-bid response when there are less bids than requests', () => { + it('registers no-bid response when there are less bids than requests', function () { server.respondWith(JSON.stringify(RESPONSE)); config.setConfig(CONFIG_ARG); @@ -287,8 +291,8 @@ describe('ServerBid S2S Adapter', () => { expect(addBidResponse.firstCall.args[0]).to.equal('div-gpt-ad-1460505748561-0'); expect(addBidResponse.secondCall.args[0]).to.equal('div-gpt-ad-1460505748561-1'); - expect(addBidResponse.firstCall.args[1]).to.have.property('adId', '123'); - expect(addBidResponse.secondCall.args[1]).to.have.property('adId', '101111'); + expect(addBidResponse.firstCall.args[1]).to.have.property('requestId', '123'); + expect(addBidResponse.secondCall.args[1]).to.have.property('requestId', '101111'); expect(addBidResponse.firstCall.args[1]) .to.have.property('statusMessage', 'Bid available'); diff --git a/test/spec/modules/shBidAdapter_spec.js b/test/spec/modules/shBidAdapter_spec.js new file mode 100644 index 00000000000..588d4c54150 --- /dev/null +++ b/test/spec/modules/shBidAdapter_spec.js @@ -0,0 +1,187 @@ +import {expect} from 'chai' +import {spec} from 'modules/shBidAdapter' +import {newBidder} from 'src/adapters/bidderFactory' +import {VIDEO, BANNER} from 'src/mediaTypes' + +const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } +} + +const gdpr = { + 'gdprConsent': { + 'consentString': 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + 'gdprApplies': true + } +} + +const bidRequestVideo = { + 'bidder': 'showheroes-bs', + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream', + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + +const bidRequestVideoVpaid = { + 'bidder': 'showheroes-bs', + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + 'vpaidMode': true, + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream', + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + +const bidRequestBanner = { + 'bidder': 'showheroes-bs', + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 360]] + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + +describe('shBidAdapter', function () { + const adapter = newBidder(spec) + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function') + }) + }) + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + const request = { + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + } + } + expect(spec.isBidRequestValid(request)).to.equal(true) + }) + + it('should return false when required params are not passed', function () { + const request = { + 'params': {} + } + expect(spec.isBidRequestValid(request)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests([bidRequestVideo], bidderRequest) + expect(request.method).to.equal('POST') + }) + + it('should attach valid params to the payload when type is video', function () { + const request = spec.buildRequests([bidRequestVideo], bidderRequest) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload).to.have.property('mediaType', VIDEO); + expect(payload).to.have.property('type', 2); + }) + + it('should attach valid params to the payload when type is video & vpaid mode on', function () { + const request = spec.buildRequests([bidRequestVideoVpaid], bidderRequest) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload).to.have.property('mediaType', VIDEO); + expect(payload).to.have.property('type', 1); + }) + + it('should attach valid params to the payload when type is banner', function () { + const request = spec.buildRequests([bidRequestBanner], bidderRequest) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload).to.have.property('mediaType', BANNER); + expect(payload).to.have.property('type', 5); + }) + + it('passes gdpr if present', function () { + const request = spec.buildRequests([bidRequestVideo], {...bidderRequest, ...gdpr}) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload.gdprConsent).to.eql(gdpr.gdprConsent) + }) + }) + + describe('interpretResponse', function () { + it('handles nobid responses', function () { + expect(spec.interpretResponse({body: {}}, {data: {meta: {}}}).length).to.equal(0) + expect(spec.interpretResponse({body: []}, {data: {meta: {}}}).length).to.equal(0) + }) + + const response = { + 'bids': [{ + 'cpm': 5, + 'currency': 'EUR', + 'bidId': '38b373e1e31c18', + 'video': {'width': 640, 'height': 480}, + 'vastTag': 'https:\/\/video-library.stage.showheroes.com\/commercial\/wrapper?player_id=47427aa0-f11a-4d24-abca-1295a46a46cd&ad_bidder=showheroes-bs&master_shadt=1&description_url=https%3A%2F%2Fbid-service.stage.showheroes.com%2Fvast%2Fad%2Fcache%2F4840b920-40e1-4e09-9231-60bbf088c8d6', + }], + } + + it('should get correct bid response when type is video', function () { + const request = spec.buildRequests([bidRequestVideo], bidderRequest) + const expectedResponse = [ + { + 'cpm': 5, + 'creativeId': 'c_38b373e1e31c18', + 'currency': 'EUR', + 'width': 640, + 'height': 480, + 'mediaType': 'video', + 'netRevenue': true, + 'vastUrl': 'https://video-library.stage.showheroes.com/commercial/wrapper?player_id=47427aa0-f11a-4d24-abca-1295a46a46cd&ad_bidder=showheroes-bs&master_shadt=1&description_url=https%3A%2F%2Fbid-service.stage.showheroes.com%2Fvast%2Fad%2Fcache%2F4840b920-40e1-4e09-9231-60bbf088c8d6', + 'requestId': '38b373e1e31c18', + 'ttl': 300, + } + ] + + const result = spec.interpretResponse({'body': response}, request) + expect(result).to.deep.equal(expectedResponse) + }) + + it('should get correct bid response when type is banner', function () { + const request = spec.buildRequests([bidRequestBanner], bidderRequest) + + const result = spec.interpretResponse({'body': response}, request) + expect(result[0]).to.have.property('mediaType', BANNER); + expect(result[0].ad).to.include('"'; + +describe('slimcutBidAdapter', function() { + const adapter = newBidder(spec); + + describe('inherited functions', function() { + it('exists and is a function', function() { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function() { + let bid = { + 'bidder': 'slimcut', + 'params': { + 'placementId': 83 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '3c871ffa8ef14c', + 'bidderRequestId': 'b41642f1aee381', + 'auctionId': '4e156668c977d7' + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when placementId is not valid (letters)', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 'ABCD' + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when placementId < 0', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': -1 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function() { + let bid = Object.assign({}, bid); + delete bid.params; + + bid.params = {}; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + let bidRequests = [ + { + 'bidder': 'teads', + 'params': { + 'placementId': 10433394 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '3c871ffa8ef14c', + 'bidderRequestId': 'b41642f1aee381', + 'auctionId': '4e156668c977d7', + 'deviceWidth': 1680 + } + ]; + + let bidderResquestDefault = { + 'auctionId': '4e156668c977d7', + 'bidderRequestId': 'b41642f1aee381', + 'timeout': 3000 + }; + + it('sends bid request to ENDPOINT via POST', function() { + const request = spec.buildRequests(bidRequests, bidderResquestDefault); + + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should send GDPR to endpoint', function() { + let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; + let bidderRequest = { + 'auctionId': '4e156668c977d7', + 'bidderRequestId': 'b41642f1aee381', + 'timeout': 3000, + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true, + 'vendorData': { + 'hasGlobalConsent': false + } + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_iab).to.exist; + expect(payload.gdpr_iab.consent).to.equal(consentString); + }); + + it('should add referer info to payload', function () { + const bidRequest = Object.assign({}, bidRequests[0]) + const bidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2 + } + } + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.referrer).to.exist; + expect(payload.referrer).to.deep.equal('http://example.com/page.html') + }); + }); + + describe('getUserSyncs', () => { + let bids = { + 'body': { + 'responses': [{ + 'ad': AD_SCRIPT, + 'cpm': 0.5, + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 360, + 'width': 300, + 'creativeId': 'er2ee', + 'transactionId': 'deadb33f', + 'winUrl': 'https://sb.freeskreen.com/win' + }] + } + }; + + it('should get the correct number of sync urls', () => { + let urls = spec.getUserSyncs({iframeEnabled: true}, bids); + expect(urls.length).to.equal(1); + expect(urls[0].url).to.equal('//sb.freeskreen.com/async_usersync.html'); + }); + + it('should return no url if not iframe enabled', () => { + let urls = spec.getUserSyncs({iframeEnabled: false}, bids); + expect(urls.length).to.equal(0); + }); + }); + + describe('interpretResponse', function() { + let bids = { + 'body': { + 'responses': [{ + 'ad': AD_SCRIPT, + 'cpm': 0.5, + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 360, + 'width': 300, + 'creativeId': 'er2ee', + 'transactionId': 'deadb33f', + 'winUrl': 'https://sb.freeskreen.com/win' + }] + } + }; + + it('should get correct bid response', function() { + let expectedResponse = [{ + 'cpm': 0.5, + 'width': 300, + 'height': 250, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + 'ad': AD_SCRIPT, + 'requestId': '3ede2a3fa0db94', + 'creativeId': 'er2ee', + 'transactionId': 'deadb33f', + 'winUrl': 'https://sb.freeskreen.com/win' + }]; + + let result = spec.interpretResponse(bids); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function() { + let bids = { + 'body': { + 'responses': [] + } + }; + + let result = spec.interpretResponse(bids); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 57c070e9748..c5ae9e59054 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -11,9 +11,10 @@ import { config } from 'src/config'; import * as utils from 'src/utils'; +import { requestBidsHook } from 'modules/consentManagement'; // Default params with optional ones -describe('Smart bid adapter tests', () => { +describe('Smart bid adapter tests', function () { var DEFAULT_PARAMS = [{ adUnitCode: 'sas_42', bidId: 'abcd1234', @@ -70,7 +71,7 @@ describe('Smart bid adapter tests', () => { } }; - it('Verify build request', () => { + it('Verify build request', function () { config.setConfig({ 'currency': { 'adServerCurrency': 'EUR' @@ -99,52 +100,59 @@ describe('Smart bid adapter tests', () => { expect(requestContent).to.have.property('ckid').and.to.equal(42); }); - it('Verify build request with GDPR', () => { - config.setConfig({ - 'currency': { - 'adServerCurrency': 'EUR' - }, - consentManagement: { - cmp: 'iab', - consentRequired: true, - timeout: 1000, - allowAuctionWithoutConsent: true - } + describe('gdpr tests', function () { + afterEach(function () { + config.resetConfig(); + $$PREBID_GLOBAL$$.requestBids.removeAll(); }); - const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, { - gdprConsent: { - consentString: 'BOKAVy4OKAVy4ABAB8AAAAAZ+A==', - gdprApplies: true - } - }); - const requestContent = JSON.parse(request[0].data); - expect(requestContent).to.have.property('gdpr').and.to.equal(true); - expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOKAVy4OKAVy4ABAB8AAAAAZ+A=='); - }); - it('Verify build request with GDPR without gdprApplies', () => { - config.setConfig({ - 'currency': { - 'adServerCurrency': 'EUR' - }, - consentManagement: { - cmp: 'iab', - consentRequired: true, - timeout: 1000, - allowAuctionWithoutConsent: true - } + it('Verify build request with GDPR', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + }, + consentManagement: { + cmp: 'iab', + consentRequired: true, + timeout: 1000, + allowAuctionWithoutConsent: true + } + }); + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, { + gdprConsent: { + consentString: 'BOKAVy4OKAVy4ABAB8AAAAAZ+A==', + gdprApplies: true + } + }); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('gdpr').and.to.equal(true); + expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOKAVy4OKAVy4ABAB8AAAAAZ+A=='); }); - const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, { - gdprConsent: { - consentString: 'BOKAVy4OKAVy4ABAB8AAAAAZ+A==' - } + + it('Verify build request with GDPR without gdprApplies', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + }, + consentManagement: { + cmp: 'iab', + consentRequired: true, + timeout: 1000, + allowAuctionWithoutConsent: true + } + }); + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, { + gdprConsent: { + consentString: 'BOKAVy4OKAVy4ABAB8AAAAAZ+A==' + } + }); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.not.have.property('gdpr'); + expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOKAVy4OKAVy4ABAB8AAAAAZ+A=='); }); - const requestContent = JSON.parse(request[0].data); - expect(requestContent).to.not.have.property('gdpr'); - expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOKAVy4OKAVy4ABAB8AAAAAZ+A=='); }); - it('Verify parse response', () => { + it('Verify parse response', function () { const request = spec.buildRequests(DEFAULT_PARAMS); const bids = spec.interpretResponse(BID_RESPONSE, request[0]); expect(bids).to.have.lengthOf(1); @@ -168,16 +176,16 @@ describe('Smart bid adapter tests', () => { }).to.not.throw(); }); - it('Verifies bidder code', () => { + it('Verifies bidder code', function () { expect(spec.code).to.equal('smartadserver'); }); - it('Verifies bidder aliases', () => { + it('Verifies bidder aliases', function () { expect(spec.aliases).to.have.lengthOf(1); expect(spec.aliases[0]).to.equal('smart'); }); - it('Verifies if bid request valid', () => { + it('Verifies if bid request valid', function () { expect(spec.isBidRequestValid(DEFAULT_PARAMS[0])).to.equal(true); expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true); expect(spec.isBidRequestValid({})).to.equal(false); @@ -235,7 +243,7 @@ describe('Smart bid adapter tests', () => { })).to.equal(false); }); - it('Verifies user sync', () => { + it('Verifies user sync', function () { var syncs = spec.getUserSyncs({ iframeEnabled: true }, [BID_RESPONSE]); diff --git a/test/spec/modules/smartrtbBidAdapter_spec.js b/test/spec/modules/smartrtbBidAdapter_spec.js new file mode 100644 index 00000000000..8f1fb9efc1e --- /dev/null +++ b/test/spec/modules/smartrtbBidAdapter_spec.js @@ -0,0 +1,109 @@ +import { expect } from 'chai' +import { spec, _getPlatform } from 'modules/smartrtbBidAdapter' +import { newBidder } from 'src/adapters/bidderFactory' + +describe('SmartRTBBidAdapter', function () { + const adapter = newBidder(spec) + + let bidRequest = { + bidId: '123', + transactionId: '456', + sizes: [[ 300, 250 ]], + params: { + pubId: 123, + medId: 'm_00a95d003340dbb2fcb8ee668a84fa', + zoneId: 'z_261b6c7e7d4d4985393b293cc903d1' + } + } + + describe('codes', function () { + it('should return a bidder code of smartrtb', function () { + expect(spec.code).to.equal('smartrtb') + }) + it('should alias smrtb', function () { + expect(spec.aliases.length > 0 && spec.aliases[0] === 'smrtb').to.be.true + }) + }) + + describe('isBidRequestValid', function () { + it('should return true if all params present', function () { + expect(spec.isBidRequestValid(bidRequest)).to.be.true + }) + + it('should return false if any parameter missing', function () { + expect(spec.isBidRequestValid(Object.assign(bidRequest, { params: { pubId: null } }))).to.be.false + expect(spec.isBidRequestValid(Object.assign(bidRequest, { params: { medId: null } }))).to.be.false + expect(spec.isBidRequestValid(Object.assign(bidRequest, { params: { zoneId: null } }))).to.be.false + }) + }) + + describe('buildRequests', function () { + let req = spec.buildRequests([ bidRequest ], { refererInfo: { } }) + let rdata + + it('should return request object', function () { + expect(req).to.not.be.null + }) + + it('should build request data', function () { + expect(req.data).to.not.be.null + }) + + it('should include one request', function () { + rdata = JSON.parse(req.data) + expect(rdata.imps.length).to.equal(1) + }) + + it('should include all publisher params', function () { + let r = rdata.imps[0] + expect(r.pub_id !== null && r.med_id !== null && r.zone_id !== null).to.be.true + }) + }) + + describe('interpretResponse', function () { + it('should form compliant bid object response', function () { + let res = { + body: { + bids: [{ + bid_id: '123', + cpm: 1.23, + w: 300, + h: 250, + html: 'deadbeef', + crid: 'crid' + }] + } + } + + let ir = spec.interpretResponse(res, bidRequest) + + expect(ir.length).to.equal(1) + + let en = ir[0] + + expect(en.requestId != null && + en.cpm != null && typeof en.cpm === 'number' && + en.width != null && typeof en.width === 'number' && + en.height != null && typeof en.height === 'number' && + en.ad != null && + en.creativeId != null + ).to.be.true + }) + }) + + describe('getUserSyncs', function () { + it('should return iframe sync', function () { + let sync = spec.getUserSyncs({ iframeEnabled: true }) + expect(sync.length).to.equal(1) + expect(sync[0].type === 'iframe') + expect(typeof sync[0].url === 'string') + }) + + it('should return pixel sync', function () { + let sync = spec.getUserSyncs({ pixelEnabled: true }) + expect(sync.length).to.equal(1) + expect(sync[0].type === 'image') + expect(typeof sync[0].url === 'string') + }) + }) +}) diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 858e8bf37a0..e301e9733a6 100644 --- a/test/spec/modules/smartyadsBidAdapter_spec.js +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import {spec} from '../../../modules/smartyadsBidAdapter'; -describe('SmartyadsAdapter', () => { +describe('SmartyadsAdapter', function () { let bid = { bidId: '23fhj33i987f', bidder: 'smartyads', @@ -11,31 +11,31 @@ describe('SmartyadsAdapter', () => { } }; - describe('isBidRequestValid', () => { - it('Should return true if there are bidId, params and placementId parameters present', () => { + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and placementId parameters present', function () { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false if at least one of parameters is not present', () => { + it('Should return false if at least one of parameters is not present', function () { delete bid.params.placementId; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let serverRequest = spec.buildRequests([bid]); - it('Creates a ServerRequest object with method, URL and data', () => { + it('Creates a ServerRequest object with method, URL and data', function () { expect(serverRequest).to.exist; expect(serverRequest.method).to.exist; expect(serverRequest.url).to.exist; expect(serverRequest.data).to.exist; }); - it('Returns POST method', () => { + it('Returns POST method', function () { expect(serverRequest.method).to.equal('POST'); }); - it('Returns valid URL', () => { + it('Returns valid URL', function () { expect(serverRequest.url).to.equal('//ssp-nj.webtradehub.com/?c=o&m=multi'); }); - it('Returns valid data if array of bids is valid', () => { + it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; expect(data).to.be.an('object'); expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); @@ -51,14 +51,14 @@ describe('SmartyadsAdapter', () => { expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal('banner'); }); - it('Returns empty data if no valid requests are passed', () => { + it('Returns empty data if no valid requests are passed', function () { serverRequest = spec.buildRequests([]); let data = serverRequest.data; expect(data.placements).to.be.an('array').that.is.empty; }); }); - describe('interpretResponse', () => { - it('Should interpret banner response', () => { + describe('interpretResponse', function () { + it('Should interpret banner response', function () { const banner = { body: [{ mediaType: 'banner', @@ -89,7 +89,7 @@ describe('SmartyadsAdapter', () => { expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal('USD'); }); - it('Should interpret video response', () => { + it('Should interpret video response', function () { const video = { body: [{ vastUrl: 'test.com', @@ -118,7 +118,7 @@ describe('SmartyadsAdapter', () => { expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal('USD'); }); - it('Should interpret native response', () => { + it('Should interpret native response', function () { const native = { body: [{ mediaType: 'native', @@ -152,7 +152,7 @@ describe('SmartyadsAdapter', () => { expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal('USD'); }); - it('Should return an empty array if invalid banner response is passed', () => { + it('Should return an empty array if invalid banner response is passed', function () { const invBanner = { body: [{ width: 300, @@ -170,7 +170,7 @@ describe('SmartyadsAdapter', () => { 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', () => { + it('Should return an empty array if invalid video response is passed', function () { const invVideo = { body: [{ mediaType: 'video', @@ -186,7 +186,7 @@ describe('SmartyadsAdapter', () => { 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', () => { + it('Should return an empty array if invalid native response is passed', function () { const invNative = { body: [{ mediaType: 'native', @@ -203,7 +203,7 @@ describe('SmartyadsAdapter', () => { 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', () => { + it('Should return an empty array if invalid response is passed', function () { const invalid = { body: [{ ttl: 120, @@ -217,9 +217,9 @@ describe('SmartyadsAdapter', () => { expect(serverResponses).to.be.an('array').that.is.empty; }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs', function () { let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', () => { + it('Returns valid URL and type', function () { expect(userSync).to.be.an('array').with.lengthOf(1); expect(userSync[0].type).to.exist; expect(userSync[0].url).to.exist; diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js new file mode 100644 index 00000000000..144c7ca60f6 --- /dev/null +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -0,0 +1,325 @@ +import { expect } from 'chai'; +import { spec } from 'modules/smilewantedBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; +import * as utils from 'src/utils'; +import { requestBidsHook } from 'modules/consentManagement'; + +const DISPLAY_REQUEST = [{ + adUnitCode: 'sw_300x250', + bidId: '12345', + sizes: [ + [300, 250], + [300, 200] + ], + bidder: 'smilewanted', + params: { + zoneId: 1, + bidfloor: 2.50 + }, + requestId: 'request_abcd1234', + transactionId: 'trans_abcd1234' +}]; + +const BID_RESPONSE_DISPLAY = { + body: { + cpm: 3, + width: 300, + height: 250, + creativeId: 'crea_sw_1', + currency: 'EUR', + isNetCpm: true, + ttl: 300, + ad: '< --- sw script --- >', + cSyncUrl: 'https://csync.smilewanted.com' + } +}; + +const VIDEO_INSTREAM_REQUEST = [{ + code: 'video1', + mediaTypes: { + video: {} + }, + sizes: [ + [640, 480] + ], + bidder: 'smilewanted', + params: { + zoneId: 2, + bidfloor: 2.50 + }, + requestId: 'request_abcd1234', + transactionId: 'trans_abcd1234' +}]; + +const BID_RESPONSE_VIDEO_INSTREAM = { + body: { + cpm: 3, + width: 640, + height: 480, + creativeId: 'crea_sw_2', + currency: 'EUR', + isNetCpm: true, + ttl: 300, + ad: 'https://vast.smilewanted.com', + cSyncUrl: 'https://csync.smilewanted.com', + formatTypeSw: 'video_instream' + } +}; + +const VIDEO_OUTSTREAM_REQUEST = [{ + code: 'video1', + mediaTypes: { + video: {} + }, + sizes: [ + [640, 480] + ], + bidder: 'smilewanted', + params: { + zoneId: 3, + bidfloor: 2.50 + }, + requestId: 'request_abcd1234', + transactionId: 'trans_abcd1234' +}]; + +const BID_RESPONSE_VIDEO_OUTSTREAM = { + body: { + cpm: 3, + width: 640, + height: 480, + creativeId: 'crea_sw_3', + currency: 'EUR', + isNetCpm: true, + ttl: 300, + ad: 'https://vast.smilewanted.com', + cSyncUrl: 'https://csync.smilewanted.com', + OustreamTemplateUrl: 'https://prebid.smilewanted.com/scripts_outstream/infeed.js', + formatTypeSw: 'video_outstream' + } +}; + +// Default params with optional ones +describe('smilewantedBidAdapterTests', function () { + it('SmileWanted - Verify build request', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + + const requestDisplay = spec.buildRequests(DISPLAY_REQUEST); + expect(requestDisplay[0]).to.have.property('url').and.to.equal('https://prebid.smilewanted.com'); + expect(requestDisplay[0]).to.have.property('method').and.to.equal('POST'); + const requestDisplayContent = JSON.parse(requestDisplay[0].data); + expect(requestDisplayContent).to.have.property('zoneId').and.to.equal(1); + expect(requestDisplayContent).to.have.property('currencyCode').and.to.equal('EUR'); + expect(requestDisplayContent).to.have.property('bidfloor').and.to.equal(2.50); + expect(requestDisplayContent).to.have.property('sizes'); + expect(requestDisplayContent.sizes[0]).to.have.property('w').and.to.equal(300); + expect(requestDisplayContent.sizes[0]).to.have.property('h').and.to.equal(250); + expect(requestDisplayContent.sizes[1]).to.have.property('w').and.to.equal(300); + expect(requestDisplayContent.sizes[1]).to.have.property('h').and.to.equal(200); + expect(requestDisplayContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + + const requestVideoInstream = spec.buildRequests(VIDEO_INSTREAM_REQUEST); + expect(requestVideoInstream[0]).to.have.property('url').and.to.equal('https://prebid.smilewanted.com'); + expect(requestVideoInstream[0]).to.have.property('method').and.to.equal('POST'); + const requestVideoInstreamContent = JSON.parse(requestVideoInstream[0].data); + expect(requestVideoInstreamContent).to.have.property('zoneId').and.to.equal(2); + expect(requestVideoInstreamContent).to.have.property('currencyCode').and.to.equal('EUR'); + expect(requestVideoInstreamContent).to.have.property('bidfloor').and.to.equal(2.50); + expect(requestVideoInstreamContent).to.have.property('sizes'); + expect(requestVideoInstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); + expect(requestVideoInstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); + expect(requestVideoInstreamContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + + const requestVideoOutstream = spec.buildRequests(VIDEO_OUTSTREAM_REQUEST); + expect(requestVideoOutstream[0]).to.have.property('url').and.to.equal('https://prebid.smilewanted.com'); + expect(requestVideoOutstream[0]).to.have.property('method').and.to.equal('POST'); + const requestVideoOutstreamContent = JSON.parse(requestVideoOutstream[0].data); + expect(requestVideoOutstreamContent).to.have.property('zoneId').and.to.equal(3); + expect(requestVideoOutstreamContent).to.have.property('currencyCode').and.to.equal('EUR'); + expect(requestVideoOutstreamContent).to.have.property('bidfloor').and.to.equal(2.50); + expect(requestVideoOutstreamContent).to.have.property('sizes'); + expect(requestVideoOutstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); + expect(requestVideoOutstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); + expect(requestVideoOutstreamContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + }); + + it('SmileWanted - Verify build request with referrer', function () { + const request = spec.buildRequests(DISPLAY_REQUEST, { + refererInfo: { + referer: 'http://localhost/Prebid.js/integrationExamples/gpt/hello_world.html' + } + }); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('pageDomain').and.to.equal('http://localhost/Prebid.js/integrationExamples/gpt/hello_world.html'); + }); + + describe('gdpr tests', function () { + afterEach(function () { + config.resetConfig(); + $$PREBID_GLOBAL$$.requestBids.removeAll(); + }); + + it('SmileWanted - Verify build request with GDPR', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + }, + consentManagement: { + cmp: 'iab', + consentRequired: true, + timeout: 1000, + allowAuctionWithoutConsent: true + } + }); + const request = spec.buildRequests(DISPLAY_REQUEST, { + gdprConsent: { + consentString: 'BOO_ch7OO_ch7AKABBENA2-AAAAZ97_______9______9uz_Gv_r_f__33e8_39v_h_7_u___m_-zzV4-_lvQV1yPA1OrfArgFA', + gdprApplies: true + } + }); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('gdpr').and.to.equal(true); + expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOO_ch7OO_ch7AKABBENA2-AAAAZ97_______9______9uz_Gv_r_f__33e8_39v_h_7_u___m_-zzV4-_lvQV1yPA1OrfArgFA'); + }); + + it('SmileWanted - Verify build request with GDPR without gdprApplies', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + }, + consentManagement: { + cmp: 'iab', + consentRequired: true, + timeout: 1000, + allowAuctionWithoutConsent: true + } + }); + const request = spec.buildRequests(DISPLAY_REQUEST, { + gdprConsent: { + consentString: 'BOO_ch7OO_ch7AKABBENA2-AAAAZ97_______9______9uz_Gv_r_f__33e8_39v_h_7_u___m_-zzV4-_lvQV1yPA1OrfArgFA' + } + }); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.not.have.property('gdpr'); + expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOO_ch7OO_ch7AKABBENA2-AAAAZ97_______9______9uz_Gv_r_f__33e8_39v_h_7_u___m_-zzV4-_lvQV1yPA1OrfArgFA'); + }); + }); + + it('SmileWanted - Verify parse response - Display', function () { + const request = spec.buildRequests(DISPLAY_REQUEST); + const bids = spec.interpretResponse(BID_RESPONSE_DISPLAY, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(3); + expect(bid.ad).to.equal('< --- sw script --- >'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('crea_sw_1'); + expect(bid.currency).to.equal('EUR'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(300); + expect(bid.requestId).to.equal(DISPLAY_REQUEST[0].bidId); + + expect(function () { + spec.interpretResponse(BID_RESPONSE_DISPLAY, { + data: 'invalid Json' + }) + }).to.not.throw(); + }); + + it('SmileWanted - Verify parse response - Video Instream', function () { + const request = spec.buildRequests(VIDEO_INSTREAM_REQUEST); + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO_INSTREAM, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(3); + expect(bid.ad).to.equal(null); + expect(bid.vastUrl).to.equal('https://vast.smilewanted.com'); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(480); + expect(bid.creativeId).to.equal('crea_sw_2'); + expect(bid.currency).to.equal('EUR'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(300); + expect(bid.requestId).to.equal(VIDEO_INSTREAM_REQUEST[0].bidId); + + expect(function () { + spec.interpretResponse(BID_RESPONSE_VIDEO_INSTREAM, { + data: 'invalid Json' + }) + }).to.not.throw(); + }); + + it('SmileWanted - Verify parse response - Video Oustream', function () { + const request = spec.buildRequests(VIDEO_OUTSTREAM_REQUEST); + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO_OUTSTREAM, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(3); + expect(bid.vastUrl).to.equal('https://vast.smilewanted.com'); + expect(bid.renderer.url).to.equal('https://prebid.smilewanted.com/scripts_outstream/infeed.js'); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(480); + expect(bid.creativeId).to.equal('crea_sw_3'); + expect(bid.currency).to.equal('EUR'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(300); + expect(bid.requestId).to.equal(VIDEO_OUTSTREAM_REQUEST[0].bidId); + + expect(function () { + spec.interpretResponse(BID_RESPONSE_VIDEO_OUTSTREAM, { + data: 'invalid Json' + }) + }).to.not.throw(); + }); + + it('SmileWanted - Verify bidder code', function () { + expect(spec.code).to.equal('smilewanted'); + }); + + it('SmileWanted - Verify bidder aliases', function () { + expect(spec.aliases).to.have.lengthOf(2); + expect(spec.aliases[0]).to.equal('smile'); + expect(spec.aliases[1]).to.equal('sw'); + }); + + it('SmileWanted - Verify if bid request valid', function () { + expect(spec.isBidRequestValid(DISPLAY_REQUEST[0])).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + zoneId: 1234 + } + })).to.equal(true); + }); + + it('SmileWanted - Verify if params(zoneId) is not passed', function () { + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ + params: {} + })).to.equal(false); + }); + + it('SmileWanted - Verify user sync', function () { + var syncs = spec.getUserSyncs({ + iframeEnabled: true + }, [BID_RESPONSE_DISPLAY]); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://csync.smilewanted.com'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false + }, [BID_RESPONSE_DISPLAY]); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true + }, []); + expect(syncs).to.have.lengthOf(0); + }); +}); diff --git a/test/spec/modules/somoBidAdapter_spec.js b/test/spec/modules/somoBidAdapter_spec.js new file mode 100644 index 00000000000..16fd43841b7 --- /dev/null +++ b/test/spec/modules/somoBidAdapter_spec.js @@ -0,0 +1,481 @@ +import {expect} from 'chai'; +import {spec} from 'modules/somoBidAdapter'; +import * as utils from 'src/utils'; + +describe('Somo Audience Adapter Tests', function () { + describe('isBidRequestValid', function () { + it('should return false when given an invalid bid', function () { + const bid = { + bidder: 'somo', + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }); + it('should return true when given a placementId bid', function () { + const bid = { + bidder: 'somo', + params: { + placementId: 'test' + } + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); + }); + + describe('buildRequests', function () { + describe('buildBannerRequests', function () { + it('should properly build a banner request with type not defined and sizes not defined', function () { + const bidRequests = [{ + bidder: 'somo', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + expect(request[0].url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); + expect(request[0].method).to.equal('POST'); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.device).to.not.equal(null); + expect(ortbRequest.device.ua).to.equal(navigator.userAgent); + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + }); + + it('should properly build a banner request with sizes defined in 2d array', function () { + const bidRequests = [{ + bidder: 'somo', + sizes: [[300, 250]], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + expect(request[0].url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); + expect(request[0].method).to.equal('POST'); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); + }); + it('should properly build a banner request with sizes defined in 1d array', function () { + const bidRequests = [{ + bidder: 'somo', + sizes: [300, 250], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + expect(request[0].url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); + expect(request[0].method).to.equal('POST'); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); + expect(ortbRequest.imp[0].banner.mimes).to.equal(undefined); + expect(ortbRequest.imp[0].banner.btype).to.equal(undefined); + expect(ortbRequest.imp[0].banner.pos).to.equal(undefined); + expect(ortbRequest.imp[0].banner.battr).to.equal(undefined); + }); + + it('should populate optional banner parameters', function () { + const bidRequests = [ + { + bidder: 'somo', + sizes: [[300, 200]], + mediaType: 'banner', + params: { + placementId: 'test', + banner: { + mimes: 'video/mp4', + btype: '4', + pos: '1', + battr: 'ibv', + } + } + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(200); + expect(ortbRequest.imp[0].banner.mimes).to.equal('video/mp4'); + expect(ortbRequest.imp[0].banner.btype).to.equal('4'); + expect(ortbRequest.imp[0].banner.pos).to.equal('1'); + expect(ortbRequest.imp[0].banner.battr).to.equal('ibv'); + }); + }); + + describe('buildVideoRequests', function () { + it('should properly build a video request with sizes defined', function () { + const bidRequests = [{ + bidder: 'somo', + mediaTypes: { + video: {} + }, + sizes: [200, 300], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.w).to.equal(200); + expect(ortbRequest.imp[0].video.h).to.equal(300); + }); + + it('should properly build a video request with sizes defined in 2d array', function () { + const bidRequests = [{ + bidder: 'somo', + mediaTypes: { + video: {} + }, + sizes: [[200, 300]], + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.w).to.equal(200); + expect(ortbRequest.imp[0].video.h).to.equal(300); + }); + it('should properly build a video request with sizes not defined', function () { + const bidRequests = [{ + bidder: 'somo', + mediaType: 'video', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.mimes).to.equal(undefined); + expect(ortbRequest.imp[0].video.minduration).to.equal(undefined); + expect(ortbRequest.imp[0].video.maxduration).to.equal(undefined); + expect(ortbRequest.imp[0].video.protocols).to.equal(undefined); + expect(ortbRequest.imp[0].video.startdelay).to.equal(undefined); + expect(ortbRequest.imp[0].video.linearity).to.equal(undefined); + expect(ortbRequest.imp[0].video.skip).to.equal(undefined); + expect(ortbRequest.imp[0].video.delivery).to.equal(undefined); + expect(ortbRequest.imp[0].video.pos).to.equal(undefined); + expect(ortbRequest.imp[0].video.api).to.equal(undefined); + expect(ortbRequest.imp[0].video.battr).to.equal(undefined); + }); + + it('should populate optional video parameters', function () { + const bidRequests = [ + { + bidder: 'somo', + sizes: [[200, 300]], + mediaType: 'video', + params: { + placementId: 'test', + video: { + mimes: 'video/mp4', + minduration: '15', + maxduration: '30', + protocols: 'mp4', + startdelay: '0', + linearity: 'linear', + skip: '1', + delivery: 'web', + pos: '1', + api: 'VPAID 1.0', + battr: 'ibv', + } + } + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp[0].video).to.not.equal(null); + expect(ortbRequest.imp[0].video.w).to.equal(200); + expect(ortbRequest.imp[0].video.h).to.equal(300); + expect(ortbRequest.imp[0].video.mimes).to.equal('video/mp4'); + expect(ortbRequest.imp[0].video.minduration).to.equal('15'); + expect(ortbRequest.imp[0].video.maxduration).to.equal('30'); + expect(ortbRequest.imp[0].video.protocols).to.equal('mp4'); + expect(ortbRequest.imp[0].video.startdelay).to.equal('0'); + expect(ortbRequest.imp[0].video.linearity).to.equal('linear'); + expect(ortbRequest.imp[0].video.skip).to.equal('1'); + expect(ortbRequest.imp[0].video.delivery).to.equal('web'); + expect(ortbRequest.imp[0].video.pos).to.equal('1'); + expect(ortbRequest.imp[0].video.api).to.equal('VPAID 1.0'); + expect(ortbRequest.imp[0].video.battr).to.equal('ibv'); + }); + }); + + describe('buildSiteRequests', function () { + it('should fill in basic site parameters', function () { + const bidRequests = [{ + bidder: 'somo', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.app).to.equal(null); + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.ref).to.equal(utils.getTopWindowReferrer()); + expect(ortbRequest.site.page).to.equal(utils.getTopWindowLocation().href); + expect(ortbRequest.site.domain).to.not.be.undefined; + }); + + it('should fill in optional site parameters', function () { + const bidRequests = [{ + bidder: 'somo', + params: { + placementId: 'test', + site: { + domain: 'somoaudience.com', + name: 'Somo Audience', + cat: 'IAB-25', + keywords: 'unit testing', + content: 'Unit Testing' + } + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.app).to.equal(null); + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.name).to.equal('Somo Audience'); + expect(ortbRequest.site.domain).to.equal('somoaudience.com'); + expect(ortbRequest.site.cat).to.equal('IAB-25'); + expect(ortbRequest.site.keywords).to.equal('unit testing'); + expect(ortbRequest.site.content).to.equal('Unit Testing'); + }) + }); + + describe('buildAppRequests', function () { + it('should fill in app parameters', function () { + const bidRequests = [{ + bidder: 'somo', + params: { + placementId: 'test', + app: { + bundle: 'com.somoaudience.apps', + storeUrl: 'http://somoaudience.com/apps', + domain: 'somoaudience.com', + name: 'Generic SomoAudience App 5', + cat: 'IAB-25', + keywords: 'unit testing', + content: 'Unit Testing', + ver: '5.423-s', + } + } + }]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.site).to.equal(null); + expect(ortbRequest.app).to.not.be.null; + expect(ortbRequest.app.bundle).to.equal('com.somoaudience.apps'); + expect(ortbRequest.app.storeUrl).to.equal('http://somoaudience.com/apps'); + expect(ortbRequest.app.domain).to.equal('somoaudience.com'); + expect(ortbRequest.app.name).to.equal('Generic SomoAudience App 5'); + expect(ortbRequest.app.ver).to.equal('5.423-s'); + expect(ortbRequest.app.cat).to.equal('IAB-25'); + expect(ortbRequest.app.keywords).to.equal('unit testing'); + expect(ortbRequest.app.content).to.equal('Unit Testing'); + }); + }); + + describe('buildGDPRRequests', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'test' + }, + }; + + it('should properly build request with gdpr consent', function () { + const bidRequests = [{ + bidder: 'somo', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request[0].data; + expect(ortbRequest.reqs).to.not.equal(undefined); + expect(ortbRequest.reqs.ext).to.not.equal(undefined); + expect(ortbRequest.reqs.ext.gdpr).to.equal(true); + expect(ortbRequest.user).to.not.equal(undefined); + expect(ortbRequest.user.ext).to.not.equal(undefined); + expect(ortbRequest.user.ext.consent).to.equal('test'); + }); + it('should properly build request with gdpr not applies', function () { + bidderRequest.gdprConsent.gdprApplies = false; + const bidRequests = [{ + bidder: 'somo', + params: { + placementId: 'test' + } + }]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request[0].data; + expect(ortbRequest.reqs).to.not.equal(undefined); + expect(ortbRequest.reqs.ext).to.not.equal(undefined); + expect(ortbRequest.reqs.ext.gdpr).to.equal(false); + expect(ortbRequest.user).to.not.equal(undefined); + expect(ortbRequest.user.ext).to.not.equal(undefined); + expect(ortbRequest.user.ext.consent).to.equal('test'); + }); + }); + + describe('buildExtraArgsRequests', function () { + it('should populate optional parameters', function () { + const bidRequests = [ + { + bidder: 'somo', + params: { + placementId: 'test', + bcat: ['IAB-2', 'IAB-7'], + badv: ['somoaudience.com', 'mobileadtrading.com'], + bidfloor: '0.05', + }, + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + expect(ortbRequest.imp[0].bidfloor).to.not.be.null; + expect(ortbRequest.imp[0].bidfloor).to.be.equal('0.05'); + expect(ortbRequest.bcat).to.not.be.null; + expect(ortbRequest.bcat).to.have.lengthOf(2); + expect(ortbRequest.bcat).to.contain('IAB-2'); + expect(ortbRequest.badv).to.not.be.null; + expect(ortbRequest.badv).to.have.lengthOf(2); + expect(ortbRequest.badv).to.contain('somoaudience.com'); + }); + }); + }); + + describe('interpretResponse', function () { + it('Verify banner parse response', function () { + const bidRequests = [ + { + bidder: 'somo', + params: { + placementId: 'test', + }, + bidId: '234234234', + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'Somo Test Ad' + }], + bidId: '234234234' + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, {bidRequest: bidRequests[0]}); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.equal('Somo Test Ad'); + }); + + it('Verify video parse response', function () { + const bidRequests = [ + { + bidder: 'somo', + mediaTypes: { + video: { + } + }, + params: { + placementId: 'test', + }, + bidId: '234234234', + } + ]; + const request = spec.buildRequests(bidRequests); + const ortbRequest = request[0].data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'Somo Test Ad' + }], + bidId: '234234234' + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, {bidRequest: bidRequests[0]}); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.vastXml).to.equal('Somo Test Ad'); + }); + }); + + describe('user sync', function () { + it('should register the pixel sync url', function () { + let syncs = spec.getUserSyncs({ + pixelEnabled: true + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + }); + + it('should pass gdpr params', function () { + let syncs = spec.getUserSyncs({ pixelEnabled: true }, {}, { + gdprApplies: false, consentString: 'test' + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.contains('gdpr=0'); + }); + + it('should pass gdpr applies params', function () { + let syncs = spec.getUserSyncs({ pixelEnabled: true }, {}, { + gdprApplies: true, consentString: 'test' + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.contains('gdpr=1'); + expect(syncs[0].url).to.contains('gdpr_consent=test'); + }); + }); +}); diff --git a/test/spec/modules/somoaudienceBidAdapter_spec.js b/test/spec/modules/somoaudienceBidAdapter_spec.js deleted file mode 100644 index 2189cacb0ec..00000000000 --- a/test/spec/modules/somoaudienceBidAdapter_spec.js +++ /dev/null @@ -1,97 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/somoaudienceBidAdapter'; -import {getTopWindowLocation, getTopWindowReferrer} from 'src/utils'; -import {newBidder} from 'src/adapters/bidderFactory'; - -describe('Somo Audience Adapter Tests', () => { - const bidderSet = [ - { - bidder: 'somoaudience', - params: { - placementId: 'test' - } - }, - ]; - - const bidderAppSet = [ - { - bidder: 'somoaudience', - params: { - placementId: 'test', - app: { - bundle: 'com.somoaudience.apps', - storeUrl: 'http://somoaudience.com/apps', - domain: 'somoaudience.com', - } - } - }, - ]; - - const bidderBadSet = [ - { - bidder: 'somoaudience', - params: { - placement_id: 'test' - } - }, - ]; - - it('Verifies bidder code', () => { - expect(spec.code).to.equal('somoaudience'); - }); - - it('Verifies bidder aliases', () => { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.equal('somo'); - }); - - it('Verifies if bid request valid', () => { - expect(spec.isBidRequestValid(bidderSet[0])).to.equal(true); - expect(spec.isBidRequestValid(bidderAppSet[0])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid(bidderBadSet[0])).to.equal(false); - expect(spec.isBidRequestValid({ params: {} })).to.equal(false); - expect(spec.isBidRequestValid({ params: { placementId: '12345' } })).to.equal(true); - }); - - it('Verifies buildRequests', () => { - const request = spec.buildRequests(bidderSet); - let br = request[0]; - expect(br.url).to.equal('//publisher-east.mobileadtrading.com/rtb/bid?s=test'); - expect(br.method).to.equal('POST'); - const ortbRequest = br.data; - expect(ortbRequest.site).to.not.equal(null); - expect(ortbRequest.site.ref).to.equal(getTopWindowReferrer()); - expect(ortbRequest.site.page).to.equal(getTopWindowLocation().href); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.device).to.not.equal(null); - expect(ortbRequest.device.ua).to.equal(navigator.userAgent); - }); - - it('Verify parse response', () => { - const request = spec.buildRequests(bidderSet); - const ortbRequest = request[0].data; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'Somo Test Ad' - }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.equal('Somo Test Ad'); - }); - it('Verify app requests', () => { - const request = spec.buildRequests(bidderAppSet); - const ortbRequest = request[0].data; - expect(ortbRequest.site).to.equal(null); - expect(ortbRequest.app).to.not.be.null; - expect(ortbRequest.app.bundle).to.equal('com.somoaudience.apps'); - expect(ortbRequest.app.storeurl).to.equal('http://somoaudience.com/apps'); - expect(ortbRequest.app.domain).to.equal('somoaudience.com'); - }); -}); diff --git a/test/spec/modules/sonobiAnalyticsAdapter_spec.js b/test/spec/modules/sonobiAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..389dfee34f9 --- /dev/null +++ b/test/spec/modules/sonobiAnalyticsAdapter_spec.js @@ -0,0 +1,89 @@ +import sonobiAnalytics from 'modules/sonobiAnalyticsAdapter'; +import {expect} from 'chai'; +let events = require('src/events'); +let adapterManager = require('src/adapterManager').default; +let constants = require('src/constants.json'); + +describe('Sonobi Prebid Analytic', function () { + let xhr; + let requests = []; + var clock; + + describe('enableAnalytics', function () { + beforeEach(function () { + requests = []; + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + sinon.stub(events, 'getEvents').returns([]); + clock = sinon.useFakeTimers(Date.now()); + }); + + afterEach(function () { + xhr.restore(); + events.getEvents.restore(); + clock.restore(); + }); + + after(function () { + sonobiAnalytics.disableAnalytics(); + }); + + it('should catch all events', function (done) { + const initOptions = { + pubId: 'A3B254F', + siteId: '1234', + delay: 100 + }; + + sonobiAnalytics.enableAnalytics(initOptions) + + const bid = { + bidderCode: 'sonobi_test_bid', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '1234', + auctionId: '13', + responseTimestamp: 1496410856397, + requestTimestamp: 1496410856295, + cpm: 1.13, + bidder: 'sonobi', + adUnitCode: 'dom-sample-id', + timeToRespond: 100, + placementCode: 'placementtest' + }; + + // Step 1: Initialize adapter + adapterManager.enableAnalytics({ + provider: 'sonobi', + options: initOptions + }); + + // Step 2: Send init auction event + events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, auctionId: '13', timestamp: Date.now()}); + + expect(sonobiAnalytics.initOptions).to.have.property('pubId', 'A3B254F'); + expect(sonobiAnalytics.initOptions).to.have.property('siteId', '1234'); + expect(sonobiAnalytics.initOptions).to.have.property('delay', 100); + // Step 3: Send bid requested event + events.emit(constants.EVENTS.BID_REQUESTED, { bids: [bid], auctionId: '13' }); + + // Step 4: Send bid response event + events.emit(constants.EVENTS.BID_RESPONSE, bid); + + // Step 5: Send bid won event + events.emit(constants.EVENTS.BID_WON, bid); + + // Step 6: Send bid timeout event + events.emit(constants.EVENTS.BID_TIMEOUT, {auctionId: '13'}); + + // Step 7: Send auction end event + events.emit(constants.EVENTS.AUCTION_END, {auctionId: '13', bidsReceived: [bid]}); + + clock.tick(5000); + expect(requests).to.have.length(1); + expect(JSON.parse(requests[0].requestBody)).to.have.length(3) + done(); + }); + }); +}); diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 431bf134349..dc536846ae2 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -1,23 +1,24 @@ import { expect } from 'chai' import { spec, _getPlatform } from 'modules/sonobiBidAdapter' import { newBidder } from 'src/adapters/bidderFactory' +import {userSync} from '../../../src/userSync'; -describe('SonobiBidAdapter', () => { +describe('SonobiBidAdapter', function () { const adapter = newBidder(spec) - describe('.code', () => { - it('should return a bidder code of sonobi', () => { + describe('.code', function () { + it('should return a bidder code of sonobi', function () { expect(spec.code).to.equal('sonobi') }) }) - describe('inherited functions', () => { - it('should exist and be a function', () => { + describe('inherited functions', function () { + it('should exist and be a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function') }) }) - describe('.isBidRequestValid', () => { + describe('.isBidRequestValid', function () { let bid = { 'bidder': 'sonobi', 'params': { @@ -32,11 +33,11 @@ describe('SonobiBidAdapter', () => { 'auctionId': '1d1a030790a475', } - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true) }) - it('should return true when bid.params.placement_id and bid.params.sizes are found', () => { + it('should return true when bid.params.placement_id and bid.params.sizes are found', function () { let bid = Object.assign({}, bid) delete bid.params delete bid.sizes @@ -48,7 +49,7 @@ describe('SonobiBidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true) }) - it('should return true when bid.params.placement_id and bid.sizes are found', () => { + it('should return true when bid.params.placement_id and bid.sizes are found', function () { let bid = Object.assign({}, bid) delete bid.params bid.sizes = [[300, 250], [300, 600]] @@ -59,7 +60,7 @@ describe('SonobiBidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true) }) - it('should return true when bid.params.ad_unit and bid.params.sizes are found', () => { + it('should return true when bid.params.ad_unit and bid.params.sizes are found', function () { let bid = Object.assign({}, bid) delete bid.params delete bid.sizes @@ -71,7 +72,7 @@ describe('SonobiBidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true) }) - it('should return true when bid.params.ad_unit and bid.sizes are found', () => { + it('should return true when bid.params.ad_unit and bid.sizes are found', function () { let bid = Object.assign({}, bid) delete bid.params bid.sizes = [[300, 250], [300, 600]] @@ -82,13 +83,13 @@ describe('SonobiBidAdapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true) }) - it('should return false when no params are found', () => { + it('should return false when no params are found', function () { let bid = Object.assign({}, bid) delete bid.params expect(spec.isBidRequestValid(bid)).to.equal(false) }) - it('should return false when bid.params.placement_id and bid.params.ad_unit are not found', () => { + it('should return false when bid.params.placement_id and bid.params.ad_unit are not found', function () { let bid = Object.assign({}, bid) delete bid.params bid.params = { @@ -100,8 +101,30 @@ describe('SonobiBidAdapter', () => { }) }) - describe('.buildRequests', () => { + describe('.buildRequests', function () { + beforeEach(function() { + sinon.stub(userSync, 'canBidderRegisterSync'); + }); + afterEach(function() { + userSync.canBidderRegisterSync.restore(); + }); let bidRequest = [{ + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 0 + }, + ] + }, 'bidder': 'sonobi', 'params': { 'placement_id': '1a2b3c4d5e6f1a2b3c4d', @@ -130,9 +153,64 @@ describe('SonobiBidAdapter', () => { '/7780971/sparks_prebid_LB|30b31c1838de1e': '300x250,300x600', }; - it('should return a properly formatted request', () => { - const bidRequests = spec.buildRequests(bidRequest) - const bidRequestsPageViewID = spec.buildRequests(bidRequest) + let bidderRequests = { + 'gdprConsent': { + 'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + 'vendorData': {}, + 'gdprApplies': true + }, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'http://example.com', + 'stack': ['http://example.com'] + } + }; + it('should include the digitrust id and keyv', () => { + window.DigiTrust = { + getUser: function () { + } + }; + let sandbox = sinon.sandbox.create(); + sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => + ({ + success: true, + identity: { + id: 'Vb0YJIxTMJV4W0GHRdJ3MwyiOVYJjYEgc2QYdBSG', + keyv: 4, + version: 2, + privacy: {} + } + }) + ); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.data.digid).to.equal('Vb0YJIxTMJV4W0GHRdJ3MwyiOVYJjYEgc2QYdBSG'); + expect(bidRequests.data.digkeyv).to.equal(4); + sandbox.restore(); + delete window.DigiTrust; + }); + + it('should not include the digitrust id and keyv', () => { + window.DigiTrust = { + getUser: function () { + } + }; + let sandbox = sinon.sandbox.create(); + sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => + ({ + success: false + }) + ); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.data.digid).to.be.undefined; + expect(bidRequests.data.digkeyv).to.be.undefined; + sandbox.restore(); + delete window.DigiTrust; + }) + + it('should return a properly formatted request', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + const bidRequestsPageViewID = spec.buildRequests(bidRequest, bidderRequests) expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json') expect(bidRequests.method).to.equal('GET') expect(bidRequests.data.key_maker).to.deep.equal(JSON.stringify(keyMakerData)) @@ -145,29 +223,196 @@ describe('SonobiBidAdapter', () => { expect(['mobile', 'tablet', 'desktop']).to.contain(bidRequests.data.vp); }) - it('should return a properly formatted request with hfa', () => { + it('should return a properly formatted request with GDPR applies set to true', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json') + expect(bidRequests.method).to.equal('GET') + expect(bidRequests.data.gdpr).to.equal('true') + expect(bidRequests.data.consent_string).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') + }) + + it('should return a properly formatted request with referer', function () { + bidRequest[0].params.referrer = '' + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.data.ref).to.equal('http://example.com') + }) + + it('should return a properly formatted request with GDPR applies set to false', function () { + bidderRequests.gdprConsent.gdprApplies = false; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json') + expect(bidRequests.method).to.equal('GET') + expect(bidRequests.data.gdpr).to.equal('false') + expect(bidRequests.data.consent_string).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') + }) + it('should return a properly formatted request with GDPR applies set to false with no consent_string param', function () { + let bidderRequests = { + 'gdprConsent': { + 'consentString': undefined, + 'vendorData': {}, + 'gdprApplies': false + }, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'http://example.com', + 'stack': ['http://example.com'] + } + }; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json') + expect(bidRequests.method).to.equal('GET') + expect(bidRequests.data.gdpr).to.equal('false') + expect(bidRequests.data).to.not.include.keys('consent_string') + }) + it('should return a properly formatted request with GDPR applies set to true with no consent_string param', function () { + let bidderRequests = { + 'gdprConsent': { + 'consentString': undefined, + 'vendorData': {}, + 'gdprApplies': true + }, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'http://example.com', + 'stack': ['http://example.com'] + } + }; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json') + expect(bidRequests.method).to.equal('GET') + expect(bidRequests.data.gdpr).to.equal('true') + expect(bidRequests.data).to.not.include.keys('consent_string') + }) + it('should return a properly formatted request with hfa', function () { bidRequest[0].params.hfa = 'hfakey' bidRequest[1].params.hfa = 'hfakey' - const bidRequests = spec.buildRequests(bidRequest) + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json') expect(bidRequests.method).to.equal('GET') expect(bidRequests.data.ref).not.to.be.empty expect(bidRequests.data.s).not.to.be.empty expect(bidRequests.data.hfa).to.equal('hfakey') }) - it('should return null if there is nothing to bid on', () => { - const bidRequests = spec.buildRequests([{params: {}}]) + + it('should return null if there is nothing to bid on', function () { + const bidRequests = spec.buildRequests([{params: {}}], bidderRequests) expect(bidRequests).to.equal(null); }) + + it('should return a properly formatted request with commonid as hfa', function () { + delete bidRequest[0].params.hfa; + delete bidRequest[1].params.hfa; + bidRequest[0].crumbs = {'pubcid': 'abcd-efg-0101'}; + bidRequest[1].crumbs = {'pubcid': 'abcd-efg-0101'}; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(bidRequests.data.hfa).to.equal('PRE-abcd-efg-0101'); + }); + + it('should return a properly formatted request with commonid from User ID as hfa', function () { + delete bidRequest[0].params.hfa; + delete bidRequest[1].params.hfa; + bidRequest[0].userId = {'pubcid': 'abcd-efg-0101'}; + bidRequest[1].userId = {'pubcid': 'abcd-efg-0101'}; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(bidRequests.data.hfa).to.equal('PRE-abcd-efg-0101'); + delete bidRequest[0].userId; + delete bidRequest[1].userId; + }) + + it('should return a properly formatted request with unified id from User ID as tdid', function () { + delete bidRequest[0].params.tdid; + delete bidRequest[1].params.tdid; + bidRequest[0].userId = {'tdid': 'td-abcd-efg-0101'}; + bidRequest[1].userId = {'tdid': 'td-abcd-efg-0101'}; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(bidRequests.data.tdid).to.equal('td-abcd-efg-0101'); + }) + + it('should return a properly formatted request with hfa preferred over commonid', function () { + bidRequest[0].params.hfa = 'hfakey'; + bidRequest[1].params.hfa = 'hfakey'; + bidRequest[0].crumbs = {'pubcid': 'abcd-efg-0101'}; + bidRequest[1].crumbs = {'pubcid': 'abcd-efg-0101'}; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json') + expect(bidRequests.method).to.equal('GET') + expect(bidRequests.data.ref).not.to.be.empty + expect(bidRequests.data.s).not.to.be.empty + expect(bidRequests.data.hfa).to.equal('hfakey') + }) + + it('should set ius as 0 if Sonobi cannot drop iframe pixels', function () { + userSync.canBidderRegisterSync.returns(false); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.ius).to.equal(0); + }); + + it('should set ius as 1 if Sonobi can drop iframe pixels', function() { + userSync.canBidderRegisterSync.returns(true); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.ius).to.equal(1); + }); + + it('should return a properly formatted request with schain defined', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(JSON.parse(bidRequests.data.schain)).to.deep.equal(bidRequest[0].schain) + }); + + it('should return a properly formatted request with userid as a JSON-encoded set of User ID results', function () { + bidRequest[0].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}; + bidRequest[1].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(JSON.parse(bidRequests.data.userid)).to.eql({'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}); + }); + + it('should return a properly formatted request with userid omitted if there are no userIds', function () { + bidRequest[0].userId = {}; + bidRequest[1].userId = {}; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(bidRequests.data.userid).to.equal(undefined); + }); + + it('should return a properly formatted request with userid omitted', function () { + bidRequest[0].userId = undefined; + bidRequest[1].userId = undefined; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(bidRequests.data.userid).to.equal(undefined); + }); }) - describe('.interpretResponse', () => { + describe('.interpretResponse', function () { const bidRequests = { 'method': 'GET', 'url': 'https://apex.go.sonobi.com/trinity.json', 'withCredentials': true, 'data': { - 'key_maker': '{"30b31c1838de1f":"1a2b3c4d5e6f1a2b3c4d|300x250,300x600|f=1.25","/7780971/sparks_prebid_LB|30b31c1838de1e":"300x250,300x600"}', 'ref': 'localhost:9876', 's': '2474372d-c0ff-4f46-aef4-a173058403d9', 'pv': 'c9cfc207-cd83-4a01-b591-8bb29389d4b0' + 'key_maker': '{"30b31c1838de1f":"1a2b3c4d5e6f1a2b3c4d|300x250,300x600|f=1.25","/7780971/sparks_prebid_LB|30b31c1838de1e":"300x250,300x600"}', 'ref': 'http://localhost/', 's': '2474372d-c0ff-4f46-aef4-a173058403d9', 'pv': 'c9cfc207-cd83-4a01-b591-8bb29389d4b0' }, 'bidderRequests': [ { @@ -189,8 +434,7 @@ describe('SonobiBidAdapter', () => { }, 'adUnitCode': 'adunit-code-2', 'sizes': [[120, 600], [300, 600], [160, 600]], - 'bidId': '30b31c1838de1e', - 'mediaType': 'video' + 'bidId': '30b31c1838de1e' }, { 'bidder': 'sonobi', @@ -201,6 +445,20 @@ describe('SonobiBidAdapter', () => { 'adUnitCode': 'adunit-code-3', 'sizes': [[120, 600], [300, 600], [160, 600]], 'bidId': '30b31c1838de1g' + }, + { + 'bidId': '30b31c1838de1zzzz', + 'adUnitCode': 'outstream-dom-id', + bidder: 'sonobi', + mediaTypes: { + video: { + context: 'outstream' + } + }, + params: { + placement_id: '92e95368e86639dbd86d', + sizes: [[640, 480]] + } } ] }; @@ -211,6 +469,7 @@ describe('SonobiBidAdapter', () => { '/7780971/sparks_prebid_LB|30b31c1838de1f': { 'sbi_size': '300x600', 'sbi_apoc': 'remnant', + 'sbi_crid': '1234abcd', 'sbi_aid': '30292e432662bd5f86d90774b944b039', 'sbi_mouse': 1.07, }, @@ -220,8 +479,27 @@ describe('SonobiBidAdapter', () => { 'sbi_aid': '30292e432662bd5f86d90774b944b038', 'sbi_mouse': 1.25, 'sbi_dozer': 'dozerkey', + 'sbi_ct': 'video' + }, + '/7780971/sparks_prebid_LB_OUTSTREAM|30b31c1838de1g': { + 'sbi_size': '300x600', + 'sbi_apoc': 'remnant', + 'sbi_crid': '1234abcd', + 'sbi_aid': '30292e432662bd5f86d90774b944b038', + 'sbi_mouse': 1.07, }, '/7780971/sparks_prebid_LB|30b31c1838de1g': {}, + '30b31c1838de1zzzz': { + sbi_aid: 'force_1550072228_da1c5d030cb49150c5db8a2136175755', + sbi_apoc: 'premium', + sbi_ct: 'video', + sbi_curr: 'USD', + sbi_mouse: 1.25, + sbi_size: 'preroll', + 'sbi_crid': 'somecrid', + + } + }, 'sbi_dc': 'mco-1-', 'sbi_px': [{ @@ -240,33 +518,87 @@ describe('SonobiBidAdapter', () => { 'cpm': 1.07, 'width': 300, 'height': 600, - 'ad': '', + 'ad': ``, 'ttl': 500, - 'creativeId': '30292e432662bd5f86d90774b944b039', + 'creativeId': '1234abcd', 'netRevenue': true, - 'currency': 'USD' + 'currency': 'USD', + 'aid': '30292e432662bd5f86d90774b944b039' }, { 'requestId': '30b31c1838de1e', 'cpm': 1.25, 'width': 300, 'height': 250, - 'ad': 'https://mco-1-apex.go.sonobi.com/vast.xml?vid=30292e432662bd5f86d90774b944b038&ref=localhost:9876', + 'vastUrl': 'https://mco-1-apex.go.sonobi.com/vast.xml?vid=30292e432662bd5f86d90774b944b038&ref=http%3A%2F%2Flocalhost%2F', 'ttl': 500, 'creativeId': '30292e432662bd5f86d90774b944b038', 'netRevenue': true, 'currency': 'USD', - 'dealId': 'dozerkey' - } + 'dealId': 'dozerkey', + 'aid': '30292e432662bd5f86d90774b944b038', + 'mediaType': 'video' + }, + { + 'requestId': '30b31c1838de1g', + 'cpm': 1.07, + 'width': 300, + 'height': 600, + 'ad': ``, + 'ttl': 500, + 'creativeId': '1234abcd', + 'netRevenue': true, + 'currency': 'USD', + 'aid': '30292e432662bd5f86d90774b944b038' + }, + { + 'requestId': '30b31c1838de1zzzz', + 'cpm': 1.25, + 'width': 640, + 'height': 480, + 'vastUrl': 'https://mco-1-apex.go.sonobi.com/vast.xml?vid=30292e432662bd5f86d90774b944b038&ref=http%3A%2F%2Flocalhost%2F', + 'ttl': 500, + 'creativeId': 'somecrid', + 'netRevenue': true, + 'currency': 'USD', + 'dealId': 'dozerkey', + 'aid': 'force_1550072228_da1c5d030cb49150c5db8a2136175755', + 'mediaType': 'video', + renderer: () => {} + }, ]; - it('should map bidResponse to prebidResponse', () => { + it('should map bidResponse to prebidResponse', function () { const response = spec.interpretResponse(bidResponse, bidRequests); - expect(response).to.deep.equal(prebidResponse); - }) - }) + response.forEach((resp, i) => { + expect(resp.requestId).to.equal(prebidResponse[i].requestId); + expect(resp.cpm).to.equal(prebidResponse[i].cpm); + + expect(resp.ttl).to.equal(prebidResponse[i].ttl); + expect(resp.creativeId).to.equal(prebidResponse[i].creativeId); + expect(resp.netRevenue).to.equal(prebidResponse[i].netRevenue); + expect(resp.currency).to.equal(prebidResponse[i].currency); + expect(resp.aid).to.equal(prebidResponse[i].aid); + if (resp.mediaType === 'video' && resp.renderer) { + expect(resp.vastUrl.indexOf('vast.xml')).to.be.greaterThan(0); + expect(resp.width).to.equal(prebidResponse[i].width); + expect(resp.height).to.equal(prebidResponse[i].height); + expect(resp.renderer).to.be.ok; + } else if (resp.mediaType === 'video') { + expect(resp.vastUrl.indexOf('vast.xml')).to.be.greaterThan(0); + expect(resp.ad).to.be.undefined; + expect(resp.width).to.be.undefined; + expect(resp.height).to.be.undefined; + } else { + expect(resp.ad.indexOf('localhost')).to.be.greaterThan(0); + expect(resp.width).to.equal(prebidResponse[i].width); + expect(resp.height).to.equal(prebidResponse[i].height); + } + }); + }); + }); - describe('.getUserSyncs', () => { + describe('.getUserSyncs', function () { let bidResponse = [{ 'body': { 'sbi_px': [{ @@ -278,34 +610,35 @@ describe('SonobiBidAdapter', () => { } }]; - it('should return one sync pixel', () => { + it('should return one sync pixel', function () { expect(spec.getUserSyncs({ pixelEnabled: true }, bidResponse)).to.deep.equal([{ type: 'image', url: 'https://pixel-test' }]); }) - it('should return an empty array when sync is enabled but there are no bidResponses', () => { + it('should return an empty array when sync is enabled but there are no bidResponses', function () { expect(spec.getUserSyncs({ pixelEnabled: true }, [])).to.have.length(0); }) - it('should return an empty array when sync is enabled but no sync pixel returned', () => { + it('should return an empty array when sync is enabled but no sync pixel returned', function () { const pixel = Object.assign({}, bidResponse); delete pixel[0].body.sbi_px; expect(spec.getUserSyncs({ pixelEnabled: true }, bidResponse)).to.have.length(0); }) - it('should return an empty array', () => { + it('should return an empty array', function () { expect(spec.getUserSyncs({ pixelEnabled: false }, bidResponse)).to.have.length(0); - }) + expect(spec.getUserSyncs({ pixelEnabled: true }, [])).to.have.length(0); + }); }) - describe('_getPlatform', () => { - it('should return mobile', () => { + describe('_getPlatform', function () { + it('should return mobile', function () { expect(_getPlatform({innerWidth: 767})).to.equal('mobile') }) - it('should return tablet', () => { + it('should return tablet', function () { expect(_getPlatform({innerWidth: 800})).to.equal('tablet') }) - it('should return desktop', () => { + it('should return desktop', function () { expect(_getPlatform({innerWidth: 1000})).to.equal('desktop') }) }) diff --git a/test/spec/modules/sortableAnalyticsAdapter_spec.js b/test/spec/modules/sortableAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..90bd5fcdf22 --- /dev/null +++ b/test/spec/modules/sortableAnalyticsAdapter_spec.js @@ -0,0 +1,307 @@ +import {expect} from 'chai'; +import sortableAnalyticsAdapter, {TIMEOUT_FOR_REGISTRY, DEFAULT_PBID_TIMEOUT} from 'modules/sortableAnalyticsAdapter'; +import events from 'src/events'; +import CONSTANTS from 'src/constants.json'; +import * as prebidGlobal from 'src/prebidGlobal'; + +describe('Sortable Analytics Adapter', function() { + let requests; + let sandbox; + let xhr; + let clock; + + const initialConfig = { + provider: 'sortable', + options: { + siteId: 'testkey' + } + }; + + const TEST_DATA = { + AUCTION_INIT: { + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + timeout: 3000 + }, + BID_REQUESTED: { + refererInfo: { + referer: 'test.com', + reachedTop: true, + numIframes: 1 + }, + bidderCode: 'sortable', + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + bids: [{ + bidder: 'sortable', + params: { + tagId: 'medrec_1' + }, + adUnitCode: '300x250', + transactionId: 'aa02b498-8a99-418e-bc59-6b6fd45f32de', + sizes: [ + [300, 250] + ], + bidId: '26721042674416', + bidderRequestId: '10141593b1d84a', + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + bidRequestsCount: 1 + }, { + bidder: 'sortable', + params: { + tagId: 'lead_1' + }, + adUnitCode: '728x90', + transactionId: 'b7e9e957-af4f-4c47-8ca7-41f01cb4f105', + sizes: [ + [728, 90] + ], + bidId: '50fa575b41e596', + bidderRequestId: '37a8760be6db23', + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + bidRequestsCount: 1 + }], + start: 1553529405788 + }, + BID_ADJUSTMENT_1: { + bidderCode: 'sortable', + adId: '88221d316425f7', + mediaType: 'banner', + cpm: 0.70, + dealId: null, + currency: 'USD', + netRevenue: true, + ttl: 60, + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + responseTimestamp: 1553534161763, + bidder: 'sortable', + adUnitCode: '300x250', + timeToRespond: 331, + width: '300', + height: '250' + }, + AUCTION_END: { + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e' + }, + BID_ADJUSTMENT_2: { + bidderCode: 'sortable', + adId: '88221d316425f8', + mediaType: 'banner', + cpm: 0.50, + dealId: null, + currency: 'USD', + netRevenue: true, + ttl: 60, + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + responseTimestamp: 1553534161770, + bidder: 'sortable', + adUnitCode: '728x90', + timeToRespond: 338, + width: '728', + height: '90' + }, + BID_WON_1: { + bidderCode: 'sortable', + adId: '88221d316425f7', + mediaType: 'banner', + cpm: 0.70, + dealId: null, + currency: 'USD', + netRevenue: true, + ttl: 60, + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + responseTimestamp: 1553534161763, + bidder: 'sortable', + adUnitCode: '300x250', + timeToRespond: 331 + }, + BID_WON_2: { + bidderCode: 'sortable', + adId: '88221d316425f8', + mediaType: 'banner', + cpm: 0.50, + dealId: null, + currency: 'USD', + netRevenue: true, + ttl: 60, + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + responseTimestamp: 1553534161770, + bidder: 'sortable', + adUnitCode: '728x90', + timeToRespond: 338 + }, + BID_TIMEOUT: [{ + auctionId: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + adUnitCode: '300x250', + bidder: 'sortable' + }] + }; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + xhr = sandbox.useFakeXMLHttpRequest(); + xhr.onCreate = (request) => requests.push(request); + clock = sandbox.useFakeTimers(); + sandbox.stub(events, 'getEvents').returns([]); + sandbox.stub(prebidGlobal, 'getGlobal').returns({ + version: '1.0', + bidderSettings: { + 'sortable': { + bidCpmAdjustment: function (number) { + return number * 0.95; + } + } + } + }); + + requests = []; + sortableAnalyticsAdapter.enableAnalytics(initialConfig); + }); + + afterEach(function() { + sandbox.restore(); + sortableAnalyticsAdapter.disableAnalytics(); + }); + + describe('initialize adapter', function() { + const settings = sortableAnalyticsAdapter.getOptions(); + + it('should init settings correctly and apply defaults', function() { + expect(settings).to.include({ + 'disableSessionTracking': false, + 'key': initialConfig.options.siteId, + 'protocol': 'https', + 'url': `https://pa.deployads.com/pae/${initialConfig.options.siteId}`, + 'timeoutForPbid': DEFAULT_PBID_TIMEOUT + }); + }); + it('should assign a pageview ID', function() { + expect(settings).to.have.own.property('pageviewId'); + }); + }); + + describe('events tracking', function() { + it('should send the PBID event', function() { + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, TEST_DATA.AUCTION_INIT); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, TEST_DATA.BID_REQUESTED); + events.emit(CONSTANTS.EVENTS.BID_ADJUSTMENT, TEST_DATA.BID_ADJUSTMENT_1); + events.emit(CONSTANTS.EVENTS.BID_ADJUSTMENT, TEST_DATA.BID_ADJUSTMENT_2); + events.emit(CONSTANTS.EVENTS.AUCTION_END, TEST_DATA.AUCTION_END); + events.emit(CONSTANTS.EVENTS.BID_WON, TEST_DATA.BID_WON_1); + events.emit(CONSTANTS.EVENTS.BID_WON, TEST_DATA.BID_WON_2); + + clock.tick(DEFAULT_PBID_TIMEOUT); + + expect(requests.length).to.equal(1); + let result = JSON.parse(requests[0].requestBody); + expect(result).to.have.own.property('pbid'); + expect(result.pbid).to.deep.include({ + ai: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + ac: ['300x250', '728x90'], + adi: ['88221d316425f7', '88221d316425f8'], + bs: 'sortable', + bid: ['26721042674416', '50fa575b41e596'], + bif: 0.95, + brc: 1, + brid: ['10141593b1d84a', '37a8760be6db23'], + rs: ['300x250', '728x90'], + btcp: [0.70, 0.50], + btcc: 'USD', + btin: true, + btsrc: 'sortable', + c: [0.70, 0.50], + cc: 'USD', + did: null, + inr: true, + it: true, + iw: true, + ito: false, + mt: 'banner', + rtp: true, + nif: 1, + pbv: '1.0', + siz: ['300x250', '728x90'], + st: 1553529405788, + tgid: ['medrec_1', 'lead_1'], + to: 3000, + trid: ['aa02b498-8a99-418e-bc59-6b6fd45f32de', 'b7e9e957-af4f-4c47-8ca7-41f01cb4f105'], + ttl: 60, + ttr: [331, 338], + u: 'test.com', + _count: 2 + }); + }); + + it('should track a late bidWon event', function() { + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, TEST_DATA.AUCTION_INIT); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, TEST_DATA.BID_REQUESTED); + events.emit(CONSTANTS.EVENTS.BID_ADJUSTMENT, TEST_DATA.BID_ADJUSTMENT_1); + events.emit(CONSTANTS.EVENTS.AUCTION_END, TEST_DATA.AUCTION_END); + + clock.tick(DEFAULT_PBID_TIMEOUT); + + events.emit(CONSTANTS.EVENTS.BID_WON, TEST_DATA.BID_WON_1); + + clock.tick(TIMEOUT_FOR_REGISTRY); + + expect(requests.length).to.equal(2); + const pbid_req = JSON.parse(requests[0].requestBody); + expect(pbid_req).to.have.own.property('pbid'); + const pbwon_req = JSON.parse(requests[1].requestBody); + expect(pbwon_req).to.have.own.property('pbrw'); + expect(pbwon_req.pbrw).to.deep.equal({ + ac: '300x250', + ai: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + bif: 0.95, + bs: 'sortable', + s: initialConfig.options.siteId, + cc: 'USD', + c: 0.70, + inr: true, + _count: 1, + _type: 'pbrw' + }); + }); + + it('should track late bidder timeouts', function() { + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, TEST_DATA.AUCTION_INIT); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, TEST_DATA.BID_REQUESTED); + events.emit(CONSTANTS.EVENTS.AUCTION_END, TEST_DATA.AUCTION_END); + clock.tick(DEFAULT_PBID_TIMEOUT); + events.emit(CONSTANTS.EVENTS.BID_TIMEOUT, TEST_DATA.BID_TIMEOUT); + + clock.tick(TIMEOUT_FOR_REGISTRY); + + expect(requests.length).to.equal(2); + const pbid_req = JSON.parse(requests[0].requestBody); + expect(pbid_req).to.have.own.property('pbid'); + const pbto_req = JSON.parse(requests[1].requestBody); + expect(pbto_req).to.have.own.property('pbto'); + expect(pbto_req.pbto).to.deep.equal({ + ai: 'fb8d579a-5c3f-4705-ab94-3cff39005d9e', + s: initialConfig.options.siteId, + ac: '300x250', + bs: 'sortable', + _type: 'pbto', + _count: 1 + }); + }); + + it('should track errors', function() { + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, TEST_DATA.AUCTION_INIT); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, {}); + + clock.tick(TIMEOUT_FOR_REGISTRY); + + expect(requests.length).to.equal(1); + const err_req = JSON.parse(requests[0].requestBody); + expect(err_req).to.have.own.property('pber'); + expect(err_req.pber).to.include({ + args: '{}', + s: initialConfig.options.siteId, + _count: 1, + ti: 'bidRequested', + _type: 'pber' + }); + expect(err_req.pber.msg).to.be.a('string'); + }); + }); +}); diff --git a/test/spec/modules/sortableBidAdapter_spec.js b/test/spec/modules/sortableBidAdapter_spec.js new file mode 100644 index 00000000000..98695f44ee0 --- /dev/null +++ b/test/spec/modules/sortableBidAdapter_spec.js @@ -0,0 +1,258 @@ +import { expect } from 'chai'; +import { spec } from 'modules/sortableBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; + +const ENDPOINT = `//c.deployads.com/openrtb2/auction?src=$$REPO_AND_VERSION$$&host=${utils.getTopWindowLocation().host}`; + +describe('sortableBidAdapter', function() { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + function makeBid() { + return { + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + 'keywords': { + 'key1': 'val1', + 'key2': 'val2' + }, + 'floorSizeMap': { + '728x90': 0.15, + '300x250': 1.20 + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + } + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(makeBid())).to.equal(true); + }); + + it('should return false when tagId not passed correctly', function () { + let bid = makeBid(); + delete bid.params.tagId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when sizes not passed correctly', function () { + let bid = makeBid(); + delete bid.sizes; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when sizes are wrong length', function () { + let bid = makeBid(); + bid.sizes = [[300]]; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = makeBid(); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when the floorSizeMap is invalid', function () { + let bid = makeBid(); + bid.params.floorSizeMap = { + 'sixforty by foureighty': 1234 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params.floorSizeMap = { + '728x90': 'three' + } + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params.floorSizeMap = 'a'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when the floorSizeMap is missing or empty', function () { + let bid = makeBid(); + bid.params.floorSizeMap = {}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + delete bid.params.floorSizeMap; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return false when the keywords are invalid', function () { + let bid = makeBid(); + bid.params.keywords = { + 'badval': 1234 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params.keywords = 'a'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when the keywords are missing or empty', function () { + let bid = makeBid(); + bid.params.keywords = {}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + delete bid.params.keywords; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + 'floor': 0.21, + 'keywords': { + 'key1': 'val1', + 'key2': 'val2' + }, + 'floorSizeMap': { + '728x90': 0.15, + '300x250': 1.20 + } + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + + const request = spec.buildRequests(bidRequests); + const requestBody = JSON.parse(request.data); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('attaches source and version to endpoint URL as query params', function () { + expect(request.url).to.equal(ENDPOINT); + }); + + it('sends screen dimensions', function () { + expect(requestBody.site.device.w).to.equal(screen.width); + expect(requestBody.site.device.h).to.equal(screen.height); + }); + + it('includes the ad size in the bid request', function () { + expect(requestBody.imp[0].banner.format[0].w).to.equal(300); + expect(requestBody.imp[0].banner.format[0].h).to.equal(250); + }); + + it('includes the params in the bid request', function () { + expect(requestBody.imp[0].ext.keywords).to.deep.equal( + {'key1': 'val1', + 'key2': 'val2'} + ); + expect(requestBody.site.publisher.id).to.equal('example.com'); + expect(requestBody.imp[0].tagid).to.equal('403370'); + expect(requestBody.imp[0].bidfloor).to.equal(0.21); + }); + + it('should have the floor size map set', function () { + expect(requestBody.imp[0].ext.floorSizeMap).to.deep.equal({ + '728x90': 0.15, + '300x250': 1.20 + }); + }); + }); + + describe('interpretResponse', function () { + function makeResponse() { + return { + body: { + 'id': '5e5c23a5ba71e78', + 'seatbid': [ + { + 'bid': [ + { + 'id': '6vmb3isptf', + 'crid': 'sortablescreative', + 'impid': '322add653672f68', + 'price': 1.22, + 'adm': '', + 'attr': [5], + 'h': 90, + 'nurl': 'http://nurl', + 'w': 728 + } + ], + 'seat': 'MOCK' + } + ], + 'bidid': '5e5c23a5ba71e78' + } + }; + } + + const expectedBid = { + 'requestId': '322add653672f68', + 'cpm': 1.22, + 'width': 728, + 'height': 90, + 'creativeId': 'sortablescreative', + 'dealId': null, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ttl': 60, + 'ad': '
    ' + }; + + it('should get the correct bid response', function () { + let result = spec.interpretResponse(makeResponse()); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(expectedBid); + }); + + it('should handle a missing crid', function () { + let noCridResponse = makeResponse(); + delete noCridResponse.body.seatbid[0].bid[0].crid; + const fallbackCrid = noCridResponse.body.seatbid[0].bid[0].id; + let noCridResult = Object.assign({}, expectedBid, {'creativeId': fallbackCrid}); + let result = spec.interpretResponse(noCridResponse); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noCridResult); + }); + + it('should handle a missing nurl', function () { + let noNurlResponse = makeResponse(); + delete noNurlResponse.body.seatbid[0].bid[0].nurl; + let noNurlResult = Object.assign({}, expectedBid); + noNurlResult.ad = ''; + let result = spec.interpretResponse(noNurlResponse); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noNurlResult); + }); + + it('should handle a missing adm', function () { + let noAdmResponse = makeResponse(); + delete noAdmResponse.body.seatbid[0].bid[0].adm; + let noAdmResult = Object.assign({}, expectedBid); + delete noAdmResult.ad; + noAdmResult.adUrl = 'http://nurl'; + let result = spec.interpretResponse(noAdmResponse); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noAdmResult); + }); + + it('handles empty bid response', function () { + let response = { + body: { + 'id': '5e5c23a5ba71e78', + 'seatbid': [] + } + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/sovrnAnalyticsAdapter_spec.js b/test/spec/modules/sovrnAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..299e22ca790 --- /dev/null +++ b/test/spec/modules/sovrnAnalyticsAdapter_spec.js @@ -0,0 +1,546 @@ +import sovrnAnalyticsAdapter from '../../../modules/sovrnAnalyticsAdapter' +import { expect } from 'chai' +import {config} from 'src/config' +import adaptermanager from 'src/adapterManager' +var assert = require('assert'); + +let events = require('src/events'); +let constants = require('src/constants.json'); + +/** + * Emit analytics events + * @param {array} eventArr - array of objects to define the events that will fire + * @param {object} eventObj - key is eventType, value is event + * @param {string} auctionId - the auction id to attached to the events + */ +function emitEvent(eventType, event, auctionId) { + event.auctionId = auctionId; + events.emit(constants.EVENTS[eventType], event); +} + +let auctionStartTimestamp = Date.now(); +let timeout = 3000; +let auctionInit = { + timestamp: auctionStartTimestamp, + timeout: timeout +}; +let bidderCode = 'sovrn'; +let bidderRequestId = '123bri'; +let adUnitCode = 'div'; +let adUnitCode2 = 'div2'; +let bidId = 'bidid'; +let bidId2 = 'bidid2'; +let tId = '7aafa3ee-a80a-46d7-a4a0-cbcba463d97a'; +let tId2 = '99dca3ee-a80a-46d7-a4a0-cbcba463d97e'; +let bidRequested = { + auctionStart: auctionStartTimestamp, + bidderCode: bidderCode, + bidderRequestId: bidderRequestId, + bids: [ + { + adUnitCode: adUnitCode, + bidId: bidId, + bidder: bidderCode, + bidderRequestId: '10340af0c7dc72', + sizes: [[300, 250]], + startTime: auctionStartTimestamp + 100, + transactionId: tId + }, + { + adUnitCode: adUnitCode2, + bidId: bidId2, + bidder: bidderCode, + bidderRequestId: '10340af0c7dc72', + sizes: [[300, 250]], + startTime: auctionStartTimestamp + 100, + transactionId: tId2 + } + ], + doneCbCallCount: 1, + start: auctionStartTimestamp, + timeout: timeout +}; +let bidResponse = { + bidderCode: bidderCode, + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '3870e27a5752fb', + mediaType: 'banner', + source: 'client', + requestId: bidId, + cpm: 0.8584999918937682, + creativeId: 'cridprebidrtb', + dealId: null, + currency: 'USD', + netRevenue: true, + ad: '
    divvy mcdiv
    ', + ttl: 60000, + responseTimestamp: auctionStartTimestamp + 150, + requestTimestamp: auctionStartTimestamp + 100, + bidder: bidderCode, + adUnitCode: adUnitCode, + timeToRespond: 50, + pbLg: '0.50', + pbMg: '0.80', + pbHg: '0.85', + pbAg: '0.85', + pbDg: '0.85', + pbCg: '', + size: '300x250', + adserverTargeting: { + hb_bidder: bidderCode, + hb_adid: '3870e27a5752fb', + hb_pb: '0.85' + }, + status: 'rendered' +}; + +let bidResponse2 = { + bidderCode: bidderCode, + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '9999e27a5752fb', + mediaType: 'banner', + source: 'client', + requestId: bidId2, + cpm: 0.12, + creativeId: 'cridprebidrtb', + dealId: null, + currency: 'USD', + netRevenue: true, + ad: '
    divvy mcdiv
    ', + ttl: 60000, + responseTimestamp: auctionStartTimestamp + 150, + requestTimestamp: auctionStartTimestamp + 100, + bidder: bidderCode, + adUnitCode: adUnitCode2, + timeToRespond: 50, + pbLg: '0.10', + pbMg: '0.10', + pbHg: '0.10', + pbAg: '0.10', + pbDg: '0.10', + pbCg: '', + size: '300x250', + adserverTargeting: { + hb_bidder: bidderCode, + hb_adid: '9999e27a5752fb', + hb_pb: '0.10' + }, + status: 'rendered' +}; +let bidAdjustment = {}; +for (var k in bidResponse) bidAdjustment[k] = bidResponse[k]; +bidAdjustment.cpm = 0.8; +let bidAdjustmentNoMatchingRequest = { + bidderCode: 'not-sovrn', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '1', + mediaType: 'banner', + source: 'client', + requestId: '1', + cpm: 0.10, + creativeId: '', + dealId: null, + currency: 'USD', + netRevenue: true, + ad: '
    divvy mcdiv
    ', + ttl: 60000, + responseTimestamp: auctionStartTimestamp + 150, + requestTimestamp: auctionStartTimestamp + 100, + bidder: 'not-sovrn', + adUnitCode: '', + timeToRespond: 50, + pbLg: '0.00', + pbMg: '0.10', + pbHg: '0.10', + pbAg: '0.10', + pbDg: '0.10', + pbCg: '', + size: '300x250', + adserverTargeting: { + hb_bidder: 'not-sovrn', + hb_adid: '1', + hb_pb: '0.10' + }, +}; +let bidResponseNoMatchingRequest = bidAdjustmentNoMatchingRequest; + +describe('Sovrn Analytics Adapter', function () { + let xhr; + let requests; + beforeEach(() => { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + requests = []; + sinon.stub(events, 'getEvents').returns([]); + }); + afterEach(() => { + xhr.restore(); + events.getEvents.restore(); + }); + + describe('enableAnalytics ', function () { + beforeEach(() => { + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + + it('should catch all events if affiliate id present', function () { + adaptermanager.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + + events.emit(constants.EVENTS.AUCTION_INIT, {}); + events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.BID_REQUESTED, {}); + events.emit(constants.EVENTS.BID_RESPONSE, {}); + events.emit(constants.EVENTS.BID_WON, {}); + + sinon.assert.callCount(sovrnAnalyticsAdapter.track, 5); + }); + + it('should catch no events if no affiliate id', function () { + adaptermanager.enableAnalytics({ + provider: 'sovrn', + options: { + } + }); + + events.emit(constants.EVENTS.AUCTION_INIT, {}); + events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.BID_REQUESTED, {}); + events.emit(constants.EVENTS.BID_RESPONSE, {}); + events.emit(constants.EVENTS.BID_WON, {}); + + sinon.assert.callCount(sovrnAnalyticsAdapter.track, 0); + }); + }); + + describe('sovrnAnalyticsAdapter ', function() { + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should have correct type', function () { + assert.equal(sovrnAnalyticsAdapter.getAdapterType(), 'endpoint') + }) + }); + + describe('auction data collector ', function() { + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should create auctiondata record from init ', function () { + let auctionId = '123.123.123.123'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + assert(currentAuction); + let expectedTimeOutData = { + buffer: config.getConfig('timeoutBuffer'), + bidder: config.getConfig('bidderTimeout'), + }; + expect(currentAuction.auction.timeouts).to.deep.equal(expectedTimeOutData); + assert.equal(currentAuction.auction.payload, 'auction'); + assert.equal(currentAuction.auction.priceGranularity, config.getConfig('priceGranularity')) + assert.equal(currentAuction.auction.auctionId, auctionId); + assert.equal(currentAuction.auction.sovrnId, 123); + }); + it('should create a bidrequest object ', function() { + let auctionId = '234.234.234.234'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + assert(currentAuction); + let requests = currentAuction.auction.requests; + assert(requests); + assert.equal(requests.length, 1); + assert.equal(requests[0].bidderCode, bidderCode); + assert.equal(requests[0].bidderRequestId, bidderRequestId); + assert.equal(requests[0].timeout, timeout); + let bids = requests[0].bids; + assert(bids); + assert.equal(bids.length, 2); + assert.equal(bids[0].bidId, bidId); + assert.equal(bids[0].bidder, bidderCode); + assert.equal(bids[0].transactionId, tId); + assert.equal(bids[0].sizes.length, 1); + assert.equal(bids[0].sizes[0][0], 300); + assert.equal(bids[0].sizes[0][1], 250); + expect(requests[0]).to.not.have.property('doneCbCallCount'); + expect(requests[0]).to.not.have.property('auctionId'); + }); + it('should add results to the bid with response ', function () { + let auctionId = '345.345.345.345'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_RESPONSE', bidResponse, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + let returnedBid = currentAuction.auction.requests[0].bids[0]; + assert.equal(returnedBid.bidId, bidId); + assert.equal(returnedBid.bidder, bidderCode); + assert.equal(returnedBid.transactionId, tId); + assert.equal(returnedBid.sizes.length, 1); + assert.equal(returnedBid.sizes[0][0], 300); + assert.equal(returnedBid.sizes[0][1], 250); + assert.equal(returnedBid.adserverTargeting.hb_adid, '3870e27a5752fb'); + assert.equal(returnedBid.adserverTargeting.hb_bidder, bidderCode); + assert.equal(returnedBid.adserverTargeting.hb_pb, '0.85'); + assert.equal(returnedBid.cpm, 0.8584999918937682); + }); + it('should add new unsynced bid if no request exists for response ', function () { + let auctionId = '456.456.456.456'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_RESPONSE', bidResponseNoMatchingRequest, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + let requests = currentAuction.auction.requests; + assert(requests); + assert.equal(requests.length, 1); + let bidRequest = requests[0].bids[0]; + expect(bidRequest).to.not.have.property('adserverTargeting'); + expect(bidRequest).to.not.have.property('cpm'); + expect(currentAuction.auction.unsynced[0]).to.deep.equal(bidResponseNoMatchingRequest); + }); + it('should adjust the bid ', function () { + let auctionId = '567.567.567.567'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_ADJUSTMENT', bidResponse, auctionId); + emitEvent('BID_RESPONSE', bidAdjustment, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + let returnedBid = currentAuction.auction.requests[0].bids[0]; + assert.equal(returnedBid.cpm, 0.8); + assert.equal(returnedBid.originalValues.cpm, 0.8584999918937682); + }); + }); + describe('auction data send ', function() { + let expectedPostBody = { + sovrnId: 123, + auctionId: '678.678.678.678', + payload: 'auction', + priceGranularity: 'medium', + }; + let expectedRequests = { + bidderCode: 'sovrn', + bidderRequestId: '123bri', + timeout: 3000 + }; + let expectedBids = { + adUnitCode: 'div', + bidId: 'bidid', + bidder: 'sovrn', + bidderRequestId: '10340af0c7dc72', + transactionId: '7aafa3ee-a80a-46d7-a4a0-cbcba463d97a', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '3870e27a5752fb', + mediaType: 'banner', + source: 'client', + cpm: 0.8584999918937682, + creativeId: 'cridprebidrtb', + dealId: null, + currency: 'USD', + netRevenue: true, + ttl: 60000, + timeToRespond: 50, + size: '300x250', + status: 'rendered', + isAuctionWinner: true + }; + let SecondAdUnitExpectedBids = { + adUnitCode: 'div2', + bidId: 'bidid2', + bidder: 'sovrn', + bidderRequestId: '10340af0c7dc72', + transactionId: '99dca3ee-a80a-46d7-a4a0-cbcba463d97e', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '9999e27a5752fb', + mediaType: 'banner', + source: 'client', + cpm: 0.12, + creativeId: 'cridprebidrtb', + dealId: null, + currency: 'USD', + netRevenue: true, + ttl: 60000, + timeToRespond: 50, + size: '300x250', + status: 'rendered', + isAuctionWinner: true + }; + let expectedAdServerTargeting = { + hb_bidder: 'sovrn', + hb_adid: '3870e27a5752fb', + hb_pb: '0.85' + }; + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should send auction data ', function () { + let auctionId = '678.678.678.678'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_RESPONSE', bidResponse, auctionId); + emitEvent('BID_RESPONSE', bidResponse2, auctionId) + emitEvent('AUCTION_END', {}, auctionId); + let requestBody = JSON.parse(requests[0].requestBody); + let requestsFromRequestBody = requestBody.requests[0]; + let bidsFromRequests = requestsFromRequestBody.bids[0]; + expect(requestBody).to.deep.include(expectedPostBody); + expect(requestBody.timeouts).to.deep.equal({buffer: 400, bidder: 3000}); + expect(requestsFromRequestBody).to.deep.include(expectedRequests); + expect(bidsFromRequests).to.deep.include(expectedBids); + let bidsFromRequests2 = requestsFromRequestBody.bids[1]; + expect(bidsFromRequests2).to.deep.include(SecondAdUnitExpectedBids); + expect(bidsFromRequests.adserverTargeting).to.deep.include(expectedAdServerTargeting); + }); + }); + describe('bid won data send ', function() { + let auctionId = '789.789.789.789'; + let creativeId = 'cridprebidrtb'; + let requestId = 'requestId69'; + let bidWonEvent = { + ad: 'html', + adId: 'adId', + adUnitCode: adUnitCode, + auctionId: auctionId, + bidder: bidderCode, + bidderCode: bidderCode, + cpm: 1.01, + creativeId: creativeId, + currency: 'USD', + height: 250, + mediaType: 'banner', + requestId: requestId, + size: '300x250', + source: 'client', + status: 'rendered', + statusMessage: 'Bid available', + timeToRespond: 421, + ttl: 60, + width: 300 + }; + let expectedBidWonBody = { + sovrnId: 123, + payload: 'winner' + }; + let expectedWinningBid = { + bidderCode: bidderCode, + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: 'adId', + mediaType: 'banner', + source: 'client', + requestId: requestId, + cpm: 1.01, + creativeId: creativeId, + currency: 'USD', + ttl: 60, + auctionId: auctionId, + bidder: bidderCode, + adUnitCode: adUnitCode, + timeToRespond: 421, + size: '300x250', + }; + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should send bid won data ', function () { + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_WON', bidWonEvent, auctionId); + let requestBody = JSON.parse(requests[0].requestBody); + expect(requestBody).to.deep.include(expectedBidWonBody); + expect(requestBody.winningBid).to.deep.include(expectedWinningBid); + }); + }); + describe('Error Tracking', function() { + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics() + sovrnAnalyticsAdapter.track.restore() + }); + it('should send an error message when a bid is received for a closed auction', function() { + let auctionId = '678.678.678.678'; + emitEvent('AUCTION_INIT', auctionInit, auctionId) + emitEvent('BID_REQUESTED', bidRequested, auctionId) + emitEvent('AUCTION_END', {}, auctionId) + requests[0].respond(200) + emitEvent('BID_RESPONSE', bidResponse, auctionId) + let requestBody = JSON.parse(requests[1].requestBody) + expect(requestBody.payload).to.equal('error') + expect(requestBody.message).to.include('Event Received after Auction Close Auction Id') + }) + }) +}) diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index b19b79c7886..7179ec00bc3 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -1,14 +1,14 @@ import { expect } from 'chai'; -import { spec } from 'modules/sovrnBidAdapter'; +import { spec, LogError } from 'modules/sovrnBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -import { REPO_AND_VERSION } from 'src/constants'; +import { SSL_OP_SINGLE_ECDH_USE } from 'constants'; -const ENDPOINT = `//ap.lijit.com/rtb/bid?src=${REPO_AND_VERSION}`; +const ENDPOINT = `//ap.lijit.com/rtb/bid?src=$$REPO_AND_VERSION$$`; describe('sovrnBidAdapter', function() { const adapter = newBidder(spec); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'sovrn', 'params': { @@ -16,30 +16,31 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when tagid not passed correctly', () => { + it('should return false when tagid not passed correctly', function () { bid.params.tagid = 'ABCD'; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when require params are not passed', () => { + it('should return false when require params are not passed', function () { let bid = Object.assign({}, bid); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { const bidRequests = [{ 'bidder': 'sovrn', 'params': { @@ -47,7 +48,8 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', @@ -56,15 +58,42 @@ describe('sovrnBidAdapter', function() { const request = spec.buildRequests(bidRequests); - it('sends bid request to our endpoint via POST', () => { + it('sends bid request to our endpoint via POST', function () { expect(request.method).to.equal('POST'); }); - it('attaches source and version to endpoint URL as query params', () => { + it('attaches source and version to endpoint URL as query params', function () { expect(request.url).to.equal(ENDPOINT) }); - it('sends \'iv\' as query param if present', () => { + it('sets the proper banner object', function() { + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) + expect(payload.imp[0].banner.w).to.equal(1) + expect(payload.imp[0].banner.h).to.equal(1) + }) + + it('accepts a single array as a size', function() { + const singleSize = [{ + 'bidder': 'sovrn', + 'params': { + 'tagid': '403370', + 'iv': 'vet' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [300, 250], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + const request = spec.buildRequests(singleSize) + const payload = JSON.parse(request.data) + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]) + expect(payload.imp[0].banner.w).to.equal(1) + expect(payload.imp[0].banner.h).to.equal(1) + }) + + it('sends \'iv\' as query param if present', function () { const ivBidRequests = [{ 'bidder': 'sovrn', 'params': { @@ -73,7 +102,8 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', @@ -81,10 +111,10 @@ describe('sovrnBidAdapter', function() { }]; const request = spec.buildRequests(ivBidRequests); - expect(request.data).to.contain('"iv":"vet"') + expect(request.url).to.contain('iv=vet') }); - it('sends gdpr info if exists', () => { + it('sends gdpr info if exists', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { 'bidderCode': 'sovrn', @@ -107,7 +137,7 @@ describe('sovrnBidAdapter', function() { expect(payload.user.ext.consent).to.equal(consentString); }); - it('converts tagid to string', () => { + it('converts tagid to string', function () { const ivBidRequests = [{ 'bidder': 'sovrn', 'params': { @@ -116,7 +146,8 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', @@ -128,9 +159,9 @@ describe('sovrnBidAdapter', function() { }) }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let response; - beforeEach(() => { + beforeEach(function () { response = { body: { 'id': '37386aade21a71', @@ -150,7 +181,7 @@ describe('sovrnBidAdapter', function() { }; }); - it('should get the correct bid response', () => { + it('should get the correct bid response', function () { let expectedResponse = [{ 'requestId': '263c448586f5a1', 'cpm': 0.45882675, @@ -169,7 +200,7 @@ describe('sovrnBidAdapter', function() { expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); }); - it('crid should default to the bid id if not on the response', () => { + it('crid should default to the bid id if not on the response', function () { delete response.body.seatbid[0].bid[0].crid; let expectedResponse = [{ 'requestId': '263c448586f5a1', @@ -189,7 +220,7 @@ describe('sovrnBidAdapter', function() { expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); }); - it('should get correct bid response when dealId is passed', () => { + it('should get correct bid response when dealId is passed', function () { response.body.dealid = 'baking'; let expectedResponse = [{ @@ -210,7 +241,7 @@ describe('sovrnBidAdapter', function() { expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); }); - it('handles empty bid response', () => { + it('handles empty bid response', function () { let response = { body: { 'id': '37386aade21a71', @@ -221,4 +252,146 @@ describe('sovrnBidAdapter', function() { expect(result.length).to.equal(0); }); }); -}); + + describe('getUserSyncs ', () => { + let syncOptions = {iframeEnabled: true, pixelEnabled: true}; + let iframeDisabledSyncOptions = {iframeEnabled: false, pixelEnabled: true}; + let serverResponse = [ + { + 'body': { + 'id': '546956d68c757f', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'a_448326_16c2ada014224bee815a90d2248322f5', + 'impid': '2a3826aae345f4', + 'price': 1.0099999904632568, + 'nurl': 'http://localhost/rtb/impression?bannerid=220958&campaignid=3890&rtb_tid=15588614-75d2-40ab-b27e-13d2127b3c2e&rpid=1295&seatid=seat1&zoneid=448326&cb=26900712&tid=a_448326_16c2ada014224bee815a90d2248322f5', + 'adm': 'yo a creative', + 'crid': 'cridprebidrtb', + 'w': 160, + 'h': 600 + }, + { + 'id': 'a_430392_beac4c1515da4576acf6cb9c5340b40c', + 'impid': '3cf96fd26ed4c5', + 'price': 1.0099999904632568, + 'nurl': 'http://localhost/rtb/impression?bannerid=220957&campaignid=3890&rtb_tid=5bc0e68b-3492-448d-a6f9-26fa3fd0b646&rpid=1295&seatid=seat1&zoneid=430392&cb=62735099&tid=a_430392_beac4c1515da4576acf6cb9c5340b40c', + 'adm': 'yo a creative', + 'crid': 'cridprebidrtb', + 'w': 300, + 'h': 250 + }, + ] + } + ], + 'ext': { + 'iid': 13487408 + } + }, + 'headers': {} + } + ]; + it('should return if iid present on server response & iframe syncs enabled', () => { + let expectedReturnStatement = [ + { + 'type': 'iframe', + 'url': '//ap.lijit.com/beacon?informer=13487408&gdpr_consent=', + } + ] + let returnStatement = spec.getUserSyncs(syncOptions, serverResponse); + expect(returnStatement[0]).to.deep.equal(expectedReturnStatement[0]); + }) + + it('should not return if iid missing on server response', () => { + let returnStatement = spec.getUserSyncs(syncOptions, []) + expect(returnStatement).to.be.empty; + }) + + it('should not return if iframe syncs disabled', () => { + let returnStatement = spec.getUserSyncs(iframeDisabledSyncOptions, serverResponse) + expect(returnStatement).to.be.empty + }) + }) + describe('LogError', () => { + it('should build and append an error object', () => { + const thrown = { + message: 'message', + stack: 'stack' + } + const data = {name: 'Oscar Hathenswiotch'} + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(1) + const errdata = JSON.parse(atob(errList[0].url.split('=')[1])) + expect(errdata.d.name).to.equal('Oscar Hathenswiotch') + }) + it('should drop data when there is too much', () => { + const thrown = { + message: 'message', + stack: 'stack' + } + const tooLong = () => { + let str = '' + for (let i = 0; i < 10000; i++) { + str = str + String.fromCharCode(i % 100) + } + return str + } + const data = {name: 'Oscar Hathenswiotch', tooLong: tooLong()} + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(2) + const errdata = JSON.parse(atob(errList[1].url.split('=')[1])) + expect(errdata.d).to.be.an('undefined') + }) + it('should drop data and stack when there is too much', () => { + const thrown = { + message: 'message', + stack: 'stack' + } + const tooLong = () => { + let str = '' + for (let i = 0; i < 10000; i++) { + str = str + String.fromCharCode(i % 100) + } + return str + } + const data = {name: 'Oscar Hathenswiotch'} + thrown.stack = tooLong() + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(3) + const errdata = JSON.parse(atob(errList[2].url.split('=')[1])) + expect(errdata.d).to.be.an('undefined') + expect(errdata.s).to.be.an('undefined') + }) + it('should drop send a reduced message when other reduction methods fail', () => { + const thrown = { + message: 'message', + stack: 'stack' + } + const tooLong = () => { + let str = '' + for (let i = 0; i < 10000; i++) { + str = str + String.fromCharCode(i % 100) + } + return str + } + const data = {name: 'Oscar Hathenswiotch'} + thrown.message = tooLong() + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(4) + const errdata = JSON.parse(atob(errList[3].url.split('=')[1])) + expect(errdata.d).to.be.an('undefined') + expect(errdata.s).to.be.an('undefined') + expect(errdata.m).to.equal('unknown error message') + }) + }) +}) diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js new file mode 100644 index 00000000000..33cf85a855e --- /dev/null +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -0,0 +1,428 @@ +import {expect} from 'chai'; +import {spec} from 'modules/spotxBidAdapter'; + +describe('the spotx adapter', function () { + function getValidBidObject() { + return { + bidId: 123, + mediaTypes: { + video: { + playerSize: [['300', '200']] + } + }, + params: { + channel_id: 12345, + } + }; + }; + + describe('isBidRequestValid', function() { + var bid; + + beforeEach(function() { + bid = getValidBidObject(); + }); + + it('should fail validation if the bid isn\'t defined or not an object', function() { + var result = spec.isBidRequestValid(); + + expect(result).to.equal(false); + + result = spec.isBidRequestValid('not an object'); + + expect(result).to.equal(false); + }); + + it('should succeed validation with all the right parameters', function() { + expect(spec.isBidRequestValid(getValidBidObject())).to.equal(true); + }); + + it('should succeed validation with mediaType and outstream_function or outstream_options', function() { + bid.mediaType = 'video'; + bid.params.outstream_function = 'outstream_func'; + + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.params.outstream_function; + bid.params.outstream_options = { + slot: 'elemID' + }; + + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should succeed with ad_unit outstream and outstream function set', function() { + bid.params.ad_unit = 'outstream'; + bid.params.outstream_function = function() {}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should succeed with ad_unit outstream, options set for outstream and slot provided', function() { + bid.params.ad_unit = 'outstream'; + bid.params.outstream_options = {slot: 'ad_container_id'}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should fail without a channel_id', function() { + delete bid.params.channel_id; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without playerSize', function() { + delete bid.mediaTypes.video.playerSize; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without video', function() { + delete bid.mediaTypes.video; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail with ad_unit outstream but no options set for outstream', function() { + bid.params.ad_unit = 'outstream'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail with ad_unit outstream, options set for outstream but no slot provided', function() { + bid.params.ad_unit = 'outstream'; + bid.params.outstream_options = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + describe('buildRequests', function() { + var bid, bidRequestObj; + + beforeEach(function() { + bid = getValidBidObject(); + bidRequestObj = {refererInfo: {referer: 'prebid.js'}}; + }); + + it('should build a very basic request', function() { + var request = spec.buildRequests([bid], bidRequestObj)[0]; + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('//search.spotxchange.com/openrtb/2.3/dados/12345'); + expect(request.bidRequest).to.equal(bidRequestObj); + expect(request.data.id).to.equal(12345); + expect(request.data.ext.wrap_response).to.equal(1); + expect(request.data.imp.id).to.match(/\d+/); + expect(request.data.imp.secure).to.equal(0); + expect(request.data.imp.video).to.deep.equal({ + ext: { + sdk_name: 'Prebid 1+', + versionOrtb: '2.3' + }, + h: '200', + mimes: [ + 'application/javascript', + 'video/mp4', + 'video/webm' + ], + w: '300' + }); + expect(request.data.site).to.deep.equal({ + content: 'content', + id: '', + page: 'prebid.js' + }); + }); + it('should change request parameters based on options sent', function() { + var request = spec.buildRequests([bid], bidRequestObj)[0]; + expect(request.data.imp.video.ext).to.deep.equal({ + sdk_name: 'Prebid 1+', + versionOrtb: '2.3' + }); + + bid.params = { + channel_id: 54321, + ad_mute: 1, + hide_skin: 1, + ad_volume: 1, + ad_unit: 'incontent', + outstream_options: {foo: 'bar'}, + outstream_function: '987', + custom: {bar: 'foo'}, + price_floor: 123, + start_delay: true + }; + + request = spec.buildRequests([bid], bidRequestObj)[0]; + expect(request.data.id).to.equal(54321); + expect(request.data.imp.video.ext).to.deep.equal({ + ad_volume: 1, + ad_unit: 'incontent', + hide_skin: 1, + outstream_options: {foo: 'bar'}, + outstream_function: '987', + custom: {bar: 'foo'}, + sdk_name: 'Prebid 1+', + versionOrtb: '2.3' + }); + expect(request.data.imp.video.startdelay).to.equal(1); + expect(request.data.imp.bidfloor).to.equal(123); + }); + + it('should process premarket bids', function() { + var request; + sinon.stub(Date, 'now').returns(1000); + + bid.params.pre_market_bids = [{ + vast_url: 'prebid.js', + deal_id: '123abc', + price: 12, + currency: 'USD' + }]; + + request = spec.buildRequests([bid], bidRequestObj)[0]; + expect(request.data.imp.video.ext.pre_market_bids).to.deep.equal([ + { + 'cur': 'USD', + 'ext': { + 'event_log': [ + {} + ] + }, + 'id': '123abc', + 'seatbid': [ + { + 'bid': [ + { + 'adm': 'prebid.js', + 'dealid': '123abc', + 'impid': 1000, + 'price': 12, + } + ] + } + ] + } + ]); + Date.now.restore(); + }); + + it('should pass GDPR params', function() { + var request; + + bidRequestObj.gdprConsent = { + consentString: 'consent123', + gdprApplies: true + }; + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.regs.ext.gdpr).to.equal(1); + expect(request.data.user.ext.consent).to.equal('consent123'); + }); + }); + + describe('interpretResponse', function() { + var serverResponse, bidderRequestObj; + + beforeEach(function() { + bidderRequestObj = { + bidRequest: { + bids: [{ + mediaTypes: { + video: { + playerSize: [['400', '300']] + } + }, + bidId: 123, + params: { + player_width: 400, + player_height: 300, + content_page_url: 'prebid.js', + ad_mute: 1, + outstream_options: {foo: 'bar'}, + outstream_function: 'function' + } + }, { + mediaTypes: { + video: { + playerSize: [['200', '100']] + } + }, + bidId: 124, + params: { + player_width: 200, + player_height: 100, + content_page_url: 'prebid.js', + ad_mute: 1, + outstream_options: {foo: 'bar'}, + outstream_function: 'function' + } + }] + } + }; + + serverResponse = { + body: { + id: 12345, + seatbid: [{ + bid: [{ + impid: 123, + cur: 'USD', + price: 12, + crid: 321, + w: 400, + h: 300, + ext: { + cache_key: 'cache123', + slot: 'slot123' + } + }, { + impid: 124, + cur: 'USD', + price: 13, + w: 200, + h: 100, + ext: { + cache_key: 'cache124', + slot: 'slot124' + } + }] + }] + } + }; + }); + + it('should return an array of bid responses', function() { + var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + expect(responses).to.be.an('array').with.length(2); + expect(responses[0].cache_key).to.equal('cache123'); + expect(responses[0].channel_id).to.equal(12345); + expect(responses[0].cpm).to.equal(12); + expect(responses[0].creativeId).to.equal(321); + expect(responses[0].currency).to.equal('USD'); + expect(responses[0].height).to.equal(300); + expect(responses[0].mediaType).to.equal('video'); + expect(responses[0].netRevenue).to.equal(true); + expect(responses[0].requestId).to.equal(123); + expect(responses[0].ttl).to.equal(360); + expect(responses[0].vastUrl).to.equal('//search.spotxchange.com/ad/vast.html?key=cache123'); + expect(responses[0].width).to.equal(400); + expect(responses[1].cache_key).to.equal('cache124'); + expect(responses[1].channel_id).to.equal(12345); + expect(responses[1].cpm).to.equal(13); + expect(responses[1].creativeId).to.equal(''); + expect(responses[1].currency).to.equal('USD'); + expect(responses[1].height).to.equal(100); + expect(responses[1].mediaType).to.equal('video'); + expect(responses[1].netRevenue).to.equal(true); + expect(responses[1].requestId).to.equal(124); + expect(responses[1].ttl).to.equal(360); + expect(responses[1].vastUrl).to.equal('//search.spotxchange.com/ad/vast.html?key=cache124'); + expect(responses[1].width).to.equal(200); + }); + }); + + describe('oustreamRender', function() { + var serverResponse, bidderRequestObj; + + beforeEach(function() { + bidderRequestObj = { + bidRequest: { + bids: [{ + mediaTypes: { + video: { + playerSize: [['400', '300']] + } + }, + bidId: 123, + params: { + ad_unit: 'outstream', + player_width: 400, + player_height: 300, + content_page_url: 'prebid.js', + outstream_options: { + ad_mute: 1, + foo: 'bar', + slot: 'slot123', + playersize_auto_adapt: true, + custom_override: { + digitrust_opt_out: 1, + vast_url: 'bad_vast' + } + }, + } + }] + } + }; + + serverResponse = { + body: { + id: 12345, + seatbid: [{ + bid: [{ + impid: 123, + cur: 'USD', + price: 12, + crid: 321, + w: 400, + h: 300, + ext: { + cache_key: 'cache123', + slot: 'slot123' + } + }] + }] + } + }; + }); + + it('should attempt to insert the EASI script', function() { + var scriptTag; + sinon.stub(window.document, 'getElementById').returns({ + appendChild: sinon.stub().callsFake(function(script) { scriptTag = script }) + }); + var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + + responses[0].renderer.render(responses[0]); + + expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); + expect(scriptTag.getAttribute('src')).to.equal('//js.spotx.tv/easi/v1/12345.js'); + expect(scriptTag.getAttribute('data-spotx_channel_id')).to.equal('12345'); + expect(scriptTag.getAttribute('data-spotx_vast_url')).to.equal('//search.spotxchange.com/ad/vast.html?key=cache123'); + expect(scriptTag.getAttribute('data-spotx_ad_unit')).to.equal('incontent'); + expect(scriptTag.getAttribute('data-spotx_collapse')).to.equal('0'); + expect(scriptTag.getAttribute('data-spotx_autoplay')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_blocked_autoplay_override_mode')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_video_slot_can_autoplay')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_digitrust_opt_out')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_content_width')).to.equal('400'); + expect(scriptTag.getAttribute('data-spotx_content_height')).to.equal('300'); + window.document.getElementById.restore(); + }); + + it('should append into an iframe', function() { + var scriptTag; + sinon.stub(window.document, 'getElementById').returns({ + nodeName: 'IFRAME', + contentDocument: { + body: { + appendChild: sinon.stub().callsFake(function(script) { scriptTag = script }) + } + } + }); + + bidderRequestObj.bidRequest.bids[0].params.outstream_options.in_iframe = 'iframeId'; + + var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + + responses[0].renderer.render(responses[0]); + + expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); + expect(scriptTag.getAttribute('src')).to.equal('//js.spotx.tv/easi/v1/12345.js'); + expect(scriptTag.getAttribute('data-spotx_channel_id')).to.equal('12345'); + expect(scriptTag.getAttribute('data-spotx_vast_url')).to.equal('//search.spotxchange.com/ad/vast.html?key=cache123'); + expect(scriptTag.getAttribute('data-spotx_ad_unit')).to.equal('incontent'); + expect(scriptTag.getAttribute('data-spotx_collapse')).to.equal('0'); + expect(scriptTag.getAttribute('data-spotx_autoplay')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_blocked_autoplay_override_mode')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_video_slot_can_autoplay')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_digitrust_opt_out')).to.equal('1'); + expect(scriptTag.getAttribute('data-spotx_content_width')).to.equal('400'); + expect(scriptTag.getAttribute('data-spotx_content_height')).to.equal('300'); + window.document.getElementById.restore(); + }); + }); +}); diff --git a/test/spec/modules/staqAnalyticsAdapter_spec.js b/test/spec/modules/staqAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..33c85d11431 --- /dev/null +++ b/test/spec/modules/staqAnalyticsAdapter_spec.js @@ -0,0 +1,301 @@ +import analyticsAdapter, {ExpiringQueue, getUmtSource, storage} from 'modules/staqAnalyticsAdapter'; +import {expect} from 'chai'; +import adapterManager from 'src/adapterManager'; +import CONSTANTS from 'src/constants.json'; + +const events = require('../../../src/events'); + +const DIRECT = { + source: '(direct)', + medium: '(direct)', + campaign: '(direct)' +}; +const REFERRER = { + source: 'lander.com', + medium: '(referral)', + campaign: '(referral)', + content: '/lander.html' +}; +const GOOGLE_ORGANIC = { + source: 'google', + medium: '(organic)', + campaign: '(organic)' +}; +const CAMPAIGN = { + source: 'adkernel', + medium: 'email', + campaign: 'new_campaign', + c1: '1', + c2: '2', + c3: '3', + c4: '4', + c5: '5' + +}; +describe('', function () { + let sandbox; + + before(function () { + sandbox = sinon.sandbox.create(); + }); + + after(function () { + sandbox.restore(); + analyticsAdapter.disableAnalytics(); + }); + + describe('UTM source parser', function () { + let stubSetItem; + let stubGetItem; + + before(function () { + stubSetItem = sandbox.stub(storage, 'setItem'); + stubGetItem = sandbox.stub(storage, 'getItem'); + }); + + afterEach(function () { + sandbox.reset(); + }); + + it('should parse first direct visit as (direct)', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://example.com'); + expect(source).to.be.eql(DIRECT); + }); + + it('should parse visit from google as organic', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://example.com', 'https://www.google.com/search?q=pikachu'); + expect(source).to.be.eql(GOOGLE_ORGANIC); + }); + + it('should parse referral visit', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://example.com', 'http://lander.com/lander.html'); + expect(source).to.be.eql(REFERRER); + }); + + it('should parse referral visit from same domain as direct', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://lander.com/news.html', 'http://lander.com/lander.html'); + expect(source).to.be.eql(DIRECT); + }); + + it('should parse campaign visit', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://lander.com/index.html?utm_campaign=new_campaign&utm_source=adkernel&utm_medium=email&utm_c1=1&utm_c2=2&utm_c3=3&utm_c4=4&utm_c5=5'); + expect(source).to.be.eql(CAMPAIGN); + }); + }); + + describe('ExpiringQueue', function () { + let timer; + before(function () { + timer = sandbox.useFakeTimers(0); + }); + after(function () { + timer.restore(); + }); + + it('should notify after timeout period', (done) => { + let queue = new ExpiringQueue(() => { + let elements = queue.popAll(); + expect(elements).to.be.eql([1, 2, 3, 4]); + elements = queue.popAll(); + expect(elements).to.have.lengthOf(0); + expect(Date.now()).to.be.equal(200); + done(); + }, 100); + + queue.push(1); + setTimeout(() => { + queue.push([2, 3]); + timer.tick(50); + }, 50); + setTimeout(() => { + queue.push([4]); + timer.tick(100); + }, 100); + timer.tick(50); + }); + }); + + const REQUEST = { + bidderCode: 'AppNexus', + bidderName: 'AppNexus', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + bidderRequestId: '1a6fc81528d0f6', + bids: [{ + bidder: 'AppNexus', + params: {}, + adUnitCode: 'container-1', + transactionId: 'de90df62-7fd0-4fbc-8787-92d133a7dc06', + sizes: [[300, 250]], + bidId: '208750227436c1', + bidderRequestId: '1a6fc81528d0f6', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f' + }], + auctionStart: 1509369418387, + timeout: 3000, + start: 1509369418389 + }; + + const RESPONSE = { + bidderCode: 'AppNexus', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '208750227436c1', + mediaType: 'banner', + cpm: 0.015, + ad: '', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + responseTimestamp: 1509369418832, + requestTimestamp: 1509369418389, + bidder: 'AppNexus', + adUnitCode: 'container-1', + timeToRespond: 443, + size: '300x250' + }; + + const bidTimeoutArgsV1 = [{ + bidId: '2baa51527bd015', + bidderCode: 'AppNexus', + adUnitCode: 'container-1', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + }, + { + bidId: '6fe3b4c2c23092', + bidderCode: 'AppNexus', + adUnitCode: 'container-2', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + }]; + + describe('Analytics adapter', function () { + let ajaxStub; + let timer; + + before(function () { + ajaxStub = sandbox.stub(analyticsAdapter, 'ajaxCall'); + timer = sandbox.useFakeTimers(0); + }); + + beforeEach(function () { + sandbox.stub(events, 'getEvents').callsFake(() => { + return [] + }); + }); + + afterEach(function () { + events.getEvents.restore(); + }); + + it('should be configurable', function () { + adapterManager.registerAnalyticsAdapter({ + code: 'staq', + adapter: analyticsAdapter + }); + + adapterManager.enableAnalytics({ + provider: 'staq', + options: { + connId: 777, + queueTimeout: 1000, + url: 'http://localhost/prebid' + } + }); + + expect(analyticsAdapter.context).to.have.property('connectionId', 777); + }); + + it('should handle auction init event', function () { + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {config: {}, timeout: 3000}); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(1); + expect(ev[0]).to.be.eql({event: 'auctionInit', auctionId: undefined}); + }); + + it('should handle bid request event', function () { + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, REQUEST); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(2); + expect(ev[1]).to.be.eql({ + adUnitCode: 'container-1', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + event: 'bidRequested', + adapter: 'AppNexus', + bidderName: 'AppNexus' + }); + }); + + it('should handle bid response event', function () { + events.emit(CONSTANTS.EVENTS.BID_RESPONSE, RESPONSE); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(3); + expect(ev[2]).to.be.eql({ + adId: '208750227436c1', + event: 'bidResponse', + adapter: 'AppNexus', + bidderName: 'AppNexus', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + adUnitCode: 'container-1', + cpm: 0.015, + timeToRespond: 0.443, + height: 250, + width: 300, + bidWon: false, + }); + }); + + it('should handle timeouts properly', function() { + events.emit(CONSTANTS.EVENTS.BID_TIMEOUT, bidTimeoutArgsV1); + + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(5); // remember, we added 2 timeout events + expect(ev[3]).to.be.eql({ + adapter: 'AppNexus', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f', + bidderName: 'AppNexus', + event: 'adapterTimedOut' + }) + }); + + it('should handle winning bid', function () { + events.emit(CONSTANTS.EVENTS.BID_WON, RESPONSE); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(6); + expect(ev[5]).to.be.eql({ + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + adId: '208750227436c1', + event: 'bidWon', + adapter: 'AppNexus', + bidderName: 'AppNexus', + adUnitCode: 'container-1', + cpm: 0.015, + height: 250, + width: 300, + bidWon: true, + }); + }); + + it('should handle auction end event', function () { + timer.tick(447); + events.emit(CONSTANTS.EVENTS.AUCTION_END, RESPONSE); + let ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(0); + expect(ajaxStub.calledOnce).to.be.equal(true); + let firstCallArgs0 = ajaxStub.firstCall.args[0]; + ev = JSON.parse(firstCallArgs0); + // console.log('AUCTION END EVENT SHAPE ' + JSON.stringify(ev)); + const ev6 = ev[6]; + expect(ev6.connId).to.be.eql(777); + expect(ev6.auctionId).to.be.eql('5018eb39-f900-4370-b71e-3bb5b48d324f'); + expect(ev6.event).to.be.eql('auctionEnd'); + }); + }); +}); diff --git a/test/spec/modules/stvBidAdapter_spec.js b/test/spec/modules/stvBidAdapter_spec.js new file mode 100644 index 00000000000..72ea428304a --- /dev/null +++ b/test/spec/modules/stvBidAdapter_spec.js @@ -0,0 +1,138 @@ +import { expect } from 'chai'; +import { spec } from 'modules/stvBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const VADS_ENDPOINT_URL = 'https://ads.smartstream.tv/r/'; + +describe('stvAdapter', function () { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'stv', + 'params': { + 'placement': '6682', + 'pfilter': { + 'floorprice': 1000000 + }, + 'bcat': 'IAB2,IAB4', + 'dvt': 'desktop' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'someIncorrectParam': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [{ + 'bidder': 'stv', + 'params': { + 'source': 'vads', + 'placement': 'prer0-0%3D4137', + 'pfilter': { + 'min_duration': 1, + 'max_duration': 100, + 'min_bitrate': 32, + 'max_bitrate': 128, + }, + 'noskip': 1 + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + + let bidderRequest = { + refererInfo: { + referer: 'some_referrer.net' + } + } + + const request = spec.buildRequests(bidRequests, bidderRequest); + it('sends bid video request to our vads endpoint via GET', function () { + expect(request[0].method).to.equal('GET'); + let data = request[0].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); + expect(data).to.equal('_f=vast2&alternative=prebid_js&_ps=prer0-0%3D4137&srw=640&srh=480&idt=100&bid_id=30b31c1838de1e&pfilter%5Bmin_duration%5D=1&pfilter%5Bmax_duration%5D=100&pfilter%5Bmin_bitrate%5D=32&pfilter%5Bmax_bitrate%5D=128&noskip=1'); + }); + }); + + describe('interpretResponse', function () { + let vadsServerVideoResponse = { + 'body': { + 'cpm': 5000000, + 'crid': 100500, + 'width': '300', + 'height': '250', + 'vastXml': '{"reason":7001,"status":"accepted"}', + 'requestId': '220ed41385952a', + 'currency': 'EUR', + 'ttl': 60, + 'netRevenue': true, + 'zone': '6682' + } + }; + + let expectedResponse = [{ + requestId: '23beaa6af6cdde', + cpm: 0.5, + width: 0, + height: 0, + creativeId: 100500, + dealId: '', + currency: 'EUR', + netRevenue: true, + ttl: 300, + vastXml: '{"reason":7001,"status":"accepted"}', + mediaType: 'video' + }]; + + it('should get the correct vads video bid response by display ad', function () { + let bidRequest = [{ + 'method': 'GET', + 'url': VADS_ENDPOINT_URL, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + } + }, + 'data': { + 'bid_id': '30b31c1838de1e' + } + }]; + let result = spec.interpretResponse(vadsServerVideoResponse, bidRequest[0]); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles empty bid response', function () { + let response = { + body: {} + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js new file mode 100644 index 00000000000..45173b09953 --- /dev/null +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -0,0 +1,255 @@ +import { expect } from 'chai'; +import { spec } from 'modules/sublimeBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('Sublime Adapter', function() { + const adapter = newBidder(spec); + + describe('inherited functions', function() { + it('exists and is a function', function() { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function() { + let bid = { + bidder: 'sublime', + params: { + zoneId: 24549, + endpoint: '', + }, + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function() { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + let bidRequests = [ + { + bidder: 'sublime', + adUnitCode: 'sublime_code', + bidId: 'abc1234', + sizes: [[1800, 1000], [640, 300]], + requestId: 'xyz654', + params: { + zoneId: 123, + callbackName: 'false' + } + }, { + bidder: 'sublime', + adUnitCode: 'sublime_code_2', + bidId: 'abc1234_2', + sizes: [[1, 1]], + requestId: 'xyz654_2', + params: { + zoneId: 456, + } + } + ]; + + let bidderRequest = { + gdprConsent: { + consentString: 'EOHEIRCOUCOUIEHZIOEIU-TEST', + gdprApplies: true + }, + refererInfo: { + referer: 'https://example.com', + numIframes: 2, + } + }; + + let request = spec.buildRequests(bidRequests, bidderRequest); + + it('should have a post method', function() { + expect(request[0].method).to.equal('POST'); + expect(request[1].method).to.equal('POST'); + }); + + it('should contains a request id equals to the bid id', function() { + expect(request[0].data.requestId).to.equal(bidRequests[0].bidId); + expect(request[1].data.requestId).to.equal(bidRequests[1].bidId); + }); + + it('should have an url that contains bid keyword', function() { + expect(request[0].url).to.match(/bid/); + expect(request[1].url).to.match(/bid/); + }); + }); + + describe('buildRequests: default arguments', function() { + let bidRequests = [{ + bidder: 'sublime', + adUnitCode: 'sublime_code', + bidId: 'abc1234', + sizes: [[1800, 1000], [640, 300]], + requestId: 'xyz654', + params: { + zoneId: 123 + } + }]; + + let request = spec.buildRequests(bidRequests); + + it('should have an url that match the default endpoint', function() { + expect(request[0].url).to.equal('https://pbjs.sskzlabs.com/bid'); + }); + }); + + describe('interpretResponse', function() { + let serverResponse = { + 'request_id': '3db3773286ee59', + 'cpm': 0.5, + 'ad': '', + }; + + it('should get correct bid response', function() { + // Mock the fire method + top.window.sublime = { + analytics: { + fire: function() {} + } + }; + + let expectedResponse = [ + { + requestId: '', + cpm: 0.5, + width: 1800, + height: 1000, + creativeId: 1, + dealId: 1, + currency: 'USD', + netRevenue: true, + ttl: 600, + ad: '', + }, + ]; + let result = spec.interpretResponse({body: serverResponse}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('should get correct default size for 1x1', function() { + let serverResponse = { + 'requestId': 'xyz654_2', + 'cpm': 0.5, + 'ad': '', + }; + + let bidRequest = { + bidder: 'sublime', + adUnitCode: 'sublime_code_2', + bidId: 'abc1234_2', + data: { + w: 1, + h: 1, + }, + requestId: 'xyz654_2', + params: { + zoneId: 456, + } + }; + + let result = spec.interpretResponse({body: serverResponse}, bidRequest); + + let expectedResponse = { + requestId: 'xyz654_2', + cpm: 0.5, + width: 1, + height: 1, + creativeId: 1, + dealId: 1, + currency: 'EUR', + netRevenue: true, + ttl: 600, + ad: '', + }; + + expect(result[0]).to.deep.equal(expectedResponse); + }); + + it('should return bid empty response', function () { + let serverResponse = ''; + let bidRequest = {}; + + let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + + let expectedResponse = []; + + expect(result).to.deep.equal(expectedResponse); + }); + + it('should return bid with default value in response', function () { + let serverResponse = { + 'requestId': 'xyz654_2', + 'ad': '', + }; + + let bidRequest = { + bidder: 'sublime', + adUnitCode: 'sublime_code_2', + bidId: 'abc1234_2', + data: { + w: 1, + h: 1, + }, + requestId: 'xyz654_2', + params: { + zoneId: 456, + } + }; + + let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + + let expectedResponse = { + requestId: 'xyz654_2', + cpm: 0, + width: 1, + height: 1, + creativeId: 1, + dealId: 1, + currency: 'EUR', + netRevenue: true, + ttl: 600, + ad: '', + }; + + expect(result[0]).to.deep.equal(expectedResponse); + }); + + it('should return empty bid response because of timeout', function () { + let serverResponse = { + 'requestId': 'xyz654_2', + 'timeout': true, + 'ad': '', + }; + + let bidRequest = { + bidder: 'sublime', + adUnitCode: 'sublime_code_2', + bidId: 'abc1234_2', + data: { + w: 1, + h: 1, + }, + requestId: 'xyz654_2', + params: { + zoneId: 456, + } + }; + + let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + + let expectedResponse = []; + + expect(result).to.deep.equal(expectedResponse); + }); + }); +}); diff --git a/test/spec/modules/supply2BidAdapter_spec.js b/test/spec/modules/supply2BidAdapter_spec.js new file mode 100644 index 00000000000..8abbecd801c --- /dev/null +++ b/test/spec/modules/supply2BidAdapter_spec.js @@ -0,0 +1,332 @@ +import { expect } from 'chai'; +import { spec } from 'modules/supply2BidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('Supply2Adapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'supply2', + 'params': { + 'uid': '16' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'uid': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + function parseRequest(url) { + const res = {}; + url.split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } + let bidRequests = [ + { + 'bidder': 'supply2', + 'params': { + 'uid': '16' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'supply2', + 'params': { + 'uid': '16' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'supply2', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should attach valid params to the tag', function () { + const request = spec.buildRequests([bidRequests[0]]); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '16'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + }); + + it('auids must not be duplicated', function () { + const request = spec.buildRequests(bidRequests); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '16,17'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + }); + + it('if timeout is present, payload must have wtimeout parameter', function () { + const request = spec.buildRequests(bidRequests, {timeout: 2000}); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('wtimeout', '2000'); + }); + + it('pt parameter must be "gross" if params.priceType === "gross"', function () { + bidRequests[1].params.priceType = 'gross'; + const request = spec.buildRequests(bidRequests); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'gross'); + expect(payload).to.have.property('auids', '16,17'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + delete bidRequests[1].params.priceType; + }); + + it('pt parameter must be "net" or "gross"', function () { + bidRequests[1].params.priceType = 'some'; + const request = spec.buildRequests(bidRequests); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '16,17'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + delete bidRequests[1].params.priceType; + }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + + it('if gdprApplies is false gdpr_applies must be 0', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '0'); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + }); + + describe('interpretResponse', function () { + const responses = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 23, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 24, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 25, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
    test content 4
    ', 'h': 250, 'w': 300}], 'seat': '1'}, + undefined, + {'bid': [], 'seat': '1'}, + {'seat': '1'}, + ]; + + it('should get correct bid response', function () { + const bidRequests = [ + { + 'bidder': 'supply2', + 'params': { + 'uid': '23' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 23, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'supply2', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should get correct multi bid response', function () { + const bidRequests = [ + { + 'bidder': 'supply2', + 'params': { + 'uid': '23' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71a5b', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + }, + { + 'bidder': 'supply2', + 'params': { + 'uid': '24' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4dff80cc4ee346', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + }, + { + 'bidder': 'supply2', + 'params': { + 'uid': '23' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '5703af74d0472a', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '300bfeb0d71a5b', + 'cpm': 1.15, + 'creativeId': 23, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'supply2', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '5703af74d0472a', + 'cpm': 1.15, + 'creativeId': 23, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'supply2', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 24, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 2
    ', + 'bidderCode': 'supply2', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [responses[0], responses[1]]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('handles wrong and nobid responses', function () { + const bidRequests = [ + { + 'bidder': 'supply2', + 'params': { + 'uid': '25' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d7190gf', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + }, + { + 'bidder': 'supply2', + 'params': { + 'uid': '26' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71321', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + }, + { + 'bidder': 'supply2', + 'params': { + 'uid': '27' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '300bfeb0d7183bb', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + } + ]; + const request = spec.buildRequests(bidRequests); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + expect(result.length).to.equal(0); + }); + }); +}); diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js new file mode 100644 index 00000000000..d2f024181a4 --- /dev/null +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -0,0 +1,698 @@ +import { assert, expect } from 'chai'; +import { BANNER } from 'src/mediaTypes'; +import { spec } from 'modules/synacormediaBidAdapter'; + +describe('synacormediaBidAdapter ', function () { + describe('isBidRequestValid', function () { + let bid; + beforeEach(function () { + bid = { + sizes: [300, 250], + params: { + seatId: 'prebid', + placementId: '1234' + } + }; + }); + + it('should return true when params placementId and seatId are truthy', function () { + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when sizes are missing', function () { + delete bid.sizes; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + + it('should return false when the only size is unwanted', function () { + bid.sizes = [[1, 1]]; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + + it('should return false when seatId param is missing', function () { + delete bid.params.seatId; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + + it('should return false when placementId param is missing', function () { + delete bid.params.placementId; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + + it('should return false when params is missing or null', function () { + assert.isFalse(spec.isBidRequestValid({ params: null })); + assert.isFalse(spec.isBidRequestValid({})); + assert.isFalse(spec.isBidRequestValid(null)); + }); + }); + + describe('impression type', function() { + let nonVideoReq = { + bidId: '9876abcd', + sizes: [[300, 250], [300, 600]], + params: { + seatId: 'prebid', + placementId: '1234', + bidfloor: '0.50' + } + }; + + let bannerReq = { + bidId: '9876abcd', + sizes: [[300, 250], [300, 600]], + params: { + seatId: 'prebid', + placementId: '1234', + bidfloor: '0.50' + }, + mediaTypes: { + banner: { + h: 600, + pos: 0, + w: 300, + } + }, + }; + + let videoReq = { + bidId: '9876abcd', + sizes: [[640, 480]], + params: { + seatId: 'prebid', + placementId: '1234', + bidfloor: '0.50' + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [ + 640, + 480 + ] + ] + } + }, + }; + it('should return correct impression type video/banner', function() { + assert.isFalse(spec.isVideoBid(nonVideoReq)); + assert.isFalse(spec.isVideoBid(bannerReq)); + assert.isTrue(spec.isVideoBid(videoReq)); + }); + }); + describe('buildRequests', function () { + let validBidRequestVideo = { + bidder: 'synacormedia', + params: { + seatId: 'prebid', + placementId: '1234', + video: { + minduration: 30 + } + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [[ 640, 480 ]] + } + }, + adUnitCode: 'video1', + transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', + sizes: [[ 640, 480 ]], + bidId: '2624fabbb078e8', + bidderRequestId: '117954d20d7c9c', + auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', + src: 'client', + bidRequestsCount: 1 + }; + + let bidderRequestVideo = { + bidderCode: 'synacormedia', + auctionId: 'VideoAuctionId124', + bidderRequestId: '117954d20d7c9c', + auctionStart: 1553624929697, + timeout: 700, + refererInfo: { + referer: 'http://localhost:9999/test/pages/video.html?pbjs_debug=true', + reachedTop: true, + numIframes: 0, + stack: [ 'http://localhost:9999/test/pages/video.html?pbjs_debug=true' ] + }, + start: 1553624929700 + }; + + bidderRequestVideo.bids = validBidRequestVideo; + let expectedDataVideo1 = { + id: 'v2624fabbb078e8-640x480', + tagid: '1234', + video: { + w: 640, + h: 480, + pos: 0, + minduration: 30 + } + }; + + let validBidRequest = { + bidId: '9876abcd', + sizes: [[300, 250], [300, 600]], + params: { + seatId: 'prebid', + placementId: '1234', + bidfloor: '0.50' + } + }; + + let bidderRequest = { + auctionId: 'xyz123', + refererInfo: { + referer: 'https://test.com/foo/bar' + } + }; + + let expectedDataImp1 = { + banner: { + h: 250, + pos: 0, + w: 300, + }, + id: 'b9876abcd-300x250', + tagid: '1234', + bidfloor: 0.5 + }; + let expectedDataImp2 = { + banner: { + h: 600, + pos: 0, + w: 300, + }, + id: 'b9876abcd-300x600', + tagid: '1234', + bidfloor: 0.5 + }; + + it('should return valid request when valid bids are used', function () { + // banner test + let req = spec.buildRequests([validBidRequest], bidderRequest); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([expectedDataImp1, expectedDataImp2]); + + // video test + let reqVideo = spec.buildRequests([validBidRequestVideo], bidderRequestVideo); + expect(reqVideo).be.an('object'); + expect(reqVideo).to.have.property('method', 'POST'); + expect(reqVideo).to.have.property('url'); + expect(reqVideo.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(reqVideo.data).to.exist.and.to.be.an('object'); + expect(reqVideo.data.id).to.equal('VideoAuctionId124'); + expect(reqVideo.data.imp).to.eql([expectedDataVideo1]); + }); + + it('should return multiple bids when multiple valid requests with the same seatId are used', function () { + let secondBidRequest = { + bidId: 'foobar', + sizes: [[300, 600]], + params: { + seatId: validBidRequest.params.seatId, + placementId: '5678', + bidfloor: '0.50' + } + }; + let req = spec.buildRequests([validBidRequest, secondBidRequest], bidderRequest); + expect(req).to.exist.and.be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([expectedDataImp1, expectedDataImp2, { + banner: { + h: 600, + pos: 0, + w: 300, + }, + id: 'bfoobar-300x600', + tagid: '5678', + bidfloor: 0.5 + }]); + }); + + it('should return only first bid when different seatIds are used', function () { + let mismatchedSeatBidRequest = { + bidId: 'foobar', + sizes: [[300, 250]], + params: { + seatId: 'somethingelse', + placementId: '5678', + bidfloor: '0.50' + } + }; + let req = spec.buildRequests([mismatchedSeatBidRequest, validBidRequest], bidderRequest); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/somethingelse?'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([ + { + banner: { + h: 250, + pos: 0, + w: 300, + }, + id: 'bfoobar-300x250', + tagid: '5678', + bidfloor: 0.5 + } + ]); + }); + + it('should not use bidfloor when the value is not a number', function () { + let badFloorBidRequest = { + bidId: '9876abcd', + sizes: [[300, 250]], + params: { + seatId: 'prebid', + placementId: '1234', + bidfloor: 'abcd' + } + }; + let req = spec.buildRequests([badFloorBidRequest], bidderRequest); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([ + { + banner: { + h: 250, + pos: 0, + w: 300, + }, + id: 'b9876abcd-300x250', + tagid: '1234', + } + ]); + }); + + it('should not use bidfloor when there is no value', function () { + let badFloorBidRequest = { + bidId: '9876abcd', + sizes: [[300, 250]], + params: { + seatId: 'prebid', + placementId: '1234' + } + }; + let req = spec.buildRequests([badFloorBidRequest], bidderRequest); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([ + { + banner: { + h: 250, + pos: 0, + w: 300, + }, + id: 'b9876abcd-300x250', + tagid: '1234', + } + ]); + }); + + it('should use the pos given by the bid request', function () { + let newPosBidRequest = { + bidId: '9876abcd', + sizes: [[300, 250]], + params: { + seatId: 'prebid', + placementId: '1234', + pos: 1 + } + }; + let req = spec.buildRequests([newPosBidRequest], bidderRequest); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([ + { + banner: { + h: 250, + w: 300, + pos: 1, + }, + id: 'b9876abcd-300x250', + tagid: '1234' + } + ]); + }); + + it('should use the default pos if none in bid request', function () { + let newPosBidRequest = { + bidId: '9876abcd', + sizes: [[300, 250]], + params: { + seatId: 'prebid', + placementId: '1234', + } + }; + let req = spec.buildRequests([newPosBidRequest], bidderRequest); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([ + { + banner: { + h: 250, + w: 300, + pos: 0, + }, + id: 'b9876abcd-300x250', + tagid: '1234' + } + ]); + }); + it('should not return a request when no valid bid request used', function () { + expect(spec.buildRequests([], bidderRequest)).to.be.undefined; + expect(spec.buildRequests([validBidRequest], null)).to.be.undefined; + }); + + it('should return empty impression when there is no valid sizes in bidrequest', function() { + let validBidReqWithoutSize = { + bidId: '9876abcd', + sizes: [], + params: { + seatId: 'prebid', + placementId: '1234', + bidfloor: '0.50' + } + }; + + let validBidReqInvalidSize = { + bidId: '9876abcd', + sizes: [[300]], + params: { + seatId: 'prebid', + placementId: '1234', + bidfloor: '0.50' + } + }; + + let bidderRequest = { + auctionId: 'xyz123', + refererInfo: { + referer: 'https://test.com/foo/bar' + } + }; + + let req = spec.buildRequests([validBidReqWithoutSize], bidderRequest); + assert.isUndefined(req); + req = spec.buildRequests([validBidReqInvalidSize], bidderRequest); + assert.isUndefined(req); + }); + it('should use all the video params in the impression request', function () { + let validBidRequestVideo = { + bidder: 'synacormedia', + params: { + seatId: 'prebid', + placementId: '1234', + video: { + minduration: 30, + maxduration: 45, + startdelay: 1, + linearity: 1, + placement: 1, + mimes: ['video/mp4'], + protocols: [1], + api: 1 + } + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [[ 640, 480 ]] + } + }, + adUnitCode: 'video1', + transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', + sizes: [[ 640, 480 ]], + bidId: '2624fabbb078e8', + bidderRequestId: '117954d20d7c9c', + auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', + src: 'client', + bidRequestsCount: 1 + }; + + let req = spec.buildRequests([validBidRequestVideo], bidderRequest); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([ + { + video: { + h: 480, + pos: 0, + w: 640, + minduration: 30, + maxduration: 45, + startdelay: 1, + linearity: 1, + placement: 1, + mimes: ['video/mp4'], + protocols: [1], + api: 1 + }, + id: 'v2624fabbb078e8-640x480', + tagid: '1234', + } + ]); + }); + it('should move any video params in the mediaTypes object to params.video object', function () { + let validBidRequestVideo = { + bidder: 'synacormedia', + params: { + seatId: 'prebid', + placementId: '1234', + video: { + minduration: 30, + maxduration: 45, + protocols: [1], + api: 1 + } + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [[ 640, 480 ]], + startdelay: 1, + linearity: 1, + placement: 1, + mimes: ['video/mp4'] + } + }, + adUnitCode: 'video1', + transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', + sizes: [[ 640, 480 ]], + bidId: '2624fabbb078e8', + bidderRequestId: '117954d20d7c9c', + auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', + src: 'client', + bidRequestsCount: 1 + }; + + let req = spec.buildRequests([validBidRequestVideo], bidderRequest); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.imp).to.eql([ + { + video: { + h: 480, + pos: 0, + w: 640, + minduration: 30, + maxduration: 45, + startdelay: 1, + linearity: 1, + placement: 1, + mimes: ['video/mp4'], + protocols: [1], + api: 1 + }, + id: 'v2624fabbb078e8-640x480', + tagid: '1234', + } + ]); + }); + }); + + describe('interpretResponse', function () { + let bidResponse = { + id: '10865933907263896~9998~0', + impid: 'b9876abcd-300x250', + price: 0.13, + crid: '1022-250', + adm: '', + nurl: '//uat-net.technoratimedia.com/openrtb/tags?ID=k5JkFVQ1RJT05fSU1QX0lEPXYyZjczN&AUCTION_PRICE=${AUCTION_PRICE}' + }; + let bidResponse2 = { + id: '10865933907263800~9999~0', + impid: 'b9876abcd-300x600', + price: 1.99, + crid: '9993-013', + adm: '', + nurl: '//uat-net.technoratimedia.com/openrtb/tags?ID=OTk5OX4wJkFVQ1RJT05fU0VBVF9JR&AUCTION_PRICE=${AUCTION_PRICE}' + }; + + let serverResponse; + beforeEach(function() { + serverResponse = { + body: { + id: 'abc123', + seatbid: [{ + seat: '9998', + bid: [], + }] + } + }; + }); + + it('should return 1 video bid when 1 bid is in the video response', function () { + let serverRespVideo = { + body: { + id: 'abcd1234', + seatbid: [ + { + bid: [ + { + id: '11339128001692337~9999~0', + impid: 'v2da7322b2df61f-640x480', + price: 0.45, + nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', + adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', + adomain: [ 'psacentral.org' ], + cid: 'bidder-crid', + crid: 'bidder-cid', + cat: [] + } + ], + seat: '9999' + } + ] + } + }; + + // serverResponse.body.seatbid[0].bid.push(bidResponse); + let resp = spec.interpretResponse(serverRespVideo); + expect(resp).to.be.an('array').to.have.lengthOf(1); + expect(resp[0]).to.eql({ + requestId: '2da7322b2df61f', + adId: '11339128001692337-9999-0', + cpm: 0.45, + width: 640, + height: 480, + creativeId: '9999_bidder-cid', + currency: 'USD', + netRevenue: true, + mediaType: 'video', + ad: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45\n\n\n', + ttl: 60, + videoCacheKey: 'QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk', + vastUrl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45' + }); + }); + + it('should return 1 bid when 1 bid is in the response', function () { + serverResponse.body.seatbid[0].bid.push(bidResponse); + let resp = spec.interpretResponse(serverResponse); + expect(resp).to.be.an('array').to.have.lengthOf(1); + expect(resp[0]).to.eql({ + requestId: '9876abcd', + adId: '10865933907263896-9998-0', + cpm: 0.13, + width: 300, + height: 250, + creativeId: '9998_1022-250', + currency: 'USD', + netRevenue: true, + mediaType: BANNER, + ad: '', + ttl: 60 + }); + }); + + it('should return 2 bids when 2 bids are in the response', function () { + serverResponse.body.seatbid[0].bid.push(bidResponse); + serverResponse.body.seatbid.push({ + seat: '9999', + bid: [bidResponse2], + }); + let resp = spec.interpretResponse(serverResponse); + expect(resp).to.be.an('array').to.have.lengthOf(2); + expect(resp[0]).to.eql({ + requestId: '9876abcd', + adId: '10865933907263896-9998-0', + cpm: 0.13, + width: 300, + height: 250, + creativeId: '9998_1022-250', + currency: 'USD', + netRevenue: true, + mediaType: BANNER, + ad: '', + ttl: 60 + }); + + expect(resp[1]).to.eql({ + requestId: '9876abcd', + adId: '10865933907263800-9999-0', + cpm: 1.99, + width: 300, + height: 600, + creativeId: '9999_9993-013', + currency: 'USD', + netRevenue: true, + mediaType: BANNER, + ad: '', + ttl: 60 + }); + }); + + it('should not return a bid when no bid is in the response', function () { + let resp = spec.interpretResponse(serverResponse); + expect(resp).to.be.an('array').that.is.empty; + }); + + it('should not return a bid when there is no response body', function () { + expect(spec.interpretResponse({ body: null })).to.not.exist; + expect(spec.interpretResponse({ body: 'some error text' })).to.not.exist; + }); + }); + describe('getUserSyncs', function () { + it('should return a usersync when iframes is enabled', function () { + let usersyncs = spec.getUserSyncs({ + iframeEnabled: true + }, null); + expect(usersyncs).to.be.an('array').that.is.not.empty; + expect(usersyncs[0]).to.have.property('type', 'iframe'); + expect(usersyncs[0]).to.have.property('url'); + expect(usersyncs[0].url).to.contain('//ad-cdn.technoratimedia.com/html/usersync.html'); + }); + + it('should not return a usersync when iframes are not enabled', function () { + let usersyncs = spec.getUserSyncs({ + pixelEnabled: true + }, null); + expect(usersyncs).to.be.an('array').that.is.empty; + }); + }); +}); diff --git a/test/spec/modules/taphypeBidAdapter_spec.js b/test/spec/modules/taphypeBidAdapter_spec.js new file mode 100644 index 00000000000..2fcdd964520 --- /dev/null +++ b/test/spec/modules/taphypeBidAdapter_spec.js @@ -0,0 +1,56 @@ +import { expect } from 'chai'; +import { spec } from 'modules/taphypeBidAdapter'; + +describe('taphypeBidAdapterTests', function () { + it('validate_pub_params', function () { + expect(spec.isBidRequestValid({ + bidder: 'taphype', + params: { + placementId: 12345 + } + })).to.equal(true); + }); + + it('validate_generated_params', function () { + let bidRequestData = [{ + bidId: 'bid12345', + bidder: 'taphype', + params: { + placementId: 12345 + }, + sizes: [[300, 250]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = request[0].data; + + expect(req_data.bidId).to.equal('bid12345'); + }); + + it('validate_response_params', function () { + let bidRequestData = { + data: { + bidId: 'bid12345' + } + }; + + let serverResponse = { + body: { + price: 1.23, + ad: '', + size: '300,250' + } + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(1.23); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal('300'); + expect(bid.height).to.equal('250'); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.ad).to.equal(''); + }); +}); diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js new file mode 100644 index 00000000000..af1c7a9c01b --- /dev/null +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -0,0 +1,394 @@ +import {expect} from 'chai'; +import {spec} from 'modules/teadsBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//a.teads.tv/hb/bid-request'; +const AD_SCRIPT = '"'; + +describe('teadsBidAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function() { + let bid = { + 'bidder': 'teads', + 'params': { + 'placementId': 10433394, + 'pageId': 1234 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee' + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when pageId is not valid (letters)', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 1234, + 'pageId': 'ABCD' + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when placementId is not valid (letters)', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 'FCP', + 'pageId': 1234 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when placementId < 0 or pageId < 0', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': -1, + 'pageId': -1 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function() { + let bid = Object.assign({}, bid); + delete bid.params; + + bid.params = { + 'placementId': 0 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + let bidRequests = [ + { + 'bidder': 'teads', + 'params': { + 'placementId': 10433394, + 'pageId': 1234 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee', + 'deviceWidth': 1680 + } + ]; + + let bidderResquestDefault = { + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000 + }; + + it('sends bid request to ENDPOINT via POST', function() { + const request = spec.buildRequests(bidRequests, bidderResquestDefault); + + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should send GDPR to endpoint', function() { + let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; + let bidderRequest = { + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true, + 'vendorData': { + 'hasGlobalConsent': false + } + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_iab).to.exist; + expect(payload.gdpr_iab.consent).to.equal(consentString); + expect(payload.gdpr_iab.status).to.equal(12); + }); + + it('should add referer info to payload', function () { + const bidRequest = Object.assign({}, bidRequests[0]) + const bidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2 + } + } + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.referrer).to.exist; + expect(payload.referrer).to.deep.equal('http://example.com/page.html') + }); + + it('should send GDPR to endpoint with 11 status', function() { + let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; + let bidderRequest = { + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true, + 'vendorData': { + 'hasGlobalScope': true + } + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_iab).to.exist; + expect(payload.gdpr_iab.consent).to.equal(consentString); + expect(payload.gdpr_iab.status).to.equal(11); + }); + + it('should send GDPR to endpoint with 22 status', function() { + let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; + let bidderRequest = { + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + 'consentString': undefined, + 'gdprApplies': undefined, + 'vendorData': undefined + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_iab).to.exist; + expect(payload.gdpr_iab.consent).to.equal(''); + expect(payload.gdpr_iab.status).to.equal(22); + }); + + it('should send GDPR to endpoint with 0 status', function() { + let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; + let bidderRequest = { + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': false, + 'vendorData': { + 'hasGlobalScope': false + } + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_iab).to.exist; + expect(payload.gdpr_iab.consent).to.equal(consentString); + expect(payload.gdpr_iab.status).to.equal(0); + }); + + it('should use good mediaTypes video playerSizes', function() { + const mediaTypesPlayerSize = { + 'mediaTypes': { + 'video': { + 'playerSize': [32, 34] + } + } + }; + checkMediaTypesSizes(mediaTypesPlayerSize, '32x34') + }); + + it('should use good mediaTypes video sizes', function() { + const mediaTypesVideoSizes = { + 'mediaTypes': { + 'video': { + 'sizes': [12, 14] + } + } + }; + checkMediaTypesSizes(mediaTypesVideoSizes, '12x14') + }); + + it('should use good mediaTypes banner sizes', function() { + const mediaTypesBannerSize = { + 'mediaTypes': { + 'banner': { + 'sizes': [46, 48] + } + } + }; + checkMediaTypesSizes(mediaTypesBannerSize, '46x48') + }); + + it('should use good mediaTypes for both video and banner sizes', function() { + const hybridMediaTypes = { + 'mediaTypes': { + 'banner': { + 'sizes': [46, 48] + }, + 'video': { + 'sizes': [[50, 34], [45, 45]] + } + } + }; + checkMediaTypesSizes(hybridMediaTypes, ['46x48', '50x34', '45x45']) + }); + + function checkMediaTypesSizes(mediaTypes, expectedSizes) { + const bidRequestWithBannerSizes = Object.assign(bidRequests[0], mediaTypes); + const requestWithBannerSizes = spec.buildRequests([bidRequestWithBannerSizes], bidderResquestDefault); + const payloadWithBannerSizes = JSON.parse(requestWithBannerSizes.data); + + return payloadWithBannerSizes.data.forEach(bid => { + if (Array.isArray(expectedSizes)) { + expect(JSON.stringify(bid.sizes)).to.equal(JSON.stringify(expectedSizes)); + } else { + expect(bid.sizes[0]).to.equal(expectedSizes); + } + }); + } + }); + + describe('interpretResponse', function() { + let bids = { + 'body': { + 'responses': [{ + 'ad': AD_SCRIPT, + 'cpm': 0.5, + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'bidId': '3ede2a3fa0db94', + 'ttl': 360, + 'width': 300, + 'creativeId': 'er2ee', + 'placementId': 34 + }] + } + }; + + it('should get correct bid response', function() { + let expectedResponse = { + 'cpm': 0.5, + 'width': 300, + 'height': 250, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + 'ad': AD_SCRIPT, + 'requestId': '3ede2a3fa0db94', + 'creativeId': 'er2ee', + 'placementId': 34 + }; + + let result = spec.interpretResponse(bids); + expect(result[0]).to.deep.equal(expectedResponse); + }); + + it('handles nobid responses', function() { + let bids = { + 'body': { + 'responses': [] + } + }; + + let result = spec.interpretResponse(bids); + expect(result.length).to.equal(0); + }); + }); + + it('should call userSync with good params', function() { + let bids = [{ + 'body': { + 'responses': [{ + 'ad': '', + 'adomain': [''], + 'cid': '1', + 'crid': '700', + 'w': 300, + 'h': 250 + }]}], + 'bidid': 'bidid', + 'cur': 'USD' + }, + 'headers': {} + }; + it('required keys', function () { + const result = spec.interpretResponse(validServerResponse, validBidRequest); + + let requiredKeys = [ + 'requestId', + 'creativeId', + 'adId', + 'cpm', + 'width', + 'height', + 'currency', + 'netRevenue', + 'ttl', + 'ad' + ]; + + let resultKeys = Object.keys(result[0]); + requiredKeys.forEach(function(key) { + expect(resultKeys.indexOf(key) !== -1).to.equal(true); + }); + }) + }); + + describe('getUserSyncs', function () { + it('check empty response getUserSyncs', function () { + const result = spec.getUserSyncs('', ''); + expect(result).to.deep.equal([]); + }); + }); +}); diff --git a/test/spec/modules/topRTBBidAdapter_spec.js b/test/spec/modules/topRTBBidAdapter_spec.js new file mode 100644 index 00000000000..83e5de3e4b9 --- /dev/null +++ b/test/spec/modules/topRTBBidAdapter_spec.js @@ -0,0 +1,67 @@ +import { expect } from 'chai'; +import { spec } from 'modules/topRTBBidAdapter'; + +describe('topRTBBidAdapterTests', function () { + it('validate_pub_params', function () { + expect(spec.isBidRequestValid({ + bidder: 'topRTB', + params: { + adUnitId: 'c5c06f77430c4c33814a0577cb4cc978' + }, + adName: 'banner' + })); + }); + + it('validate_generated_params', function () { + let bidRequestData = [{ + bidId: 'bid12345', + bidder: 'topRTB', + adName: 'banner', + adType: '{"banner":{"sizes":[[]]}}', + params: { + adUnitId: 'c5c06f77430c4c33814a0577cb4cc978' + }, + sizes: [[728, 90]] + }]; + + let request = spec.buildRequests(bidRequestData); + const current_url = request.url; + const search_params = current_url.searchParams; + }); + + it('validate_response_params', function () { + let bidRequestData = { + data: { + bidId: 'bid12345' + } + }; + + let serverResponse = { + body: [{ + 'cpm': 1, + 'mediadata': "Banner 728x90", + 'width': 728, + 'currency': 'USD', + 'id': 'cd95dffec6b645afbc4e5aa9f68f2ff3', + 'type': 'RICHMEDIA', + 'ttl': 4000, + 'bidId': 'bid12345', + 'status': 'success', + 'height': 90}], + 'adName': 'banner', + 'vastXml': '', + 'mediaType': 'banner', + 'tracking': 'https://ssp.toprtb.com/ssp/tracking?F0cloTiKIw%2BjZ2UNDvlKGn5%2FWoAO9cnlAUDm6gFBM8bImY2fKo%2BMTvI0XvXzFTZSb5v8o4EUbPId9hckptTqA4QPaWvpVYCRKRZceXNa4kjtvfm4j2e%2FcRKgkns2goHXi7IZC0sBIbE77WWg%2BPBYv%2BCu84H%2FSH69mi%2FDaWcQlfaEOdkaJdstJEkaZtkgWnFnS7aagte%2BfdEbOqcTxq5hzj%2BZ4NZbwgReuWTQZbfrMWjkXFbn%2B35vZuI319o6XH9n9fKLS4xp8zstXfQT2oSgjw1NmrwqRKf1efB1UaWlS1TbkSqxZ7Kcy7nJvAZrDk0tzcSeIxe4VfHpwgPPs%2BueUeGwz3o7OCh7H1sCmogSrmJFB9JTeXudFjC13iANAtu4SvG9bGIbiJxS%2BNfkjy2mLFm8kSIcIobjNkMEcUAwmoqJNRndwb66a3Iovk2NTo0Ly%2FV7Y5ECPcS5%2FPBrIEOuQXS5SNUPRWKoklX5nexHtOc%3D', + 'impression': 'https://ssp.toprtb.com/ssp/impression?id=64f29f7b226249f19925a680a506b32d' + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(1); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal(728); + expect(bid.height).to.equal(90); + expect(bid.requestId).to.equal('bid12345'); + }); +}); diff --git a/test/spec/modules/trafficrootsBidAdapter_spec.js b/test/spec/modules/trafficrootsBidAdapter_spec.js new file mode 100644 index 00000000000..54c102d33ef --- /dev/null +++ b/test/spec/modules/trafficrootsBidAdapter_spec.js @@ -0,0 +1,149 @@ +import { expect } from 'chai'; +import { spec } from 'modules/trafficrootsBidAdapter'; + +describe('trafficrootsAdapterTests', () => { + describe('bidRequestValidity', () => { + it('bidRequest with zoneId and deliveryUrl params', () => { + expect(spec.isBidRequestValid({ + bidder: 'trafficroots', + params: { + zoneId: 'aa0444af31', + deliveryUrl: 'https://service.trafficroosts.com/prebid' + } + })).to.equal(true); + }); + + it('bidRequest with only zoneId', () => { + expect(spec.isBidRequestValid({ + bidder: 'trafficroots', + params: { + zoneId: '8f527a4835' + } + })).to.equal(true); + }); + + it('bidRequest with only deliveryUrl', () => { + expect(spec.isBidRequestValid({ + bidder: 'trafficroots', + params: { + deliveryUrl: 'https://service.trafficroosts.com/prebid' + } + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'trafficroots', + 'bidId': '29fa5c08928bde', + 'params': { + 'zoneId': 'aa0444af31', + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }, { + 'bidder': 'trafficroots', + 'bidId': '29fa5c08928bde', + 'params': { + 'zoneId': '8f527a4835', + 'deliveryUrl': 'https://service.trafficroosts.com/prebid' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + it('bidRequest method', () => { + const request = spec.buildRequests(bidRequests); + expect(request.method).to.equal('GET'); + }); + + it('bidRequest url', () => { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.match(new RegExp(`${bidRequests[1].params.deliveryUrl}`)); + }); + + it('bidRequest data', () => { + const request = spec.buildRequests(bidRequests); + expect(request.data).to.exist; + }); + + it('bidRequest zoneIds', () => { + const request = spec.buildRequests(bidRequests); + expect(request.data.zoneId).to.equal('aa0444af31;8f527a4835'); + }); + + it('bidRequest gdpr consent', () => { + const consentString = 'consentString'; + const bidderRequest = { + bidderCode: 'trafficroots', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + timeout: 3000, + gdprConsent: { + consentString: consentString, + gdprApplies: true + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.gdpr).to.exist; + expect(request.data.gdpr.applies).to.exist.and.to.be.true; + expect(request.data.gdpr.consent).to.exist.and.to.equal(consentString); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = [{ + 'bidder': 'trafficroots', + 'bidId': '29fa5c08928bde', + 'params': { + 'zoneId': 'aa0444af31', + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + const bidResponse = { + body: [{ + 'id': 'div-gpt-ad-1460505748561-0', + 'ad': 'test ad', + 'width': 320, + 'height': 250, + 'cpm': 5.2 + }], + headers: {} + }; + + it('required keys', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + let requiredKeys = [ + 'requestId', + 'creativeId', + 'adId', + 'cpm', + 'width', + 'height', + 'currency', + 'netRevenue', + 'ttl', + 'ad' + ]; + + let resultKeys = Object.keys(result[0]); + resultKeys.forEach(function(key) { + expect(requiredKeys.indexOf(key) !== -1).to.equal(true); + }); + }) + }); +}); diff --git a/test/spec/modules/trionBidAdapter_spec.js b/test/spec/modules/trionBidAdapter_spec.js index 559122a2772..805ae70a339 100644 --- a/test/spec/modules/trionBidAdapter_spec.js +++ b/test/spec/modules/trionBidAdapter_spec.js @@ -35,22 +35,24 @@ const TRION_BID_RESPONSE = { }; -describe('Trion adapter tests', () => { +describe('Trion adapter tests', function () { let adapter; - beforeEach(() => { + beforeEach(function () { // adapter = trionAdapter.createNew(); sinon.stub(document.body, 'appendChild'); }); - afterEach(() => document.body.appendChild.restore()); + afterEach(function () { + document.body.appendChild.restore(); + }); - describe('isBidRequestValid', () => { - it('should return true with correct params', () => { + describe('isBidRequestValid', function () { + it('should return true with correct params', function () { expect(spec.isBidRequestValid(TRION_BID)).to.equal(true); }); - it('should return false when params are missing', () => { + it('should return false when params are missing', function () { TRION_BID.params = {}; expect(spec.isBidRequestValid(TRION_BID)).to.equal(false); @@ -60,7 +62,7 @@ describe('Trion adapter tests', () => { }; }); - it('should return false when pubId is missing', () => { + it('should return false when pubId is missing', function () { TRION_BID.params = { sectionId: '2' }; @@ -72,7 +74,7 @@ describe('Trion adapter tests', () => { }; }); - it('should return false when sectionId is missing', () => { + it('should return false when sectionId is missing', function () { TRION_BID.params = { pubId: '1' }; @@ -85,20 +87,20 @@ describe('Trion adapter tests', () => { }); }); - describe('buildRequests', () => { - it('should return bids requests with empty params', () => { + describe('buildRequests', function () { + it('should return bids requests with empty params', function () { let bidRequests = spec.buildRequests([]); expect(bidRequests.length).to.equal(0); }); - it('should include the base bidrequest url', () => { + it('should include the base bidrequest url', function () { let bidRequests = spec.buildRequests(TRION_BID_REQUEST); let bidUrl = bidRequests[0].url; expect(bidUrl).to.include(BID_REQUEST_BASE_URL); }); - it('should call buildRequests with the correct required params', () => { + it('should call buildRequests with the correct required params', function () { let bidRequests = spec.buildRequests(TRION_BID_REQUEST); let bidUrlParams = bidRequests[0].data; @@ -107,7 +109,7 @@ describe('Trion adapter tests', () => { expect(bidUrlParams).to.include('sizes=300x250,300x600'); }); - it('should call buildRequests with the correct optional params', () => { + it('should call buildRequests with the correct optional params', function () { let params = TRION_BID_REQUEST[0].params; params.re = 1; let bidRequests = spec.buildRequests(TRION_BID_REQUEST); @@ -119,13 +121,13 @@ describe('Trion adapter tests', () => { }); }); - describe('interpretResponse', () => { - it('when there is no response do not bid', () => { + describe('interpretResponse', function () { + it('when there is no response do not bid', function () { let response = spec.interpretResponse(null, {bidRequest: TRION_BID}); expect(response).to.deep.equal([]); }); - it('when place bid is returned as false', () => { + it('when place bid is returned as false', function () { TRION_BID_RESPONSE.result.placeBid = false; let response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); @@ -134,21 +136,21 @@ describe('Trion adapter tests', () => { TRION_BID_RESPONSE.result.placeBid = true; }); - it('when no cpm is in the response', () => { + it('when no cpm is in the response', function () { TRION_BID_RESPONSE.result.cpm = 0; let response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); expect(response).to.deep.equal([]); TRION_BID_RESPONSE.result.cpm = 1; }); - it('when no ad is in the response', () => { + it('when no ad is in the response', function () { TRION_BID_RESPONSE.result.ad = null; let response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); expect(response).to.deep.equal([]); TRION_BID_RESPONSE.result.ad = 'test'; }); - it('height and width are appropriately set', () => { + it('height and width are appropriately set', function () { let bidWidth = '1'; let bidHeight = '2'; TRION_BID_RESPONSE.result.width = bidWidth; @@ -160,7 +162,7 @@ describe('Trion adapter tests', () => { TRION_BID_RESPONSE.result.height = '250'; }); - it('cpm is properly set and transformed to cents', () => { + it('cpm is properly set and transformed to cents', function () { let bidCpm = 2; TRION_BID_RESPONSE.result.cpm = bidCpm * 100; let response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); @@ -169,15 +171,15 @@ describe('Trion adapter tests', () => { }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs', function () { const USER_SYNC_URL = 'https://in-appadvertising.com/api/userSync.html'; const BASE_KEY = '_trion_'; - beforeEach(() => { + beforeEach(function () { delete window.TR_INT_T; }); - it('trion int is included in bid url', () => { + it('trion int is included in bid url', function () { window.TR_INT_T = 'test_user_sync'; let userTag = encodeURIComponent(window.TR_INT_T); let bidRequests = spec.buildRequests(TRION_BID_REQUEST); @@ -186,7 +188,7 @@ describe('Trion adapter tests', () => { expect(bidUrlParams).to.include(userTag); }); - it('should register trion user script', () => { + it('should register trion user script', function () { let syncs = spec.getUserSyncs({iframeEnabled: true}); let url = utils.getTopWindowUrl(); let pubId = 1; @@ -195,7 +197,7 @@ describe('Trion adapter tests', () => { expect(syncs[0]).to.deep.equal({type: 'iframe', url: USER_SYNC_URL + syncString}); }); - it('should except posted messages from user sync script', () => { + it('should except posted messages from user sync script', function () { let testId = 'testId'; let message = BASE_KEY + 'userId=' + testId; setStorageData(BASE_KEY + 'int_t', null); @@ -204,7 +206,7 @@ describe('Trion adapter tests', () => { expect(newKey).to.equal(testId); }); - it('should not try to post messages not from trion', () => { + it('should not try to post messages not from trion', function () { let testId = 'testId'; let badId = 'badId'; let message = 'Not Trion: userId=' + testId; diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js new file mode 100644 index 00000000000..190f463f7a5 --- /dev/null +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -0,0 +1,340 @@ +import { expect } from 'chai'; +import { tripleliftAdapterSpec } from 'modules/tripleliftBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { deepClone } from 'src/utils'; +import prebid from '../../../package.json'; + +const ENDPOINT = 'https://tlx.3lift.com/header/auction?'; + +describe('triplelift adapter', function () { + const adapter = newBidder(tripleliftAdapterSpec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + bidder: 'triplelift', + params: { + inventoryCode: '12345', + floor: 1.0, + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true for valid bid request', function () { + expect(tripleliftAdapterSpec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when required params found', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + inventoryCode: 'another_inv_code', + floor: 0.05 + }; + expect(tripleliftAdapterSpec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + floor: 1.0 + }; + expect(tripleliftAdapterSpec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests; + let bidderRequest; + + this.beforeEach(() => { + bidRequests = [ + { + bidder: 'triplelift', + params: { + inventoryCode: '12345', + floor: 1.0, + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + } + ]; + + bidderRequest = { + bidderCode: 'triplelift', + auctionId: 'a7ebcd1d-66ff-4b5c-a82c-6a21a6ee5a18', + bidderRequestId: '5c55612f99bc11', + bids: [ + { + imp_id: 0, + cpm: 1.062, + width: 300, + height: 250, + ad: 'ad-markup', + iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + } + ], + refererInfo: { + referer: 'http://examplereferer.com' + }, + gdprConsent: { + consentString: 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7Vf7nnG072lPVA9LTOQ6gEaY', + gdprApplies: true + }, + }; + }); + + it('exists and is an object', function () { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + expect(request).to.exist.and.to.be.a('object'); + }); + + it('should only parse sizes that are of the proper length and format', function () { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + expect(request.data.imp[0].banner.format).to.have.length(2); + expect(request.data.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + }); + + it('should be a post request and populate the payload', function () { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const payload = request.data; + expect(payload).to.exist; + expect(payload.imp[0].tagid).to.equal('12345'); + expect(payload.imp[0].floor).to.equal(1.0); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + }); + + it('should add tdid to the payload if included', function () { + const id = '6bca7f6b-a98a-46c0-be05-6020f7604598'; + bidRequests[0].userId.tdid = id; + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const payload = request.data; + expect(payload).to.exist; + expect(payload.user).to.deep.equal({ext: {eids: [{source: 'adserver.org', uids: [{id, ext: {rtiPartner: 'TDID'}}]}]}}); + }); + + it('should add idl_env to the payload if included', function () { + const id = 'XY6104gr0njcH9UDIR7ysFFJcm2XNpqeJTYslleJ_cMlsFOfZI'; + bidRequests[0].userId.idl_env = id; + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const payload = request.data; + expect(payload).to.exist; + expect(payload.user).to.deep.equal({ext: {eids: [{source: 'liveramp.com', uids: [{id, ext: {rtiPartner: 'idl'}}]}]}}); + }); + + it('should add both tdid and idl_env to the payload if both are included', function () { + const tdidId = '6bca7f6b-a98a-46c0-be05-6020f7604598'; + const idlEnvId = 'XY6104gr0njcH9UDIR7ysFFJcm2XNpqeJTYslleJ_cMlsFOfZI'; + bidRequests[0].userId.tdid = tdidId; + bidRequests[0].userId.idl_env = idlEnvId; + + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const payload = request.data; + + expect(payload).to.exist; + expect(payload.user).to.deep.equal({ + ext: { + eids: [ + { + source: 'adserver.org', + uids: [ + { + id: tdidId, + ext: { rtiPartner: 'TDID' } + } + ], + }, + { + source: 'liveramp.com', + uids: [ + { + id: idlEnvId, + ext: { rtiPartner: 'idl' } + } + ] + } + ] + } + }); + }); + + it('should add user ids from multiple bid requests', function () { + const tdidId = '6bca7f6b-a98a-46c0-be05-6020f7604598'; + const idlEnvId = 'XY6104gr0njcH9UDIR7ysFFJcm2XNpqeJTYslleJ_cMlsFOfZI'; + + const bidRequestsMultiple = [ + { ...bidRequests[0], userId: { tdid: tdidId } }, + { ...bidRequests[0], userId: { idl_env: idlEnvId } }, + ]; + + const request = tripleliftAdapterSpec.buildRequests(bidRequestsMultiple, bidderRequest); + const payload = request.data; + + expect(payload.user).to.deep.equal({ + ext: { + eids: [ + { + source: 'adserver.org', + uids: [ + { + id: tdidId, + ext: { rtiPartner: 'TDID' } + } + ], + }, + { + source: 'liveramp.com', + uids: [ + { + id: idlEnvId, + ext: { rtiPartner: 'idl' } + } + ] + } + ] + } + }); + }); + + it('should return a query string for TL call', function () { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const url = request.url; + expect(url).to.exist; + expect(url).to.be.a('string'); + expect(url).to.match(/(?:tlx.3lift.com\/header\/auction)/) + expect(url).to.match(/(?:lib=prebid)/) + expect(url).to.match(new RegExp('(?:' + prebid.version + ')')) + expect(url).to.match(/(?:referrer)/); + }); + }); + + describe('interpretResponse', function () { + let response = { + body: { + bids: [ + { + imp_id: 0, + cpm: 1.062, + width: 300, + height: 250, + ad: 'ad-markup', + iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + } + ] + } + }; + let bidderRequest = { + bidderCode: 'triplelift', + auctionId: 'a7ebcd1d-66ff-4b5c-a82c-6a21a6ee5a18', + bidderRequestId: '5c55612f99bc11', + bids: [ + { + imp_id: 0, + cpm: 1.062, + width: 300, + height: 250, + ad: 'ad-markup', + iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + } + ], + refererInfo: { + referer: 'http://examplereferer.com' + }, + gdprConsent: { + consentString: 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7Vf7nnG072lPVA9LTOQ6gEaY', + gdprApplies: true + } + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '3db3773286ee59', + cpm: 1.062, + width: 300, + height: 250, + netRevenue: true, + ad: 'ad-markup', + creativeId: 29681110, + dealId: '', + currency: 'USD', + ttl: 33, + } + ]; + let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + expect(result).to.have.length(1); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('should return multile responses to support SRA', function () { + let response = { + body: { + bids: [ + { + imp_id: 0, + cpm: 1.062, + width: 300, + height: 250, + ad: 'ad-markup', + iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + }, + { + imp_id: 0, + cpm: 1.9, + width: 300, + height: 600, + ad: 'ad-markup-2', + iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + } + ] + } + }; + let bidderRequest = { + bidderCode: 'triplelift', + auctionId: 'a7ebcd1d-66ff-4b5c-a82c-6a21a6ee5a18', + bidderRequestId: '5c55612f99bc11', + bids: [ + { + imp_id: 0, + cpm: 1.062, + width: 300, + height: 600, + ad: 'ad-markup', + iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + }, + { + imp_id: 0, + cpm: 1.9, + width: 300, + height: 250, + ad: 'ad-markup-2', + iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + } + ], + refererInfo: { + referer: 'http://examplereferer.com' + }, + gdprConsent: { + consentString: 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7Vf7nnG072lPVA9LTOQ6gEaY', + gdprApplies: true + } + }; + let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + expect(result).to.have.length(2); + }); + }); +}); diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 6149545d59f..4256012ba0b 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -5,13 +5,13 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('TrustXAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'trustx', 'params': { @@ -24,11 +24,11 @@ describe('TrustXAdapter', function () { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -38,7 +38,23 @@ describe('TrustXAdapter', function () { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { + function parseRequest(url) { + const res = {}; + url.split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } + + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + const referrer = bidderRequest.refererInfo.referer; + let bidRequests = [ { 'bidder': 'trustx', @@ -57,7 +73,7 @@ describe('TrustXAdapter', function () { 'uid': '43' }, 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], + 'sizes': [[728, 90], [300, 250]], 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', @@ -75,63 +91,146 @@ describe('TrustXAdapter', function () { } ]; - it('should attach valid params to the tag', () => { - const request = spec.buildRequests([bidRequests[0]]); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + it('should attach valid params to the tag', function () { + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); + expect(payload).to.have.property('sizes', '300x250,300x600'); expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); }); - it('auids must not be duplicated', () => { - const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + it('sizes must not be duplicated', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('auids', '43,43,45'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); }); - it('pt parameter must be "gross" if params.priceType === "gross"', () => { + it('pt parameter must be "gross" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; - const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'gross'); - expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('auids', '43,43,45'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); delete bidRequests[1].params.priceType; }); - it('pt parameter must be "net" or "gross"', () => { + it('pt parameter must be "net" or "gross"', function () { bidRequests[1].params.priceType = 'some'; - const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('auids', '43,43,45'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); delete bidRequests[1].params.priceType; }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + + it('if gdprApplies is false gdpr_applies must be 0', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: false}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '0'); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA'}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + + it('should convert keyword params to proper form and attaches to request', function () { + const bidRequestWithKeywords = [].concat(bidRequests); + bidRequestWithKeywords[1] = Object.assign({}, + bidRequests[1], + { + params: { + uid: '43', + keywords: { + single: 'val', + singleArr: ['val'], + singleArrNum: [5], + multiValMixed: ['value1', 2, 'value3'], + singleValNum: 123, + emptyStr: '', + emptyArr: [''], + badValue: {'foo': 'bar'} // should be dropped + } + } + } + ); + + const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.keywords).to.be.an('string'); + payload.keywords = JSON.parse(payload.keywords); + + expect(payload.keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' + }]); + }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 44, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adm': '
    test content 4
    ', 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
    test content 5
    ', 'h': 250, 'w': 300}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, {'seat': '1'}, ]; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const bidRequests = [ { 'bidder': 'trustx', @@ -157,6 +256,7 @@ describe('TrustXAdapter', function () { 'ad': '
    test content 1
    ', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, } @@ -166,7 +266,7 @@ describe('TrustXAdapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('should get correct multi bid response', () => { + it('should get correct multi bid response', function () { const bidRequests = [ { 'bidder': 'trustx', @@ -214,42 +314,45 @@ describe('TrustXAdapter', function () { 'ad': '
    test content 1
    ', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, { - 'requestId': '5703af74d0472a', - 'cpm': 1.15, - 'creativeId': 43, + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 44, 'dealId': undefined, 'width': 300, - 'height': 250, - 'ad': '
    test content 1
    ', + 'height': 600, + 'ad': '
    test content 2
    ', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, { - 'requestId': '4dff80cc4ee346', - 'cpm': 0.5, - 'creativeId': 44, + 'requestId': '5703af74d0472a', + 'cpm': 0.15, + 'creativeId': 43, 'dealId': undefined, 'width': 728, 'height': 90, - 'ad': '
    test content 2
    ', + 'ad': '
    test content 3
    ', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, } ]; - const result = spec.interpretResponse({'body': {'seatbid': [responses[0], responses[1]]}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); expect(result).to.deep.equal(expectedResponse); }); - it('handles wrong and nobid responses', () => { + it('handles wrong and nobid responses', function () { const bidRequests = [ { 'bidder': 'trustx', @@ -286,8 +389,410 @@ describe('TrustXAdapter', function () { } ]; const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); expect(result.length).to.equal(0); }); + + it('complicated case', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 4
    ', 'auid': 43, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 5
    ', 'auid': 44, 'h': 600, 'w': 350}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2164be6358b9', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '326bde7fbf69', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '44' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4e111f1b66e4', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '44' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '1751cd90161', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '2164be6358b9', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '4e111f1b66e4', + 'cpm': 0.5, + 'creativeId': 44, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '26d6f897b516', + 'cpm': 0.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 3
    ', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '326bde7fbf69', + 'cpm': 0.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 4
    ', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('dublicate uids and sizes in one slot', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '5126e301f4be', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57b2ebe70e16', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '225fcd44b18c', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '5126e301f4be', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '57b2ebe70e16', + 'cpm': 0.5, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 2
    ', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + }); + + it('should get correct video bid response', function () { + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '50' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57dfefb80eca', + 'bidderRequestId': '20394420a762a2', + 'auctionId': '140132d07b031', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '51' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'e893c787c22dd', + 'bidderRequestId': '20394420a762a2', + 'auctionId': '140132d07b031', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + } + ]; + const response = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 50, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 51, content_type: 'video'}], 'seat': '2'} + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '57dfefb80eca', + 'cpm': 1.15, + 'creativeId': 50, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': response}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should have right renderer in the bid response', function () { + const spySetRenderer = sinon.spy(); + const stubRenderer = { + setRender: spySetRenderer + }; + const spyRendererInstall = sinon.spy(function() { return stubRenderer; }); + const stubRendererConst = { + install: spyRendererInstall + }; + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '50' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'e6e65553fc8', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3', + 'mediaTypes': { + 'video': { + 'context': 'outstream' + } + } + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '51' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c8fdcb3f269f', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3' + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '52' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '1de036c37685', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3', + 'renderer': {} + } + ]; + const response = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 50, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 51, content_type: 'video', w: 300, h: 250}], 'seat': '2'}, + {'bid': [{'price': 1.20, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 52, content_type: 'video', w: 300, h: 250}], 'seat': '2'} + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': 'e6e65553fc8', + 'cpm': 1.15, + 'creativeId': 50, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + }, + 'renderer': stubRenderer + }, + { + 'requestId': 'c8fdcb3f269f', + 'cpm': 1.00, + 'creativeId': 51, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + }, + 'renderer': stubRenderer + }, + { + 'requestId': '1de036c37685', + 'cpm': 1.20, + 'creativeId': 52, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': response}}, request, stubRendererConst); + + expect(spySetRenderer.calledTwice).to.equal(true); + expect(spySetRenderer.getCall(0).args[0]).to.be.a('function'); + expect(spySetRenderer.getCall(1).args[0]).to.be.a('function'); + + expect(spyRendererInstall.calledTwice).to.equal(true); + expect(spyRendererInstall.getCall(0).args[0]).to.deep.equal({ + id: 'e6e65553fc8', + url: '//acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + loaded: false + }); + expect(spyRendererInstall.getCall(1).args[0]).to.deep.equal({ + id: 'c8fdcb3f269f', + url: '//acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + loaded: false + }); + + expect(result).to.deep.equal(expectedResponse); }); }); diff --git a/test/spec/modules/turktelekomBidAdapter_spec.js b/test/spec/modules/turktelekomBidAdapter_spec.js new file mode 100644 index 00000000000..066d2724a97 --- /dev/null +++ b/test/spec/modules/turktelekomBidAdapter_spec.js @@ -0,0 +1,749 @@ +import { expect } from 'chai'; +import { spec } from 'modules/turktelekomBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('TurkTelekomAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'uid': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + function parseRequest(url) { + const res = {}; + url.split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } + + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + const referrer = bidderRequest.refererInfo.referer; + + let bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '18' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '18' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90], [300, 250]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '20' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should attach valid params to the tag', function () { + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '18'); + expect(payload).to.have.property('sizes', '300x250,300x600'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); + }); + + it('sizes must not be duplicated', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '18,18,20'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + }); + + it('pt parameter must be "gross" if params.priceType === "gross"', function () { + bidRequests[1].params.priceType = 'gross'; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('pt', 'gross'); + expect(payload).to.have.property('auids', '18,18,20'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + delete bidRequests[1].params.priceType; + }); + + it('pt parameter must be "net" or "gross"', function () { + bidRequests[1].params.priceType = 'some'; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '18,18,20'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + delete bidRequests[1].params.priceType; + }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + + it('if gdprApplies is false gdpr_applies must be 0', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: false}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '0'); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA'}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + }); + + describe('interpretResponse', function () { + const responses = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 18, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 17, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 19, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
    test content 5
    ', 'h': 250, 'w': 300}], 'seat': '1'}, + undefined, + {'bid': [], 'seat': '1'}, + {'seat': '1'}, + ]; + + it('should get correct bid response', function () { + const bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 17, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should get correct multi bid response', function () { + const bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71a5b', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '18' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4dff80cc4ee346', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '5703af74d0472a', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '300bfeb0d71a5b', + 'cpm': 1.15, + 'creativeId': 17, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 18, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '5703af74d0472a', + 'cpm': 0.15, + 'creativeId': 17, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 3
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('handles wrong and nobid responses', function () { + const bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '19' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d7190gf', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '20' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71321', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '25' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '300bfeb0d7183bb', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', + } + ]; + const request = spec.buildRequests(bidRequests); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); + expect(result.length).to.equal(0); + }); + + it('complicated case', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 18, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 17, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 4
    ', 'auid': 17, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 5
    ', 'auid': 18, 'h': 600, 'w': 350}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2164be6358b9', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '326bde7fbf69', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '18' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4e111f1b66e4', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '44' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '1751cd90161', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '2164be6358b9', + 'cpm': 1.15, + 'creativeId': 17, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '4e111f1b66e4', + 'cpm': 0.5, + 'creativeId': 18, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '26d6f897b516', + 'cpm': 0.15, + 'creativeId': 17, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 3
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '326bde7fbf69', + 'cpm': 0.15, + 'creativeId': 17, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 4
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('dublicate uids and sizes in one slot', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '5126e301f4be', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57b2ebe70e16', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '17' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '225fcd44b18c', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '5126e301f4be', + 'cpm': 1.15, + 'creativeId': 17, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '57b2ebe70e16', + 'cpm': 0.5, + 'creativeId': 17, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 2
    ', + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + }); + + it('should get correct video bid response', function () { + const bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '25' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57dfefb80eca', + 'bidderRequestId': '20394420a762a2', + 'auctionId': '140132d07b031', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '26' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'e893c787c22dd', + 'bidderRequestId': '20394420a762a2', + 'auctionId': '140132d07b031', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + } + ]; + const response = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 25, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 26, content_type: 'video'}], 'seat': '2'} + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '57dfefb80eca', + 'cpm': 1.15, + 'creativeId': 25, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': response}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should have right renderer in the bid response', function () { + const spySetRenderer = sinon.spy(); + const stubRenderer = { + setRender: spySetRenderer + }; + const spyRendererInstall = sinon.spy(function() { return stubRenderer; }); + const stubRendererConst = { + install: spyRendererInstall + }; + const bidRequests = [ + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '25' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'e6e65553fc8', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3', + 'mediaTypes': { + 'video': { + 'context': 'outstream' + } + } + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '26' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c8fdcb3f269f', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3' + }, + { + 'bidder': 'turktelekom', + 'params': { + 'uid': '27' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '1de036c37685', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3', + 'renderer': {} + } + ]; + const response = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 25, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 26, content_type: 'video', w: 300, h: 250}], 'seat': '2'}, + {'bid': [{'price': 1.20, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 27, content_type: 'video', w: 300, h: 250}], 'seat': '2'} + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': 'e6e65553fc8', + 'cpm': 1.15, + 'creativeId': 25, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + }, + 'renderer': stubRenderer + }, + { + 'requestId': 'c8fdcb3f269f', + 'cpm': 1.00, + 'creativeId': 26, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + }, + 'renderer': stubRenderer + }, + { + 'requestId': '1de036c37685', + 'cpm': 1.20, + 'creativeId': 27, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'bidderCode': 'turktelekom', + 'currency': 'TRY', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': response}}, request, stubRendererConst); + + expect(spySetRenderer.calledTwice).to.equal(true); + expect(spySetRenderer.getCall(0).args[0]).to.be.a('function'); + expect(spySetRenderer.getCall(1).args[0]).to.be.a('function'); + + expect(spyRendererInstall.calledTwice).to.equal(true); + expect(spyRendererInstall.getCall(0).args[0]).to.deep.equal({ + id: 'e6e65553fc8', + url: '//acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + loaded: false + }); + expect(spyRendererInstall.getCall(1).args[0]).to.deep.equal({ + id: 'c8fdcb3f269f', + url: '//acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + loaded: false + }); + + expect(result).to.deep.equal(expectedResponse); + }); +}); diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 152c7c39b1e..32daf5ecb96 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -1,19 +1,20 @@ import { expect } from 'chai'; import { spec } from 'modules/ucfunnelBidAdapter'; +import {BANNER, VIDEO, NATIVE} from 'src/mediaTypes'; const URL = '//hb.aralego.com/header'; const BIDDER_CODE = 'ucfunnel'; -const validBidReq = { +const validBannerBidReq = { bidder: BIDDER_CODE, params: { - adid: 'test-ad-83444226E44368D1E32E49EEBE6D29' + adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D' }, sizes: [[300, 250]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', }; -const invalidBidReq = { +const invalidBannerBidReq = { bidder: BIDDER_CODE, params: { adid: 123456789 @@ -23,86 +24,166 @@ const invalidBidReq = { auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' }; -const bidReq = [{ +const validBannerBidRes = { + creative_type: BANNER, + ad_id: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D', + adm: '
    ', + cpm: 0.01, + height: 250, + width: 300 +}; + +const validVideoBidReq = { bidder: BIDDER_CODE, params: { - adid: 'test-ad-83444226E44368D1E32E49EEBE6D29' + adid: 'ad-9A22D466494297EAC443D967B2622DA9' }, - sizes: [[300, 250]], - bidId: '263be71e91dd9d', - auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' -}]; + sizes: [[640, 360]], + bidId: '263be71e91dd9f', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', +}; -const validBidRes = { - ad_id: 'ad-83444226E44368D1E32E49EEBE6D29', - adm: '
    ', +const validVideoBidRes = { + creative_type: VIDEO, + ad_id: 'ad-9A22D466494297EAC443D967B2622DA9', + vastUrl: 'https://ads.aralego.com/ads/58f9749f-0553-4993-8d9a-013a38b29e55', + vastXml: 'ucX I-Primo 00:00:30', cpm: 0.01, - height: 250, - width: 300 + width: 640, + height: 360 +}; + +const validNativeBidReq = { + bidder: BIDDER_CODE, + params: { + adid: 'ad-627736446B2BD3A60E8AEABDB7BD833E' + }, + sizes: [[1, 1]], + bidId: '263be71e91dda0', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', }; -const bidResponse = validBidRes; - -const bidResArray = [ - validBidRes, - { - ad: '', - bidRequestId: '263be71e91dd9d', - cpm: 100, - adId: '123abc', - currency: 'USD', - netRevenue: true, - width: 300, - height: 250, - ttl: 360 +const validNativeBidRes = { + creative_type: NATIVE, + ad_id: 'ad-9A22D466494297EAC443D967B2622DA9', + native: { + title: 'ucfunnel adExchange', + body: 'We monetize your traffic via historic data driven protocol', + cta: 'Learn more', + sponsoredBy: 'ucfunnel Co., Ltd.', + image: { + url: 'https://cdn.aralego.net/img/main/AdGent-1200x627.jpg', + width: 1200, + height: 627 + }, + icon: { + url: 'https://cdn.aralego.net/img/logo/logo-84x84.jpg', + widt: 84, + heigh: 84 + }, + clickUrl: 'https://www.ucfunnel.com', + impressionTrackers: ['https://www.aralego.net/imp?mf=native&adid=ad-9A22D466494297EAC443D967B2622DA9&auc=9ad1fa8d-2297-4660-a018-b39945054746'], }, - { - ad: '
    Hello
    ', - bidRequestId: '', - cpm: 0, - adId: '123abc', - currency: 'USD', - netRevenue: true, - width: 300, - height: 250, - ttl: 360 - } -]; - -describe('ucfunnel Adapter', () => { - describe('request', () => { - it('should validate bid request', () => { - expect(spec.isBidRequestValid(validBidReq)).to.equal(true); + cpm: 0.01, + height: 1, + width: 1 +}; + +describe('ucfunnel Adapter', function () { + describe('request', function () { + it('should validate bid request', function () { + expect(spec.isBidRequestValid(validBannerBidReq)).to.equal(true); }); - it('should not validate incorrect bid request', () => { - expect(spec.isBidRequestValid(invalidBidReq)).to.equal(false); + it('should not validate incorrect bid request', function () { + expect(spec.isBidRequestValid(invalidBannerBidReq)).to.equal(false); }); }); - describe('build request', () => { - it('Verify bid request', () => { - const request = spec.buildRequests(bidReq); + describe('build request', function () { + const request = spec.buildRequests([validBannerBidReq]); + it('should create a POST request for every bid', function () { expect(request[0].method).to.equal('GET'); - expect(request[0].url).to.equal(URL); - expect(request[0].data).to.match(new RegExp(`${bidReq[0].params.adid}`)); + expect(request[0].url).to.equal(location.protocol + spec.ENDPOINT); + }); + + it('should attach the bid request object', function () { + expect(request[0].bidRequest).to.equal(validBannerBidReq); + }); + + it('should attach request data', function () { + const data = request[0].data; + const [ width, height ] = validBannerBidReq.sizes[0]; + expect(data.w).to.equal(width); + expect(data.h).to.equal(height); + }); + + it('must parse bid size from a nested array', function () { + const width = 640; + const height = 480; + validBannerBidReq.sizes = [[ width, height ]]; + const requests = spec.buildRequests([ validBannerBidReq ]); + const data = requests[0].data; + expect(data.w).to.equal(width); + expect(data.h).to.equal(height); }); }); - describe('interpretResponse', () => { - it('should build bid array', () => { - const request = spec.buildRequests(bidReq); - const result = spec.interpretResponse({body: bidResponse}, request[0]); - expect(result.length).to.equal(1); + describe('interpretResponse', function () { + describe('should support banner', function () { + const request = spec.buildRequests([ validBannerBidReq ]); + const result = spec.interpretResponse({body: validBannerBidRes}, request[0]); + it('should build bid array for banner', function () { + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', function () { + const bid = result[0]; + + expect(bid.mediaType).to.equal(BANNER); + expect(bid.ad).to.exist; + expect(bid.requestId).to.equal('263be71e91dd9d'); + expect(bid.cpm).to.equal(0.01); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + }); }); - it('should have all relevant fields', () => { - const request = spec.buildRequests(bidReq); - const result = spec.interpretResponse({body: bidResponse}, request[0]); - const bid = result[0]; + describe('should support video', function () { + const request = spec.buildRequests([ validVideoBidReq ]); + const result = spec.interpretResponse({body: validVideoBidRes}, request[0]); + it('should build bid array', function () { + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', function () { + const bid = result[0]; + + expect(bid.mediaType).to.equal(VIDEO); + expect(bid.vastUrl).to.exist; + expect(bid.vastXml).to.exist; + expect(bid.requestId).to.equal('263be71e91dd9f'); + expect(bid.cpm).to.equal(0.01); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(360); + }); + }); + + describe('should support native', function () { + const request = spec.buildRequests([ validNativeBidReq ]); + const result = spec.interpretResponse({body: validNativeBidRes}, request[0]); + it('should build bid array', function () { + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', function () { + const bid = result[0]; - expect(bid.requestId).to.equal('263be71e91dd9d'); - expect(bid.cpm).to.equal(0.01); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); + expect(bid.mediaType).to.equal(NATIVE); + expect(bid.native).to.exist; + expect(bid.requestId).to.equal('263be71e91dda0'); + expect(bid.cpm).to.equal(0.01); + expect(bid.width).to.equal(1); + expect(bid.height).to.equal(1); + }); }); }); }); diff --git a/test/spec/modules/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index 5dc2a65399f..6f4df57d316 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -1,10 +1,11 @@ import { expect } from 'chai'; import { spec } from 'modules/underdogmediaBidAdapter'; -describe('UnderdogMedia adapter', () => { +describe('UnderdogMedia adapter', function () { let bidRequests; + let bidderRequest; - beforeEach(() => { + beforeEach(function () { bidRequests = [ { bidder: 'underdogmedia', @@ -19,11 +20,24 @@ describe('UnderdogMedia adapter', () => { transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' } ]; + + bidderRequest = { + timeout: 3000, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentDataString', + vendorData: { + vendorConsents: { + '159': 1 + }, + }, + }, + } }); - describe('implementation', () => { - describe('for requests', () => { - it('should accept valid bid', () => { + describe('implementation', function () { + describe('for requests', function () { + it('should accept valid bid', function () { let validBid = { bidder: 'underdogmedia', params: { @@ -36,7 +50,7 @@ describe('UnderdogMedia adapter', () => { expect(isValid).to.equal(true); }); - it('should reject invalid bid missing sizes', () => { + it('should reject invalid bid missing sizes', function () { let invalidBid = { bidder: 'underdogmedia', params: { @@ -48,7 +62,7 @@ describe('UnderdogMedia adapter', () => { expect(isValid).to.equal(false); }); - it('should reject invalid bid missing siteId', () => { + it('should reject invalid bid missing siteId', function () { let invalidBid = { bidder: 'underdogmedia', params: {}, @@ -59,7 +73,7 @@ describe('UnderdogMedia adapter', () => { expect(isValid).to.equal(false); }); - it('request data should contain sid', () => { + it('request data should contain sid', function () { let bidRequests = [ { bidId: '3c9408cdbf2f68', @@ -72,12 +86,116 @@ describe('UnderdogMedia adapter', () => { adUnitCode: '/123456/header-bid-tag-1' } ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.sid).to.equal('12143'); + }); + + it('request data should contain sizes', function () { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250], [728, 90]], + bidder: 'underdogmedia', + params: { + siteId: '12143' + }, + auctionId: '10b327aa396609', + adUnitCode: '/123456/header-bid-tag-1' + } + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.sizes).to.equal('300x250,728x90'); + }); + + it('request data should contain gdpr info', function () { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250], [728, 90]], + bidder: 'underdogmedia', + params: { + siteId: '12143' + }, + auctionId: '10b327aa396609', + adUnitCode: '/123456/header-bid-tag-1' + } + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.gdprApplies).to.equal(true); + expect(request.data.consentGiven).to.equal(true); + expect(request.data.consentData).to.equal('consentDataString'); + }); + + it('should not build a request if no vendorConsent', function () { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250], [728, 90]], + bidder: 'underdogmedia', + params: { + siteId: '12143' + }, + auctionId: '10b327aa396609', + adUnitCode: '/123456/header-bid-tag-1' + } + ]; + + let bidderRequest = { + timeout: 3000, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentDataString', + vendorData: { + vendorConsents: { + '159': 0 + }, + }, + }, + } + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request).to.equal(undefined); + }); + + it('should properly build a request if no vendorConsent but no gdprApplies', function () { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250], [728, 90]], + bidder: 'underdogmedia', + params: { + siteId: '12143' + }, + auctionId: '10b327aa396609', + adUnitCode: '/123456/header-bid-tag-1' + } + ]; - expect(request.data).to.have.string('sid=12143'); + let bidderRequest = { + timeout: 3000, + gdprConsent: { + gdprApplies: 0, + consentString: 'consentDataString', + vendorData: { + vendorConsents: { + '159': 0 + }, + }, + }, + } + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.sizes).to.equal('300x250,728x90'); + expect(request.data.sid).to.equal('12143'); + expect(request.data.gdprApplies).to.equal(false); + expect(request.data.consentGiven).to.equal(false); + expect(request.data.consentData).to.equal('consentDataString'); }); - it('request data should contain sizes', () => { + it('should properly build a request if gdprConsent empty', function () { let bidRequests = [ { bidId: '3c9408cdbf2f68', @@ -90,14 +208,20 @@ describe('UnderdogMedia adapter', () => { adUnitCode: '/123456/header-bid-tag-1' } ]; - const request = spec.buildRequests(bidRequests); - expect(request.data).to.have.string('sizes=300x250,728x90'); + let bidderRequest = { + timeout: 3000, + gdprConsent: {} + } + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.sizes).to.equal('300x250,728x90'); + expect(request.data.sid).to.equal('12143'); }); }); - describe('bid responses', () => { - it('should return complete bid response', () => { + describe('bid responses', function () { + it('should return complete bid response', function () { let serverResponse = { body: { mids: [ @@ -122,7 +246,7 @@ describe('UnderdogMedia adapter', () => { ] } }; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const bids = spec.interpretResponse(serverResponse, request); expect(bids).to.be.lengthOf(2); @@ -136,19 +260,19 @@ describe('UnderdogMedia adapter', () => { expect(bids[0].currency).to.equal('USD'); }); - it('should return empty bid response if mids empty', () => { + it('should return empty bid response if mids empty', function () { let serverResponse = { body: { mids: [] } }; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const bids = spec.interpretResponse(serverResponse, request); expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response on incorrect size', () => { + it('should return empty bid response on incorrect size', function () { let serverResponse = { body: { mids: [ @@ -164,13 +288,13 @@ describe('UnderdogMedia adapter', () => { ] } }; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const bids = spec.interpretResponse(serverResponse, request); expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response on 0 cpm', () => { + it('should return empty bid response on 0 cpm', function () { let serverResponse = { body: { mids: [ @@ -186,13 +310,13 @@ describe('UnderdogMedia adapter', () => { ] } }; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const bids = spec.interpretResponse(serverResponse, request); expect(bids).to.be.lengthOf(0); }); - it('should return empty bid response if no ad in response', () => { + it('should return empty bid response if no ad in response', function () { let serverResponse = { body: { mids: [ @@ -208,13 +332,13 @@ describe('UnderdogMedia adapter', () => { ] } }; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const bids = spec.interpretResponse(serverResponse, request); expect(bids).to.be.lengthOf(0); }); - it('ad html string should contain the notification urls', () => { + it('ad html string should contain the notification urls', function () { let serverResponse = { body: { mids: [ @@ -230,7 +354,7 @@ describe('UnderdogMedia adapter', () => { ] } }; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const bids = spec.interpretResponse(serverResponse, request); expect(bids[0].ad).to.have.string('notification_url'); diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index d86a1dc5735..676f2714693 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -11,7 +11,7 @@ const validBidReq = { }, sizes: [[300, 250], [300, 600]], bidId: '263be71e91dd9d', - auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054747', }; const invalidBidReq = { @@ -33,8 +33,57 @@ const bidReq = [{ sizes: [[300, 250], [300, 600]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' +}, +{ + bidder: BIDDER_CODE, + params: { + publisherId: 12345 + }, + sizes: [[1, 1]], + bidId: '453cf42d72bb3c', + auctionId: '6c22f5a5-59df-4dc6-b92c-f433bcf0a874' +}]; + +const bidReqUserIds = [{ + bidder: BIDDER_CODE, + params: { + placementId: '10433394', + publisherId: 12345 + }, + sizes: [[300, 250], [300, 600]], + bidId: '263be71e91dd9d', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', + userId: { + tdid: '123456', + digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}} + } +}, +{ + bidder: BIDDER_CODE, + params: { + publisherId: 12345 + }, + sizes: [[1, 1]], + bidId: '453cf42d72bb3c', + auctionId: '6c22f5a5-59df-4dc6-b92c-f433bcf0a874' }]; +const bidderReq = { + refererInfo: { + referer: 'http://prebid.org/dev-docs/bidder-adaptor.html' + } +}; + +const bidderReqGdpr = { + refererInfo: { + referer: 'http://prebid.org/dev-docs/bidder-adaptor.html' + }, + gdprConsent: { + gdprApplies: true, + consentString: 'acdefgh' + } +}; + const validBidRes = { ad: '
    Hello
    ', publisherId: 12345, @@ -78,41 +127,67 @@ const bidResArray = [ } ]; -describe('Undertone Adapter', () => { - describe('request', () => { - it('should validate bid request', () => { +describe('Undertone Adapter', function () { + describe('request', function () { + it('should validate bid request', function () { expect(spec.isBidRequestValid(validBidReq)).to.equal(true); }); - it('should not validate incorrect bid request', () => { + it('should not validate incorrect bid request', function () { expect(spec.isBidRequestValid(invalidBidReq)).to.equal(undefined); }); }); - describe('build request', () => { - it('should send request to correct url via POST', () => { - const request = spec.buildRequests(bidReq); - const domain = null; + describe('build request', function () { + it('should send request to correct url via POST not in GDPR', function () { + const request = spec.buildRequests(bidReq, bidderReq); + const domainStart = bidderReq.refererInfo.referer.indexOf('//'); + const domainEnd = bidderReq.refererInfo.referer.indexOf('/', domainStart + 2); + const domain = bidderReq.refererInfo.referer.substring(domainStart + 2, domainEnd); const REQ_URL = `${URL}?pid=${bidReq[0].params.publisherId}&domain=${domain}`; expect(request.url).to.equal(REQ_URL); expect(request.method).to.equal('POST'); }); - it('should have all relevant fields', () => { - const request = spec.buildRequests(bidReq); - const bid = JSON.parse(request.data)['x-ut-hb-params'][0]; - expect(bid.bidRequestId).to.equal('263be71e91dd9d'); - expect(bid.sizes.length > 0).to.equal(true); - expect(bid.placementId).to.equal('10433394'); - expect(bid.publisherId).to.equal(12345); - expect(bid.params).to.be.an('object'); + it('should send request to correct url via POST when in GDPR', function () { + const request = spec.buildRequests(bidReq, bidderReqGdpr); + const domainStart = bidderReq.refererInfo.referer.indexOf('//'); + const domainEnd = bidderReq.refererInfo.referer.indexOf('/', domainStart + 2); + const domain = bidderReq.refererInfo.referer.substring(domainStart + 2, domainEnd); + let gdpr = bidderReqGdpr.gdprConsent.gdprApplies ? 1 : 0 + const REQ_URL = `${URL}?pid=${bidReq[0].params.publisherId}&domain=${domain}&gdpr=${gdpr}&gdprstr=${bidderReqGdpr.gdprConsent.consentString}`; + expect(request.url).to.equal(REQ_URL); + expect(request.method).to.equal('POST'); + }); + it('should have all relevant fields', function () { + const request = spec.buildRequests(bidReq, bidderReq); + const bid1 = JSON.parse(request.data)['x-ut-hb-params'][0]; + expect(bid1.bidRequestId).to.equal('263be71e91dd9d'); + expect(bid1.sizes.length).to.equal(2); + expect(bid1.placementId).to.equal('10433394'); + expect(bid1.publisherId).to.equal(12345); + expect(bid1.params).to.be.an('object'); + const bid2 = JSON.parse(request.data)['x-ut-hb-params'][1]; + expect(bid2.bidRequestId).to.equal('453cf42d72bb3c'); + expect(bid2.sizes.length).to.equal(1); + expect(bid2.placementId === null).to.equal(true); + expect(bid2.publisherId).to.equal(12345); + expect(bid2.params).to.be.an('object'); + }); + it('should send all userIds data to server', function () { + const request = spec.buildRequests(bidReqUserIds, bidderReq); + const bidCommons = JSON.parse(request.data)['commons']; + expect(bidCommons).to.be.an('object'); + expect(bidCommons.uids).to.be.an('object'); + expect(bidCommons.uids.tdid).to.equal('123456'); + expect(bidCommons.uids.digitrustid.data.id).to.equal('DTID'); }); }); - describe('interpretResponse', () => { - it('should build bid array', () => { + describe('interpretResponse', function () { + it('should build bid array', function () { let result = spec.interpretResponse({body: bidResponse}); expect(result.length).to.equal(1); }); - it('should have all relevant fields', () => { + it('should have all relevant fields', function () { const result = spec.interpretResponse({body: bidResponse}); const bid = result[0]; @@ -126,13 +201,81 @@ describe('Undertone Adapter', () => { expect(bid.ttl).to.equal(360); }); - it('should return empty array when response is incorrect', () => { + it('should return empty array when response is incorrect', function () { expect(spec.interpretResponse({body: {}}).length).to.equal(0); expect(spec.interpretResponse({body: []}).length).to.equal(0); }); - it('should only use valid bid responses', () => { + it('should only use valid bid responses', function () { expect(spec.interpretResponse({ body: bidResArray }).length).to.equal(1); }); }); + + describe('getUserSyncs', () => { + let testParams = [ + { + name: 'with iframe and no gdpr data', + arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, null], + expect: { + type: 'iframe', + pixels: ['//cdn.undertone.com/js/usersync.html'] + } + }, + { + name: 'with iframe and gdpr on', + arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}], + expect: { + type: 'iframe', + pixels: ['//cdn.undertone.com/js/usersync.html?gdpr=1&gdprstr=234234'] + } + }, + { + name: 'with iframe and no gdpr off', + arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, {gdprApplies: false}], + expect: { + type: 'iframe', + pixels: ['//cdn.undertone.com/js/usersync.html?gdpr=0&gdprstr='] + } + }, + { + name: 'with pixels and no gdpr data', + arguments: [{ pixelEnabled: true }, {}, null], + expect: { + type: 'image', + pixels: ['//usr.undertone.com/userPixel/syncOne?id=1&of=2', + '//usr.undertone.com/userPixel/syncOne?id=2&of=2'] + } + }, + { + name: 'with pixels and gdpr on', + arguments: [{ pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}], + expect: { + type: 'image', + pixels: ['//usr.undertone.com/userPixel/syncOne?id=1&of=2&gdpr=1&gdprstr=234234', + '//usr.undertone.com/userPixel/syncOne?id=2&of=2&gdpr=1&gdprstr=234234'] + } + }, + { + name: 'with pixels and gdpr off', + arguments: [{ pixelEnabled: true }, {}, {gdprApplies: false}], + expect: { + type: 'image', + pixels: ['//usr.undertone.com/userPixel/syncOne?id=1&of=2&gdpr=0&gdprstr=', + '//usr.undertone.com/userPixel/syncOne?id=2&of=2&gdpr=0&gdprstr='] + } + } + ]; + + for (let i = 0; i < testParams.length; i++) { + let currParams = testParams[i]; + it(currParams.name, function () { + const result = spec.getUserSyncs.apply(this, currParams.arguments); + expect(result).to.have.lengthOf(currParams.expect.pixels.length); + for (let ix = 0; ix < currParams.expect.pixels.length; ix++) { + expect(result[ix].url).to.equal(currParams.expect.pixels[ix]); + expect(result[ix].type).to.equal(currParams.expect.type); + } + }); + } + }); }); diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index e4eaa35d662..cb30fcf1a9d 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -6,7 +6,7 @@ import { VIDEO } from 'src/mediaTypes' import { Renderer } from 'src/Renderer' import { adapter } from 'modules/unrulyBidAdapter' -describe('UnrulyAdapter', () => { +describe('UnrulyAdapter', function () { function createOutStreamExchangeBid({ adUnitCode = 'placement2', statusCode = 1, @@ -18,7 +18,10 @@ describe('UnrulyAdapter', () => { 'statusCode': statusCode, 'renderer': { 'id': 'unruly_inarticle', - 'config': {}, + 'config': { + 'siteId': 123456, + 'targetingUUID': 'xxx-yyy-zzz' + }, 'url': 'https://video.unrulymedia.com/native/prebid-loader.js' }, 'adUnitCode': adUnitCode @@ -39,7 +42,7 @@ describe('UnrulyAdapter', () => { let sandbox; let fakeRenderer; - beforeEach(() => { + beforeEach(function () { sandbox = sinon.sandbox.create(); sandbox.stub(utils, 'logError'); sandbox.stub(Renderer, 'install'); @@ -50,12 +53,12 @@ describe('UnrulyAdapter', () => { Renderer.install.returns(fakeRenderer) }); - afterEach(() => { + afterEach(function () { sandbox.restore(); delete parent.window.unruly }); - it('should expose Unruly Bidder code', () => { + it('should expose Unruly Bidder code', function () { expect(adapter.code).to.equal('unruly') }); @@ -63,22 +66,22 @@ describe('UnrulyAdapter', () => { expect(adapter.supportedMediaTypes).to.deep.equal([ VIDEO ]) }); - describe('isBidRequestValid', () => { - it('should be a function', () => { + describe('isBidRequestValid', function () { + it('should be a function', function () { expect(typeof adapter.isBidRequestValid).to.equal('function') }); - it('should return false if bid is falsey', () => { + it('should return false if bid is falsey', function () { expect(adapter.isBidRequestValid()).to.be.false; }); - it('should return true if bid.mediaType is "video"', () => { + it('should return true if bid.mediaType is "video"', function () { const mockBid = { mediaType: 'video' }; expect(adapter.isBidRequestValid(mockBid)).to.be.true; }); - it('should return true if bid.mediaTypes.video.context is "outstream"', () => { + it('should return true if bid.mediaTypes.video.context is "outstream"', function () { const mockBid = { mediaTypes: { video: { @@ -91,46 +94,48 @@ describe('UnrulyAdapter', () => { }); }); - describe('buildRequests', () => { - it('should be a function', () => { + describe('buildRequests', function () { + it('should be a function', function () { expect(typeof adapter.buildRequests).to.equal('function'); }); - it('should return an object', () => { + it('should return an object', function () { const mockBidRequests = ['mockBid']; expect(typeof adapter.buildRequests(mockBidRequests)).to.equal('object') }); - it('should return a server request with a valid exchange url', () => { + it('should return a server request with a valid exchange url', function () { const mockBidRequests = ['mockBid']; expect(adapter.buildRequests(mockBidRequests).url).to.equal('https://targeting.unrulymedia.com/prebid') }); - it('should return a server request with method === POST', () => { + it('should return a server request with method === POST', function () { const mockBidRequests = ['mockBid']; expect(adapter.buildRequests(mockBidRequests).method).to.equal('POST'); }); - it('should ensure contentType is `application/json`', function () { + it('should ensure contentType is `text/plain`', function () { const mockBidRequests = ['mockBid']; expect(adapter.buildRequests(mockBidRequests).options).to.deep.equal({ - contentType: 'application/json' + contentType: 'text/plain' }); }); - it('should return a server request with valid payload', () => { + it('should return a server request with valid payload', function () { const mockBidRequests = ['mockBid']; - expect(adapter.buildRequests(mockBidRequests).data).to.deep.equal({bidRequests: mockBidRequests}) + const mockBidderRequest = {bidderCode: 'mockBidder'}; + expect(adapter.buildRequests(mockBidRequests, mockBidderRequest).data) + .to.deep.equal({bidRequests: mockBidRequests, bidderRequest: mockBidderRequest}) }) }); - describe('interpretResponse', () => { - it('should be a function', () => { + describe('interpretResponse', function () { + it('should be a function', function () { expect(typeof adapter.interpretResponse).to.equal('function'); }); - it('should return empty array when serverResponse is undefined', () => { + it('should return [] when serverResponse is undefined', function () { expect(adapter.interpretResponse()).to.deep.equal([]); }); - it('should return empty array when serverResponse has no bids', () => { + it('should return [] when serverResponse has no bids', function () { const mockServerResponse = { body: { bids: [] } }; expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]) }); - it('should return array of bids when receive a successful response from server', () => { + it('should return array of bids when receive a successful response from server', function () { const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); const mockServerResponse = createExchangeResponse(mockExchangeBid); expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([ @@ -144,23 +149,30 @@ describe('UnrulyAdapter', () => { creativeId: 'mockBidId', ttl: 360, currency: 'USD', - renderer: fakeRenderer + renderer: fakeRenderer, + mediaType: 'video' } ]) }); - it('should initialize and set the renderer', () => { - expect(Renderer.install).not.to.have.been.called; - expect(fakeRenderer.setRender).not.to.have.been.called; + it('should initialize and set the renderer', function () { + expect(Renderer.install.called).to.be.false; + expect(fakeRenderer.setRender.called).to.be.false; const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); - const mockRenderer = { url: 'value: mockRendererURL' }; + const mockRenderer = { + url: 'value: mockRendererURL', + config: { + siteId: 123456, + targetingUUID: 'xxx-yyy-zzz' + } + }; mockReturnedBid.ext.renderer = mockRenderer; const mockServerResponse = createExchangeResponse(mockReturnedBid); adapter.interpretResponse(mockServerResponse); - expect(Renderer.install).to.have.been.calledOnce; + expect(Renderer.install.calledOnce).to.be.true; sinon.assert.calledWithExactly( Renderer.install, Object.assign({}, mockRenderer, {callback: sinon.match.func}) @@ -170,7 +182,59 @@ describe('UnrulyAdapter', () => { sinon.assert.calledWithExactly(fakeRenderer.setRender, sinon.match.func) }); - it('bid is placed on the bid queue when render is called', () => { + it('should return [] and log if bidResponse renderer config is not available', function () { + sinon.assert.notCalled(utils.logError) + + expect(Renderer.install.called).to.be.false; + expect(fakeRenderer.setRender.called).to.be.false; + + const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockRenderer = { + url: 'value: mockRendererURL' + }; + mockReturnedBid.ext.renderer = mockRenderer; + const mockServerResponse = createExchangeResponse(mockReturnedBid); + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); + + const logErrorCalls = utils.logError.getCalls(); + expect(logErrorCalls.length).to.equal(2); + + const [ configErrorCall, siteIdErrorCall ] = logErrorCalls; + + expect(configErrorCall.args.length).to.equal(1); + expect(configErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer config.'); + + expect(siteIdErrorCall.args.length).to.equal(1); + expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer siteId.'); + }); + + it('should return [] and log if siteId is not available', function () { + sinon.assert.notCalled(utils.logError) + + expect(Renderer.install.called).to.be.false; + expect(fakeRenderer.setRender.called).to.be.false; + + const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockRenderer = { + url: 'value: mockRendererURL', + config: {} + }; + mockReturnedBid.ext.renderer = mockRenderer; + const mockServerResponse = createExchangeResponse(mockReturnedBid); + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); + + const logErrorCalls = utils.logError.getCalls(); + expect(logErrorCalls.length).to.equal(1); + + const [ siteIdErrorCall ] = logErrorCalls; + + expect(siteIdErrorCall.args.length).to.equal(1); + expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer siteId.'); + }); + + it('bid is placed on the bid queue when render is called', function () { const exchangeBid = createOutStreamExchangeBid({ adUnitCode: 'video', vastUrl: 'value: vastUrl' }); const exchangeResponse = createExchangeResponse(exchangeBid); @@ -179,7 +243,7 @@ describe('UnrulyAdapter', () => { sinon.assert.calledOnce(fakeRenderer.setRender); fakeRenderer.setRender.firstCall.args[0](); - expect(window.top).to.have.deep.property('unruly.native.prebid.uq'); + expect(window.top).to.have.deep.nested.property('unruly.native.prebid.uq'); const uq = window.top.unruly.native.prebid.uq; const sentRendererConfig = uq[0][1]; @@ -188,9 +252,9 @@ describe('UnrulyAdapter', () => { expect(sentRendererConfig.vastUrl).to.equal('value: vastUrl'); expect(sentRendererConfig.renderer).to.equal(fakeRenderer); expect(sentRendererConfig.adUnitCode).to.equal('video') - }) + }); - it('should ensure that renderer is placed in Prebid supply mode', () => { + it('should ensure that renderer is placed in Prebid supply mode', function () { const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); const mockServerResponse = createExchangeResponse(mockExchangeBid); @@ -203,4 +267,64 @@ describe('UnrulyAdapter', () => { expect(supplyMode).to.equal('prebid'); }); }); + + describe('getUserSyncs', () => { + it('should push user sync iframe if enabled', () => { + const mockConsent = {} + const response = {} + const syncOptions = { iframeEnabled: true } + const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) + expect(syncs[0]).to.deep.equal({ + type: 'iframe', + url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html' + }); + }) + + it('should not push user sync iframe if not enabled', () => { + const mockConsent = {} + const response = {} + const syncOptions = { iframeEnabled: false } + const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent); + expect(syncs).to.be.empty; + }); + }); + + it('should not append consent params if gdpr does not apply', () => { + const mockConsent = {} + const response = {} + const syncOptions = { iframeEnabled: true } + const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) + expect(syncs[0]).to.deep.equal({ + type: 'iframe', + url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html' + }) + }); + + it('should append consent params if gdpr does apply and consent is given', () => { + const mockConsent = { + gdprApplies: true, + consentString: 'hello' + }; + const response = {} + const syncOptions = { iframeEnabled: true } + const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) + expect(syncs[0]).to.deep.equal({ + type: 'iframe', + url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html?gdpr=1&gdpr_consent=hello' + }) + }); + + it('should append consent param if gdpr applies and no consent is given', () => { + const mockConsent = { + gdprApplies: true, + consentString: {} + }; + const response = {}; + const syncOptions = { iframeEnabled: true } + const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) + expect(syncs[0]).to.deep.equal({ + type: 'iframe', + url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html?gdpr=0' + }) + }) }); diff --git a/test/spec/modules/uolBidAdapter_spec.js b/test/spec/modules/uolBidAdapter_spec.js new file mode 100644 index 00000000000..e9341772e7d --- /dev/null +++ b/test/spec/modules/uolBidAdapter_spec.js @@ -0,0 +1,308 @@ +import { expect } from 'chai'; +import { spec } from 'modules/uolBidAdapter'; + +const ENDPOINT = 'https://prebid.adilligo.com/v1/prebid.json'; + +describe('UOL Bid Adapter', function () { + let sandbox; + let queryStub; + let getCurrentPositionStub; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function() { + sandbox.restore(); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'uol', + 'params': { + 'placementId': '19571273' + }, + 'adUnitCode': '/uol/unit/code', + 'sizes': [[300, 250], [970, 250]], + 'bidId': '3ddb6ed2d73b45', + 'bidderRequestId': 'd2b12f9d2bad975b7', + 'auctionId': 'eb511c63-df7e-4240-9b65-2f8ae50303e4', + }; + + it('should return true for valid params', function () { + let clonedBid = Object.assign({}, bid); + expect(spec.isBidRequestValid(clonedBid)).to.equal(true); + + delete clonedBid.params; + clonedBid.params = { + 'placementId': '19571277', + 'test': 'true' + } + expect(spec.isBidRequestValid(clonedBid)).to.equal(true); + + delete clonedBid.params; + clonedBid.params = { + 'placementId': '19571278', + 'test': 'true', + 'cpmFactor': 2 + } + expect(spec.isBidRequestValid(clonedBid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let clonedBid = Object.assign({}, bid); + delete clonedBid.params; + expect(spec.isBidRequestValid(clonedBid)).to.equal(false); + }); + + it('should return false when params are invalid', function () { + let clonedBid = Object.assign({}, bid); + delete clonedBid.params; + clonedBid.params = { + 'placementId': 0 + } + expect(spec.isBidRequestValid(clonedBid)).to.equal(false); + + delete clonedBid.params; + clonedBid.params = { + 'placementId': '19571281', + 'cpmFactor': 2 + } + expect(spec.isBidRequestValid(clonedBid)).to.equal(false); + + delete clonedBid.params; + clonedBid.params = { + 'placementId': '19571282', + 'cpmFactor': 'two' + } + expect(spec.isBidRequestValid(clonedBid)).to.equal(false); + }); + + it('should return false when cpmFactor is passed and test flag isn\'t active', function () { + let clonedBid = Object.assign({}, bid); + delete clonedBid.params; + clonedBid.params = { + 'placementId': '19571283', + 'test': false, + 'cpmFactor': 2 + }; + expect(spec.isBidRequestValid(clonedBid)).to.equal(false); + }); + + it('should not allow empty size', function () { + let clonedBid = Object.assign({}, bid); + delete clonedBid.sizes; + expect(spec.isBidRequestValid(clonedBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'uol', + 'params': { + 'placementId': '19571273' + }, + 'adUnitCode': '/uol/unit/code', + 'sizes': [[300, 250]], + 'bidId': '3ddb6ed2d73b45', + 'bidderRequestId': 'd2b12f9d2bad975b7', + 'auctionId': 'eb511c63-df7e-4240-9b65-2f8ae50303e4', + }, { + 'bidder': 'uol', + 'params': { + 'placementId': '19571274' + }, + 'adUnitCode': '/uol/unit/code2', + 'sizes': [[300, 600], [970, 250]], + 'bidId': '3a3ea8e80a2dc5', + 'bidderRequestId': 'd2b12f9d2bad975b7', + 'auctionId': 'eb511c63-df7e-4240-9b65-2f8ae50303e4', + } + ]; + + let bidderRequest = { + 'auctionId': 'eb511c63-df7e-4240-9b65-2f8ae50303e4', + 'auctionStart': 1530133180799, + 'bidderCode': 'uol', + 'bidderRequestId': 'd2b12f9d2bad975b7', + 'bids': bidRequests, + 'doneCbCallCount': 1, + 'start': 1530133180801, + 'timeout': 3000 + }; + + describe('buildRequest basic params', function () { + const requestObject = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(requestObject.data); + + it('should send bid requests to expected endpoint via POST method', function () { + expect(requestObject.url).to.equal(ENDPOINT); + expect(requestObject.method).to.equal('POST'); + }); + + it('should contain referrer URL', function () { + expect(payload.referrerURL).to.exist.and.to.match(/^http(s)?:\/\/.+$/) + }); + + it('should contain an array of requests with length equivalent to bid count', function () { + expect(payload.requests).to.have.length(bidRequests.length); + }); + it('should return propper ad size if at least one entry is provided', function () { + expect(payload.requests[0].sizes).to.deep.equal(bidRequests[0].sizes); + }); + }); + + if (navigator.permissions && navigator.permissions.query && navigator.geolocation) { + describe('buildRequest geolocation param', function () { // shall only be tested if browser engine supports geolocation and permissions API. + let geolocation = { lat: 4, long: 3, timestamp: 123121451 }; + + beforeEach(function() { + getCurrentPositionStub = sandbox.stub(navigator.geolocation, 'getCurrentPosition'); + queryStub = sandbox.stub(navigator.permissions, 'query'); + }); + + it('should not contain user coordinates if browser doesnt support permission query', function () { + localStorage.setItem('uolLocationTracker', JSON.stringify(geolocation)); + navigator.permissions.query = undefined; + const requestObject = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(requestObject.data); + expect(payload.geolocation).to.not.exist; + }) + + it('should contain user coordinates if (i) DNT is off; (ii) browser supports implementation; (iii) localStorage contains geolocation history', function (done) { + localStorage.setItem('uolLocationTracker', JSON.stringify(geolocation)); + queryStub.callsFake(function() { + return new Promise((resolve, reject) => { + resolve({state: 'granted'}); + }); + }); + getCurrentPositionStub.callsFake(() => done()); + const requestObject = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(requestObject.data); + expect(payload.geolocation).to.exist.and.not.be.empty; + }) + + it('should not contain user coordinates if localStorage is empty', function () { + localStorage.removeItem('uolLocationTracker'); + queryStub.callsFake(function() { + return new Promise((resolve, reject) => { + resolve({state: 'prompt'}); + }); + }); + const requestObject = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(requestObject.data); + expect(payload.geolocation).to.not.exist; + }) + }) + } + describe('buildRequest test params', function () { + it('should return test and cpmFactor params if defined', function () { + let clonedBid = JSON.parse(JSON.stringify(bidRequests)); + delete clonedBid[0].params; + clonedBid.splice(1, 1); + clonedBid[0].params = { + 'placementId': '19571277', + 'test': true + } + let requestObject = spec.buildRequests(clonedBid, bidderRequest); + let payload = JSON.parse(requestObject.data); + expect(payload.requests[0].customParams.test).to.exist.and.equal(true); + expect(payload.requests[0].customParams.cpmFactor).to.be.an('undefined'); + + delete clonedBid[0].params; + clonedBid[0].params = { + 'placementId': '19571278', + 'test': true, + 'cpmFactor': 2 + } + requestObject = spec.buildRequests(clonedBid, bidderRequest); + payload = JSON.parse(requestObject.data); + expect(payload.requests[0].customParams.test).to.exist.and.equal(true); + expect(payload.requests[0].customParams.cpmFactor).to.exist.and.equal(2); + }); + }) + }); + + describe('interpretResponse', function () { + let serverResponse = { + 'body': { + 'bidderRequestId': '2a21a2fc993ef9', + 'ads': [{ + 'currency': 'BRL', + 'creativeId': '12334', + 'cpm': 1.9, + 'ttl': 300, + 'netRevenue': false, + 'ad': '', + 'width': 300, + 'height': 250, + 'bidId': '26df49c6447b82', + 'mediaType': 'banner' + }, { + 'currency': 'BRL', + 'creativeId': '12335', + 'cpm': 1.99, + 'ttl': 300, + 'netRevenue': false, + 'ad': '', + 'width': 300, + 'height': 600, + 'bidId': '26df49c6447b82', + 'mediaType': 'banner' + }] + }, + 'headers': {} + }; + let bidRequest = {}; + + it('should return the correct bid response structure', function () { + let expectedResponse = [ + { + 'requestId': '2a21a2fc993ef9', + 'cpm': 1.9, + 'width': 300, + 'height': 250, + 'creativeId': '12335', + 'currency': 'BRL', + 'dealId': null, + 'mediaType': 'banner', + 'netRevenue': false, + 'ttl': 300, + 'ad': '' + } + ]; + let result = spec.interpretResponse(serverResponse, {bidRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('should corretly return an empty array of bidResponses if no ads were received', function () { + let emptyResponse = Object.assign({}, serverResponse); + emptyResponse.body.ads = []; + let result = spec.interpretResponse(emptyResponse, {bidRequest}); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + let syncOptions = { iframeEnabled: true }; + let serverResponses = [{ body: { trackingPixel: 'https://www.uol.com.br' } }, { body: { trackingPixel: 'http://www.dynad.net/' } }]; + + it('should return the two sync params for iframeEnabled bids with a trackingPixel response', function () { + expect(spec.getUserSyncs(syncOptions, serverResponses)).to.have.length(2); + }) + + it('should not return any sync params if iframe is disabled or no trackingPixel is received', function () { + let cloneOptions = Object.assign({}, syncOptions); + delete cloneOptions.iframeEnabled; + expect(spec.getUserSyncs(cloneOptions, serverResponses)).to.have.length(0); + + let cloneResponses = Object.assign({}, serverResponses); + delete cloneResponses[0].body.trackingPixel; + delete cloneResponses[1].body.trackingPixel; + expect(spec.getUserSyncs(syncOptions, cloneResponses)).to.have.length(0); + + expect(spec.getUserSyncs(cloneOptions, cloneResponses)).to.have.length(0); + }) + }); +}); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js new file mode 100644 index 00000000000..5bbd21a3c12 --- /dev/null +++ b/test/spec/modules/userId_spec.js @@ -0,0 +1,712 @@ +import {attachIdSystem, init, requestBidsHook, setSubmoduleRegistry, syncDelay} from 'modules/userId/index.js'; +import {config} from 'src/config'; +import * as utils from 'src/utils'; +import {unifiedIdSubmodule} from 'modules/userId/unifiedIdSystem'; +import {pubCommonIdSubmodule} from 'modules/userId/pubCommonIdSystem'; +import {id5IdSubmodule} from 'modules/id5IdSystem'; +import {identityLinkSubmodule} from 'modules/identityLinkIdSystem'; + +let assert = require('chai').assert; +let expect = require('chai').expect; +let events = require('src/events'); +let constants = require('src/constants.json'); +const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; + +describe('User ID', function() { + function getConfigMock(configArr1, configArr2, configArr3, configArr4) { + return { + userSync: { + syncDelay: 0, + userIds: [ + (configArr1 && configArr1.length >= 3) ? getStorageMock.apply(null, configArr1) : null, + (configArr2 && configArr2.length >= 3) ? getStorageMock.apply(null, configArr2) : null, + (configArr3 && configArr3.length >= 3) ? getStorageMock.apply(null, configArr3) : null, + (configArr4 && configArr4.length >= 3) ? getStorageMock.apply(null, configArr4) : null + ].filter(i => i)} + } + } + function getStorageMock(name = 'pubCommonId', key = 'pubcid', type = 'cookie', expires = 30, refreshInSeconds) { + return { name: name, storage: { name: key, type: type, expires: expires, refreshInSeconds: refreshInSeconds } } + } + function getConfigValueMock(name, value) { + return { + userSync: { syncDelay: 0, userIds: [{ name: name, value: value }] } + } + } + + function getAdUnitMock(code = 'adUnit-code') { + return { + code, + mediaTypes: {banner: {}, native: {}}, + sizes: [[300, 200], [300, 600]], + bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] + }; + } + + function addConfig(cfg, name, value) { + if (cfg && cfg.userSync && cfg.userSync.userIds) { + cfg.userSync.userIds.forEach(element => { + if (element[name] !== undefined) { element[name] = Object.assign(element[name], value); } else { element[name] = value; } + }); + } + + return cfg; + } + + before(function() { + utils.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); + localStorage.removeItem('_pbjs_id_optout'); + localStorage.removeItem('_pubcid_optout'); + }); + + describe('Decorate Ad Units', function() { + beforeEach(function() { + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('pubcid_alt', 'altpubcid200000', (new Date(Date.now() + 5000).toUTCString())); + sinon.spy(utils, 'setCookie'); + }); + + afterEach(function () { + $$PREBID_GLOBAL$$.requestBids.removeAll(); + config.resetConfig(); + utils.setCookie.restore(); + }); + + after(function() { + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('pubcid_alt', '', EXPIRED_COOKIE_DATE); + }); + + it('Check same cookie behavior', function () { + let adUnits1 = [getAdUnitMock()]; + let adUnits2 = [getAdUnitMock()]; + let innerAdUnits1; + let innerAdUnits2; + + let pubcid = utils.getCookie('pubcid'); + expect(pubcid).to.be.null; // there should be no cookie initially + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + + requestBidsHook(config => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); + pubcid = utils.getCookie('pubcid'); // cookies is created after requestbidHook + + innerAdUnits1.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal(pubcid); + }); + }); + + requestBidsHook(config => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); + assert.deepEqual(innerAdUnits1, innerAdUnits2); + }); + + it('Check different cookies', function () { + let adUnits1 = [getAdUnitMock()]; + let adUnits2 = [getAdUnitMock()]; + let innerAdUnits1; + let innerAdUnits2; + let pubcid1; + let pubcid2; + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + requestBidsHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); + pubcid1 = utils.getCookie('pubcid'); // get first cookie + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); // erase cookie + + innerAdUnits1.forEach((unit) => { + unit.bids.forEach((bid) => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal(pubcid1); + }); + }); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + requestBidsHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); + + pubcid2 = utils.getCookie('pubcid'); // get second cookie + + innerAdUnits2.forEach((unit) => { + unit.bids.forEach((bid) => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal(pubcid2); + }); + }); + + expect(pubcid1).to.not.equal(pubcid2); + }); + + it('Use existing cookie', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie'])); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + innerAdUnits.forEach((unit) => { + unit.bids.forEach((bid) => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('altpubcid200000'); + }); + }); + // Because the cookie exists already, there should be no setCookie call by default + expect(utils.setCookie.callCount).to.equal(0); + }); + + it('Extend cookie', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customConfig = getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie']); + customConfig = addConfig(customConfig, 'params', {extend: true}); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customConfig); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + innerAdUnits.forEach((unit) => { + unit.bids.forEach((bid) => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('altpubcid200000'); + }); + }); + // Because extend is true, the cookie will be updated even if it exists already + expect(utils.setCookie.callCount).to.equal(1); + }); + + it('Disable auto create', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customConfig = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + customConfig = addConfig(customConfig, 'params', {create: false}); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customConfig); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + innerAdUnits.forEach((unit) => { + unit.bids.forEach((bid) => { + expect(bid).to.not.have.deep.nested.property('userId.pubcid'); + }); + }); + expect(utils.setCookie.callCount).to.equal(0); + }); + }); + + describe('Opt out', function () { + before(function () { + utils.setCookie('_pbjs_id_optout', '1', (new Date(Date.now() + 5000).toUTCString())); + }); + + beforeEach(function () { + sinon.stub(utils, 'logInfo'); + }); + + afterEach(function () { + // removed cookie + utils.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); + $$PREBID_GLOBAL$$.requestBids.removeAll(); + utils.logInfo.restore(); + config.resetConfig(); + }); + + after(function () { + utils.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); + }); + + it('fails initialization if opt out cookie exists', function () { + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - opt-out cookie found, exit module'); + }); + + it('initializes if no opt out cookie exists', function () { + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); + }); + }); + + describe('Handle variations of config values', function () { + beforeEach(function () { + sinon.stub(utils, 'logInfo'); + }); + + afterEach(function () { + $$PREBID_GLOBAL$$.requestBids.removeAll(); + utils.logInfo.restore(); + config.resetConfig(); + }); + + it('handles config with no usersync object', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig({}); + // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' + expect(typeof utils.logInfo.args[0]).to.equal('undefined'); + }); + + it('handles config with empty usersync object', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig({ usersync: {} }); + expect(typeof utils.logInfo.args[0]).to.equal('undefined'); + }); + + it('handles config with usersync and userIds that are empty objs', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig({ + usersync: { + userIds: [{}] + } + }); + expect(typeof utils.logInfo.args[0]).to.equal('undefined'); + }); + + it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig({ + usersync: { + userIds: [{ + name: '', + value: { test: '1' } + }, { + name: 'foo', + value: { test: '1' } + }] + } + }); + expect(typeof utils.logInfo.args[0]).to.equal('undefined'); + }); + + it('config with 1 configuration should create 1 submodule', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); + + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); + }); + + it('config with 4 configurations should result in 4 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig({ + usersync: { + syncDelay: 0, + userIds: [{ + name: 'pubCommonId', value: {'pubcid': '11111'} + }, { + name: 'unifiedId', + storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'id5Id', + storage: { name: 'id5id', type: 'cookie' } + }, { + name: 'identityLink', + storage: { name: 'idl_env', type: 'cookie' } + }] + } + }); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 4 submodules'); + }); + + it('config syncDelay updates module correctly', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig({ + usersync: { + syncDelay: 99, + userIds: [{ + name: 'unifiedId', + storage: { name: 'unifiedid', type: 'cookie' } + }] + } + }); + expect(syncDelay).to.equal(99); + }); + }); + + describe('Request bids hook appends userId to bid objs in adapters', function() { + let adUnits; + + beforeEach(function() { + adUnits = [getAdUnitMock()]; + }); + + it('test hook from pubcommonid cookie', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + }); + }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook from pubcommonid config value object', function(done) { + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigValueMock('pubCommonId', {'pubcidvalue': 'testpubcidvalue'})); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcidvalue'); + expect(bid.userId.pubcidvalue).to.equal('testpubcidvalue'); + }); + }); + done(); + }, {adUnits}); + }); + + it('test hook from pubcommonid html5', function(done) { + // simulate existing browser local storage values + localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); + localStorage.setItem('unifiedid_alt_exp', ''); + + setSubmoduleRegistry([unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['unifiedId', 'unifiedid_alt', 'html5'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('testunifiedid_alt'); + }); + }); + localStorage.removeItem('unifiedid_alt'); + localStorage.removeItem('unifiedid_alt_exp'); + done(); + }, {adUnits}); + }); + + it('test hook from identityLink html5', function(done) { + // simulate existing browser local storage values + localStorage.setItem('idl_env', 'AiGNC8Z5ONyZKSpIPf'); + localStorage.setItem('idl_env_exp', ''); + + setSubmoduleRegistry([identityLinkSubmodule]); + init(config); + config.setConfig(getConfigMock(['identityLink', 'idl_env', 'html5'])); + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + }); + }); + localStorage.removeItem('idl_env'); + localStorage.removeItem('idl_env_exp'); + done(); + }, {adUnits}); + }); + + it('test hook from identityLink cookie', function(done) { + utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 100000).toUTCString())); + + setSubmoduleRegistry([identityLinkSubmodule]); + init(config); + config.setConfig(getConfigMock(['identityLink', 'idl_env', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + }); + }); + utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook from id5id cookies when refresh needed', function(done) { + // simulate existing browser local storage values + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('id5id_last', (new Date(Date.now() - 7200 * 1000)).toUTCString(), (new Date(Date.now() + 5000).toUTCString())); + + sinon.stub(utils, 'logError'); // getId should failed with a logError as it has no partnerId + + setSubmoduleRegistry([id5IdSubmodule]); + init(config); + config.setConfig(getConfigMock(['id5Id', 'id5id', 'cookie', 10, 3600])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + }); + }); + sinon.assert.calledOnce(utils.logError); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + utils.logError.restore(); + done(); + }, {adUnits}); + }); + + it('test hook from id5id value-based config', function(done) { + setSubmoduleRegistry([id5IdSubmodule]); + init(config); + config.setConfig(getConfigValueMock('id5Id', {'id5id': 'testid5id'})); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + }); + }); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId and id5Id have data to pass', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'], + ['identityLink', 'idl_env', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // also check that UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('testunifiedid'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + // check that identityLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + }); + }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId and id5Id have their modules added before and after init', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); + + setSubmoduleRegistry([]); + + // attaching before init + attachIdSystem(pubCommonIdSubmodule); + + init(config); + + // attaching after init + attachIdSystem(unifiedIdSubmodule); + attachIdSystem(id5IdSubmodule); + attachIdSystem(identityLinkSubmodule); + + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'], + ['identityLink', 'idl_env', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // also check that UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + // also check that identityLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + }); + }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('should add new id system ', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule]); + init(config); + + config.setConfig({ + usersync: { + syncDelay: 0, + userIds: [{ + name: 'pubCommonId', storage: { name: 'pubcid', type: 'cookie' } + }, { + name: 'unifiedId', storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'id5Id', storage: { name: 'id5id', type: 'cookie' } + }, { + name: 'identityLink', storage: { name: 'idl_env', type: 'cookie' } + }, { + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } + }] + } + }); + + // Add new submodule named 'mockId' + attachIdSystem({ + name: 'mockId', + decode: function(value) { + return { + 'mid': value['MOCKID'] + }; + }, + getId: function(params, storedId) { + if (storedId) return {}; + return {id: {'MOCKID': '1234'}}; + } + }); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // check PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // check UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + // also check that identityLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + // check MockId data was copied to bid + expect(bid).to.have.deep.nested.property('userId.mid'); + expect(bid.userId.mid).to.equal('123456778'); + }); + }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + utils.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + }); + + describe('callbacks at the end of auction', function() { + let xhr; + let requests; + + beforeEach(function() { + requests = []; + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + sinon.stub(events, 'getEvents').returns([]); + sinon.stub(utils, 'triggerPixel'); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + }); + + afterEach(function() { + xhr.restore(); + events.getEvents.restore(); + utils.triggerPixel.restore(); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + }); + + it('pubcid callback with url', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie']); + customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url'}); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(utils.triggerPixel.called).to.be.false; + events.emit(constants.EVENTS.AUCTION_END, {}); + expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + }); + + it('unifiedid callback with url', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); + addConfig(customCfg, 'params', {url: '/any/unifiedid/url'}); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(requests).to.be.empty; + events.emit(constants.EVENTS.AUCTION_END, {}); + expect(requests[0].url).to.equal('/any/unifiedid/url'); + }); + + it('unifiedid callback with partner', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); + addConfig(customCfg, 'params', {partner: 'rubicon'}); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(requests).to.be.empty; + events.emit(constants.EVENTS.AUCTION_END, {}); + expect(requests[0].url).to.equal('//match.adsrvr.org/track/rid?ttd_pid=rubicon&fmt=json'); + }); + }); +}); diff --git a/test/spec/modules/vertamediaBidAdapter_spec.js b/test/spec/modules/vertamediaBidAdapter_spec.js index 271f1f2d04a..fefa8e446ed 100644 --- a/test/spec/modules/vertamediaBidAdapter_spec.js +++ b/test/spec/modules/vertamediaBidAdapter_spec.js @@ -96,45 +96,45 @@ const displayEqResponse = [{ cpm: 0.9 }]; -describe('vertamediaBidAdapter', () => { +describe('vertamediaBidAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - it('should return true when required params found', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(VIDEO_REQUEST)).to.equal(12345); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, VIDEO_REQUEST); delete bid.params; expect(spec.isBidRequestValid(bid)).to.equal(undefined); }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let videoBidRequests = [VIDEO_REQUEST]; let dispalyBidRequests = [DISPLAY_REQUEST]; const displayRequest = spec.buildRequests(dispalyBidRequests, {}); const videoRequest = spec.buildRequests(videoBidRequests, {}); - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { expect(videoRequest.method).to.equal('GET'); expect(displayRequest.method).to.equal('GET'); }); - it('sends bid request to correct ENDPOINT', () => { + it('sends bid request to correct ENDPOINT', function () { expect(videoRequest.url).to.equal(ENDPOINT); expect(displayRequest.url).to.equal(ENDPOINT); }); - it('sends correct video bid parameters', () => { + it('sends correct video bid parameters', function () { const bid = Object.assign({}, videoRequest.data); delete bid.domain; @@ -148,7 +148,7 @@ describe('vertamediaBidAdapter', () => { expect(bid).to.deep.equal(eq); }); - it('sends correct display bid parameters', () => { + it('sends correct display bid parameters', function () { const bid = Object.assign({}, displayRequest.data); delete bid.domain; @@ -163,18 +163,18 @@ describe('vertamediaBidAdapter', () => { }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let serverResponse; let bidderRequest; let eqResponse; - afterEach(() => { + afterEach(function () { serverResponse = null; bidderRequest = null; eqResponse = null; }); - it('should get correct video bid response', () => { + it('should get correct video bid response', function () { serverResponse = SERVER_VIDEO_RESPONSE; bidderRequest = videoBidderRequest; eqResponse = videoEqResponse; @@ -182,7 +182,7 @@ describe('vertamediaBidAdapter', () => { bidServerResponseCheck(); }); - it('should get correct display bid response', () => { + it('should get correct display bid response', function () { serverResponse = SERVER_DISPLAY_RESPONSE; bidderRequest = displayBidderRequest; eqResponse = displayEqResponse; @@ -203,13 +203,13 @@ describe('vertamediaBidAdapter', () => { expect(noBidResult.length).to.equal(0); } - it('handles video nobid responses', () => { + it('handles video nobid responses', function () { bidderRequest = videoBidderRequest; nobidServerResponseCheck(); }); - it('handles display nobid responses', () => { + it('handles display nobid responses', function () { bidderRequest = displayBidderRequest; nobidServerResponseCheck(); diff --git a/test/spec/modules/vertozBidAdapter_spec.js b/test/spec/modules/vertozBidAdapter_spec.js index a84fc4847f5..1eb85b6b566 100644 --- a/test/spec/modules/vertozBidAdapter_spec.js +++ b/test/spec/modules/vertozBidAdapter_spec.js @@ -4,16 +4,16 @@ import { newBidder } from 'src/adapters/bidderFactory'; const BASE_URI = '//hb.vrtzads.com/vzhbidder/bid?'; -describe('VertozAdapter', () => { +describe('VertozAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'vertoz', 'params': { @@ -26,11 +26,11 @@ describe('VertozAdapter', () => { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -40,7 +40,7 @@ describe('VertozAdapter', () => { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': 'vertoz', @@ -55,14 +55,14 @@ describe('VertozAdapter', () => { } ]; - it('sends bid request to ENDPOINT via POST', () => { + it('sends bid request to ENDPOINT via POST', function () { const request = spec.buildRequests(bidRequests)[0]; expect(request.url).to.equal(BASE_URI); expect(request.method).to.equal('POST'); }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { let response = { 'vzhPlacementId': 'VZ-HB-B784382V6C6G3C', 'bid': '76021e56-adaf-4114-b68d-ccacd1b3e551_1', @@ -75,7 +75,7 @@ describe('VertozAdapter', () => { 'statusText': 'Vertoz:Success' }; - it('should get correct bid response', () => { + it('should get correct bid response', function () { let expectedResponse = [ { 'requestId': '44b3fcfd24aa93', @@ -97,7 +97,7 @@ describe('VertozAdapter', () => { expect(result[0].cpm).to.not.equal(null); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let response = { 'vzhPlacementId': 'VZ-HB-I617046VBGE3EH', 'slotBidId': 'f00412ac86b79', diff --git a/test/spec/modules/viBidAdapter_spec.js b/test/spec/modules/viBidAdapter_spec.js index e8b0fbcc4b2..b12d534ff02 100644 --- a/test/spec/modules/viBidAdapter_spec.js +++ b/test/spec/modules/viBidAdapter_spec.js @@ -1,139 +1,894 @@ -import { expect } from 'chai'; -import { spec } from 'modules/viBidAdapter'; -import { newBidder } from 'src/adapters/bidderFactory'; - -const ENDPOINT = `//pb.vi-serve.com/prebid/bid`; - -describe('viBidAdapter', function() { - newBidder(spec); - - describe('isBidRequestValid', () => { - let bid = { - 'bidder': 'vi', - 'params': { - 'pubId': 'sb_test', - 'lang': 'en-US', - 'cat': 'IAB1', - 'bidFloor': 0.05 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [320, 480] - ], - 'bidId': '29b891ad542377', - 'bidderRequestId': '1dc9a08206a57b', - 'requestId': '24176695-e3f0-44db-815b-ed97cf5ad49b', - 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '474da635-9cf0-4188-a3d9-58961be8f905' - }; +import { + ratioToPercentageCeil, + merge, + getDocumentHeight, + getOffset, + getWindowParents, + getRectCuts, + getTopmostReachableWindow, + topDocumentIsReachable, + isInsideIframe, + isInsideSafeframe, + getIframeType, + getFrameElements, + getElementCuts, + getInViewRatio, + getMayBecomeVisible, + getInViewPercentage, + getInViewRatioInsideTopFrame, + getOffsetTopDocument, + getOffsetTopDocumentPercentage, + getOffsetToView, + getOffsetToViewPercentage, + area, + get, + getViewabilityDescription, + mergeArrays +} from 'modules/viBidAdapter'; - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); +describe('ratioToPercentageCeil', () => { + it('1 converts to percentage', () => + expect(ratioToPercentageCeil(0.01)).to.equal(1)); + it('2 converts to percentage', () => + expect(ratioToPercentageCeil(0.00000000001)).to.equal(1)); + it('3 converts to percentage', () => + expect(ratioToPercentageCeil(0.5)).to.equal(50)); + it('4 converts to percentage', () => + expect(ratioToPercentageCeil(1)).to.equal(100)); + it('5 converts to percentage', () => + expect(ratioToPercentageCeil(0.99)).to.equal(99)); + it('6 converts to percentage', () => + expect(ratioToPercentageCeil(0.990000000000001)).to.equal(100)); +}); - it('should return false when pubId not passed', () => { - bid.params.pubId = undefined; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); +describe('merge', () => { + it('merges two objects', () => { + expect( + merge({ a: 1, b: 2, d: 0 }, { a: 2, b: 2, c: 3 }, (a, b) => a + b) + ).to.deep.equal({ a: 3, b: 4, c: 3, d: 0 }); }); +}); - describe('buildRequests', () => { - let bidRequests = [{ - 'bidder': 'vi', - 'params': { - 'pubId': 'sb_test', - 'lang': 'en-US', - 'cat': 'IAB1', - 'bidFloor': 0.05 +describe('getDocumentHeight', () => { + [ + { + curDocument: { + body: { + clientHeight: 0, + offsetHeight: 0, + scrollHeight: 0 + }, + documentElement: { + clientHeight: 0, + offsetHeight: 0, + scrollHeight: 0 + } }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [320, 480] - ], - 'bidId': '29b891ad542377', - 'bidderRequestId': '1dc9a08206a57b', - 'requestId': '24176695-e3f0-44db-815b-ed97cf5ad49b', - 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '474da635-9cf0-4188-a3d9-58961be8f905' - }]; - - const request = spec.buildRequests(bidRequests); - - it('POST bid request to vi', () => { - expect(request.method).to.equal('POST'); - }); + expected: 0 + }, + { + curDocument: { + body: { + clientHeight: 0, + offsetHeight: 13, + scrollHeight: 24 + }, + documentElement: { + clientHeight: 0, + offsetHeight: 0, + scrollHeight: 0 + } + }, + expected: 24 + }, + { + curDocument: { + body: { + clientHeight: 0, + offsetHeight: 13, + scrollHeight: 24 + }, + documentElement: { + clientHeight: 100, + offsetHeight: 50, + scrollHeight: 30 + } + }, + expected: 100 + } + ].forEach(({ curDocument, expected }) => + expect(getDocumentHeight(curDocument)).to.be.equal(expected) + ); +}); - it('check endpoint URL', () => { - expect(request.url).to.equal(ENDPOINT) - }); +describe('getOffset', () => { + [ + { + element: { + ownerDocument: { + defaultView: { + pageXOffset: 0, + pageYOffset: 0 + } + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }, + expected: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } + ].forEach(({ description, element, expected }, i) => + it( + 'returns element offsets from the document edges (including scroll): ' + + i, + () => expect(getOffset(element)).to.be.deep.equal(expected) + ) + ); + it('Throws when there is no window', () => + expect( + getOffset.bind(null, { + ownerDocument: { + defaultView: null + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.throw()); +}); + +describe('getWindowParents', () => { + const win = {}; + win.top = win; + win.parent = win; + const win1 = { top: win, parent: win }; + const win2 = { top: win, parent: win1 }; + const win3 = { top: win, parent: win2 }; + + it('get parents up to the top', () => + expect(getWindowParents(win3)).to.be.deep.equal([win2, win1, win])); +}); + +describe('getTopmostReachableWindow', () => { + const win = {}; + win.top = win; + win.parent = win; + const win1 = { top: win, parent: win }; + const win2 = { top: win, parent: win1 }; + const win3 = { top: win, parent: win2 }; + + it('get parents up to the top', () => + expect(getTopmostReachableWindow(win3)).to.be.equal(win)); +}); + +const topWindow = { document, frameElement: 0 }; +topWindow.top = topWindow; +topWindow.parent = topWindow; +const topFrameElement = { + ownerDocument: { + defaultView: topWindow + } +}; +const frameWindow1 = { + top: topWindow, + parent: topWindow, + frameElement: topFrameElement +}; +const frameElement1 = { + ownerDocument: { + defaultView: frameWindow1 + } +}; +const frameWindow2 = { + top: topWindow, + parent: frameWindow1, + frameElement: frameElement1 +}; +const frameElement2 = { + ownerDocument: { + defaultView: frameWindow2 + } +}; +const frameWindow3 = { + top: topWindow, + parent: frameWindow2, + frameElement: frameElement2 +}; + +describe('topDocumentIsReachable', () => { + it('returns true if it no inside iframe', () => + expect(topDocumentIsReachable(topWindow)).to.be.true); + it('returns true if it can access top document', () => + expect(topDocumentIsReachable(frameWindow3)).to.be.true); +}); + +describe('isInsideIframe', () => { + it('returns true if window !== window.top', () => + expect(isInsideIframe(topWindow)).to.be.false); + it('returns true if window !== window.top', () => + expect(isInsideIframe(frameWindow1)).to.be.true); +}); + +const safeframeWindow = { $sf: {} }; + +describe('isInsideSafeframe', () => { + it('returns true if top window is not reachable and window.$sf is defined', () => + expect(isInsideSafeframe(safeframeWindow)).to.be.true); +}); + +const hostileFrameWindow = {}; + +describe('getIframeType', () => { + it('returns undefined when is not inside iframe', () => + expect(getIframeType(topWindow)).to.be.undefined); + it("returns 'safeframe' when inside sf", () => + expect(getIframeType(safeframeWindow)).to.be.equal('safeframe')); + it("returns 'friendly' when inside friendly iframe and can reach top window", () => + expect(getIframeType(frameWindow3)).to.be.equal('friendly')); + it("returns 'nonfriendly' when cannot get top window", () => + expect(getIframeType(hostileFrameWindow)).to.be.equal('nonfriendly')); +}); + +describe('getFrameElements', () => { + it('it returns a list iframe elements up to the top, topmost goes first', () => { + expect(getFrameElements(frameWindow3)).to.be.deep.equal([ + topFrameElement, + frameElement1, + frameElement2 + ]); + }); +}); + +describe('area', () => { + it('calculates area', () => expect(area(10, 10)).to.be.equal(100)); + it('calculates area', () => + expect( + area(10, 10, { top: -2, left: -2, bottom: 0, right: 0 }) + ).to.be.equal(64)); +}); + +describe('getElementCuts', () => { + it('returns element cuts', () => + expect( + getElementCuts({ + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + }, + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + } + }) + ).to.be.deep.equal({ + top: 0, + right: 0, + bottom: 0, + left: 0 + })); +}); + +describe('getInViewRatio', () => { + it('returns inViewRatio', () => + expect( + getInViewRatio({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.deep.equal(1)); +}); + +describe('getMayBecomeVisible', () => { + it('returns true if not inside iframe of visible inside the iframe', () => + expect( + getMayBecomeVisible({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.true); +}); + +describe('getInViewPercentage', () => { + it('returns inViewRatioPercentage', () => + expect( + getInViewPercentage({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.deep.equal(100)); +}); + +describe('getInViewRatioInsideTopFrame', () => { + it('returns inViewRatio', () => + expect( + getInViewRatioInsideTopFrame({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.deep.equal(1)); +}); + +describe('getOffsetTopDocument', () => { + it('returns offset relative to the top document', () => + expect( + getOffsetTopDocument({ + ownerDocument: { + defaultView: { + pageXOffset: 0, + pageYOffset: 0 + } + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.deep.equal({ + top: 0, + right: 0, + bottom: 0, + left: 0 + })); +}); + +describe('getOffsetTopDocumentPercentage', () => { + it('returns offset from the top as a percentage of the page length', () => { + const topWindow = { + pageXOffset: 0, + pageYOffset: 100, + document: { + body: { + clientHeight: 1000 + } + } + }; + topWindow.top = topWindow; + topWindow.parent = topWindow; + expect( + getOffsetTopDocumentPercentage({ + ownerDocument: { + defaultView: topWindow + }, + getBoundingClientRect: () => ({ + top: 100, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.equal(20); + }); + it('throws when cannot get window', () => + expect(() => + getOffsetTopDocumentPercentage({ + ownerDocument: {} + }) + ).to.throw()); + it("throw when top document isn't reachable", () => { + const topWindow = { ...topWindow, document: null }; + expect(() => + getOffsetTopDocumentPercentage({ + ownerDocument: { + defaultView: { + top: topWindow + } + } + }) + ).to.throw(); }); +}); + +describe('getOffsetToView', () => { + expect( + getOffsetToView({ + ownerDocument: { + defaultView: { + scrollY: 0, + pageXOffset: 0, + pageYOffset: 0 + } + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.equal(0); +}); - describe('buildRequests can handle size in 1-dim array', () => { - let bidRequests = [{ - 'bidder': 'vi', - 'params': { - 'pubId': 'sb_test', - 'lang': 'en-US', - 'cat': 'IAB1', - 'bidFloor': 0.05 +describe('getOffsetToView', () => { + expect( + getOffsetToViewPercentage({ + ownerDocument: { + defaultView: { + scrollY: 0, + pageXOffset: 0, + pageYOffset: 0, + document: { + body: { + clientHeight: 1000 + } + } + } }, - 'adUnitCode': 'adunit-code', - 'sizes': [320, 480], - 'bidId': '29b891ad542377', - 'bidderRequestId': '1dc9a08206a57b', - 'requestId': '24176695-e3f0-44db-815b-ed97cf5ad49b', - 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '474da635-9cf0-4188-a3d9-58961be8f905' - }]; - - const request = spec.buildRequests(bidRequests); - - it('POST bid request to vi', () => { - expect(request.method).to.equal('POST'); + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.equal(0); +}); + +describe('getCuts without vCuts', () => { + const cases = { + 'completely in view 1': { + top: 0, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + 'completely in view 2': { + top: 100, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + 'half cut from the top': { + top: -200, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'half cut from the bottom': { + top: 0, + bottom: 600, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'quarter cut from top and bottom': { + top: -25, + bottom: 75, + right: 200, + left: 0, + vw: 300, + vh: 50, + expected: { + top: -25, + right: 0, + bottom: -25, + left: 0 + } + }, + 'out of view top': { + top: -200, + bottom: -5, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'out of view bottom': { + top: 250, + bottom: 500, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'half cut from left': { + top: 0, + bottom: 200, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: -200 + } + }, + 'half cut from left and top': { + top: -100, + bottom: 100, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: -100, + right: 0, + bottom: 0, + left: -200 + } + }, + 'quarter cut from all sides': { + top: -100, + left: -100, + bottom: 300, + right: 300, + vw: 200, + vh: 200, + expected: { + top: -100, + right: -100, + bottom: -100, + left: -100 + } + } + }; + for (let descr in cases) { + it(descr, () => { + const { expected, vh, vw, ...rect } = cases[descr]; + expect(getRectCuts(rect, vh, vw)).to.deep.equal(expected); }); + } +}); - it('check endpoint URL', () => { - expect(request.url).to.equal(ENDPOINT) +describe('getCuts with vCuts', () => { + const cases = { + 'completely in view 1, half-cut viewport from top': { + top: 0, + right: 200, + bottom: 200, + left: 0, + vw: 200, + vh: 200, + vCuts: { + top: -100, + right: 0, + bottom: 0, + left: 0 + }, + expected: { + top: -100, + right: 0, + bottom: 0, + left: 0 + } + }, + 'completely in view 2, half-cut viewport from bottom': { + top: 100, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + vCuts: { + top: 0, + right: 0, + bottom: -150, + left: 0 + }, + expected: { + top: 0, + right: 0, + bottom: -50, + left: 0 + } + }, + 'half cut from the top, 1/3 viewport cut from the bottom': { + top: -200, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + vCuts: { + top: 0, + right: 0, + bottom: -100, + left: 0 + }, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'half cut from the bottom': { + top: 0, + bottom: 600, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'quarter cut from top and bottom': { + top: -25, + bottom: 75, + right: 200, + left: 0, + vw: 300, + vh: 50, + expected: { + top: -25, + right: 0, + bottom: -25, + left: 0 + } + }, + 'out of view top': { + top: -200, + bottom: -5, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'out of view bottom': { + top: 250, + bottom: 500, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'half cut from left': { + top: 0, + bottom: 200, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: -200 + } + }, + 'half cut from left and top': { + top: -100, + bottom: 100, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: -100, + right: 0, + bottom: 0, + left: -200 + } + }, + 'quarter cut from all sides': { + top: -100, + left: -100, + bottom: 300, + right: 300, + vw: 200, + vh: 200, + expected: { + top: -100, + right: -100, + bottom: -100, + left: -100 + } + } + }; + for (let descr in cases) { + it(descr, () => { + const { expected, vh, vw, vCuts, ...rect } = cases[descr]; + expect(getRectCuts(rect, vh, vw, vCuts)).to.deep.equal(expected); }); - }); + } +}); - describe('interpretResponse', () => { - let response = { - body: [{ - 'id': '29b891ad542377', - 'price': 0.1, - 'width': 320, - 'height': 480, - 'ad': '', - 'creativeId': 'dZsPGv' - }] - }; +describe('get', () => { + it('returns a property in a nested object 1', () => + expect(get(['a'], { a: 1 })).to.equal(1)); + it('returns a property in a nested object 2', () => + expect(get(['a', 'b'], { a: { b: 1 } })).to.equal(1)); + it('returns a property in a nested object 3', () => + expect(get(['a', 'b'], { a: { b: 1 } })).to.equal(1)); + it('returns undefined if property does not exist', () => + expect(get(['a', 'b'], { b: 1 })).to.equal(undefined)); + it('returns undefined if property does not exist', () => + expect(get(['a', 'b'], undefined)).to.equal(undefined)); + it('returns undefined if property does not exist', () => + expect(get(['a', 'b'], 1213)).to.equal(undefined)); + const DEFAULT = -5; + it('returns defaultValue if property does not exist', () => + expect(get(['a', 'b'], { b: 1 }, DEFAULT)).to.equal(DEFAULT)); + it('returns defaultValue if property does not exist', () => + expect(get(['a', 'b'], undefined, DEFAULT)).to.equal(DEFAULT)); + it('returns defaultValue if property does not exist', () => + expect(get(['a', 'b'], 1213, DEFAULT)).to.equal(DEFAULT)); + it('can work with arrays 1', () => expect(get([0, 1], [[1, 2]])).to.equal(2)); + it('can work with arrays 2', () => + expect(get([0, 'a'], [{ a: 42 }])).to.equal(42)); +}); - it('should get the correct bid response', () => { - let expectedResponse = [{ - 'requestId': '29b891ad542377', - 'cpm': 0.1, - 'width': 320, - 'height': 480, - 'creativeId': 'dZsPGv', - 'dealId': null, - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', - 'ad': decodeURIComponent(``), - 'ttl': 60000 - }]; - - let result = spec.interpretResponse(response); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); +describe('getViewabilityDescription', () => { + it('returns error when there is no element', () => { + expect(getViewabilityDescription(null)).to.deep.equal({ + error: 'no element' }); - - it('handles empty bid response', () => { - let response = { - body: [] - }; - let result = spec.interpretResponse(response); - expect(result.length).to.equal(0); + }); + it('returns only iframe type for nonfrienly iframe', () => { + expect( + getViewabilityDescription({ + ownerDocument: { + defaultView: {} + } + }) + ).to.deep.equal({ + iframeType: 'nonfriendly' + }); + }); + it('returns only iframe type for safeframe iframe', () => { + expect( + getViewabilityDescription({ + ownerDocument: { + defaultView: { + $sf: true + } + } + }) + ).to.deep.equal({ + iframeType: 'safeframe' }); }); }); + +describe('mergeSizes', () => { + it('merges provides arrays of tuples, leaving only unique', () => { + expect( + mergeArrays(x => x.join(','), [[1, 2], [2, 4]], [[1, 2]]) + ).to.deep.equal([[1, 2], [2, 4]]); + }); + it('merges provides arrays of tuples, leaving only unique', () => { + expect( + mergeArrays( + x => x.join(','), + [[1, 2], [2, 4]], + [[1, 2]], + [[400, 500], [500, 600]] + ) + ).to.deep.equal([[1, 2], [2, 4], [400, 500], [500, 600]]); + }); +}); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index d88e5a718ed..d9c08ad924c 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec as adapter, URL} from 'modules/vidazooBidAdapter'; import * as utils from 'src/utils'; + const BID = { 'bidId': '2d52001cabd527', 'params': { @@ -19,6 +20,12 @@ const BID = { 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a' }; +const BIDDER_REQUEST = { + 'gdprConsent': { + 'consentString': 'consent_string' + } +}; + const SERVER_RESPONSE = { body: { 'ad': '', @@ -47,31 +54,31 @@ const SYNC_OPTIONS = { 'pixelEnabled': true }; -describe('VidazooBidAdapter', () => { - describe('validtae spec', () => { - it('exists and is a function', () => { +describe('VidazooBidAdapter', function () { + describe('validtae spec', function () { + it('exists and is a function', function () { expect(adapter.isBidRequestValid).to.exist.and.to.be.a('function'); }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.buildRequests).to.exist.and.to.be.a('function'); }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.interpretResponse).to.exist.and.to.be.a('function'); }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.getUserSyncs).to.exist.and.to.be.a('function'); }); - it('exists and is a string', () => { + it('exists and is a string', function () { expect(adapter.code).to.exist.and.to.be.a('string'); }); }); - describe('validate bid requests', () => { - it('should require cId', () => { + describe('validate bid requests', function () { + it('should require cId', function () { const isValid = adapter.isBidRequestValid({ params: { pId: 'pid' @@ -80,7 +87,7 @@ describe('VidazooBidAdapter', () => { expect(isValid).to.be.false; }); - it('should require pId', () => { + it('should require pId', function () { const isValid = adapter.isBidRequestValid({ params: { cId: 'cid' @@ -89,7 +96,7 @@ describe('VidazooBidAdapter', () => { expect(isValid).to.be.false; }); - it('should validate correctly', () => { + it('should validate correctly', function () { const isValid = adapter.isBidRequestValid({ params: { cId: 'cid', @@ -100,21 +107,22 @@ describe('VidazooBidAdapter', () => { }); }); - describe('build requests', () => { + describe('build requests', function () { let sandbox; - before(() => { + before(function () { sandbox = sinon.sandbox.create(); sandbox.stub(utils, 'getTopWindowUrl').returns('http://www.greatsite.com'); sandbox.stub(Date, 'now').returns(1000); }); - it('should build request for each size', () => { - const requests = adapter.buildRequests([BID]); + it('should build request for each size', function () { + const requests = adapter.buildRequests([BID], BIDDER_REQUEST); expect(requests).to.have.length(2); expect(requests[0]).to.deep.equal({ method: 'GET', url: `${URL}/prebid/59db6b3b4ffaa70004f45cdc`, data: { + consent: 'consent_string', width: '300', height: '250', url: 'http://www.greatsite.com', @@ -130,6 +138,7 @@ describe('VidazooBidAdapter', () => { method: 'GET', url: `${URL}/prebid/59db6b3b4ffaa70004f45cdc`, data: { + consent: 'consent_string', width: '300', height: '600', url: 'http://www.greatsite.com', @@ -143,28 +152,28 @@ describe('VidazooBidAdapter', () => { }); }); - after(() => { + after(function () { sandbox.restore(); }); }); - describe('interpret response', () => { - it('should return empty array when there is no response', () => { + describe('interpret response', function () { + it('should return empty array when there is no response', function () { const responses = adapter.interpretResponse(null); expect(responses).to.be.empty; }); - it('should return empty array when there is no ad', () => { + it('should return empty array when there is no ad', function () { const responses = adapter.interpretResponse({price: 1, ad: ''}); expect(responses).to.be.empty; }); - it('should return empty array when there is no price', () => { + it('should return empty array when there is no price', function () { const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); expect(responses).to.be.empty; }); - it('should return an array of interpreted responses', () => { + it('should return an array of interpreted responses', function () { const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST); expect(responses).to.have.length(1); expect(responses[0]).to.deep.equal({ @@ -180,7 +189,7 @@ describe('VidazooBidAdapter', () => { }); }); - it('should take default TTL', () => { + it('should take default TTL', function () { const serverResponse = utils.deepClone(SERVER_RESPONSE); delete serverResponse.body.exp; const responses = adapter.interpretResponse(serverResponse, REQUEST); diff --git a/test/spec/modules/videoNowBidAdapter_spec.js b/test/spec/modules/videoNowBidAdapter_spec.js new file mode 100644 index 00000000000..337960c6edd --- /dev/null +++ b/test/spec/modules/videoNowBidAdapter_spec.js @@ -0,0 +1,566 @@ +import { expect } from 'chai' +import { spec } from 'modules/videoNowBidAdapter' +import { replaceAuctionPrice } from '../../../src/utils' + +const placementId = 'div-gpt-ad-1438287399331-1' +const LS_ITEM_NAME = 'videonow-config' + +const getValidServerResponse = () => { + const serverResponse = { + body: { + id: '111-111', + bidid: '2955a162-699e-4811-ce88-5c3ac973e73c', + cur: 'RUB', + seatbid: [ + { + bid: [ + { + id: 'e3bf2b82e3e9485113fad6c9b27f8768.1', + impid: '1', + price: 10.97, + nurl: 'http://localhost:8086/event/nurl', + netRevenue: false, + ttl: 800, + adm: '', + crid: 'e3bf2b82e3e9485113fad6c9b27f8768.1', + h: 640, + w: 480, + ext: { + init: 'http://localhost:8086/vn_init.js', + module: { + min: 'http://localhost:8086/vn_module.js', + log: 'http://localhost:8086/vn_module.js?log=1' + }, + format: { + name: 'flyRoll', + }, + }, + + }, + ], + group: 0, + }, + ], + price: 10, + ext: { + placementId, + pixels: [ + 'http://localhost:8086/event/pxlcookiematching?uiid=1', + 'http://localhost:8086/event/pxlcookiematching?uiid=2', + ], + iframes: [ + 'http://localhost:8086/event/ifrcookiematching?uiid=1', + 'http://localhost:8086/event/ifrcookiematching?uiid=2', + ], + }, + }, + headers: {}, + } + + return JSON.parse(JSON.stringify(serverResponse)) +} + +describe('videonowAdapterTests', function() { + describe('bidRequestValidity', function() { + it('bidRequest with pId', function() { + expect(spec.isBidRequestValid({ + bidder: 'videonow', + params: { + pId: '86858', + }, + })).to.equal(true) + }) + + it('bidRequest without pId', function() { + expect(spec.isBidRequestValid({ + bidder: 'videonow', + params: { + nomater: 86858, + }, + })).to.equal(false) + + it('bidRequest is empty', function() { + expect(spec.isBidRequestValid({})).to.equal(false) + }) + + it('bidRequest is undefned', function() { + expect(spec.isBidRequestValid(undefined)).to.equal(false) + }) + }) + + describe('bidRequest', function() { + const validBidRequests = [ + { + bidder: 'videonow', + params: { + pId: '1', + placementId, + url: 'http://localhost:8086/bid?p=exists', + bidFloor: 10, + cur: 'RUB' + }, + crumbs: { + pubcid: 'feded041-35dd-4b54-979a-6d7805abfa75', + }, + mediaTypes: { + banner: { + sizes: [[640, 480], [320, 200]] + }, + }, + adUnitCode: 'test-ad', + transactionId: '676403c7-09c9-4b56-be82-e7cae81f40b9', + sizes: [[640, 480], [320, 200]], + bidId: '268c309f46390d', + bidderRequestId: '1dfdd514c36ef6', + auctionId: '4d523546-889a-4029-9a79-13d3c69f9922', + src: 'client', + bidRequestsCount: 1, + }, + ] + + const bidderRequest = { + bidderCode: 'videonow', + auctionId: '4d523546-889a-4029-9a79-13d3c69f9922', + bidderRequestId: '1dfdd514c36ef6', + bids: [ + { + bidder: 'videonow', + params: { + pId: '1', + placementId, + url: 'http://localhost:8086/bid', + bidFloor: 10, + cur: 'RUB', + }, + crumbs: { + pubcid: 'feded041-35dd-4b54-979a-6d7805abfa75', + }, + mediaTypes: { + banner: { + sizes: [[640, 480], [320, 200]], + }, + }, + adUnitCode: 'test-ad', + transactionId: '676403c7-09c9-4b56-be82-e7cae81f40b9', + sizes: [[640, 480], [320, 200]], + bidId: '268c309f46390d', + bidderRequestId: '1dfdd514c36ef6', + auctionId: '4d523546-889a-4029-9a79-13d3c69f9922', + src: 'client', + bidRequestsCount: 1, + }, + ], + auctionStart: 1565794308584, + timeout: 3000, + refererInfo: { + referer: 'http://localhost:8086/page', + reachedTop: true, + numIframes: 0, + stack: [ + 'http://localhost:8086/page', + ], + }, + start: 1565794308589, + } + + const requests = spec.buildRequests(validBidRequests, bidderRequest) + const request = (requests && requests.length && requests[0]) || {} + + it('bidRequest count', function() { + expect(requests.length).to.equal(1) + }) + + it('bidRequest method', function() { + expect(request.method).to.equal('POST') + }) + + it('bidRequest url', function() { + expect(request.url).to.equal('http://localhost:8086/bid?p=exists&profile_id=1') + }) + + it('bidRequest data', function() { + const data = request.data + expect(data.aid).to.be.eql(validBidRequests[0].params.aid) + expect(data.id).to.be.eql(validBidRequests[0].bidId) + expect(data.sizes).to.be.eql(validBidRequests[0].sizes) + }) + + describe('bidRequest advanced', function() { + const bidderRequestEmptyParamsAndExtParams = { + bidder: 'videonow', + params: { + pId: '1', + }, + ext: { + p1: 'ext1', + p2: 'ext2', + }, + } + + it('bidRequest count', function() { + const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) + expect(requests.length).to.equal(1) + }) + + it('bidRequest default url', function() { + const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) + const request = (requests && requests.length && requests[0]) || {} + expect(request.url).to.equal('https://bidder.videonow.ru/prebid?profile_id=1') + }) + + it('bidRequest default currency', function() { + const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) + const request = (requests && requests.length && requests[0]) || {} + const data = (request && request.data) || {} + expect(data.cur).to.equal('RUB') + }) + + it('bidRequest ext parameters ', function() { + const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) + const request = (requests && requests.length && requests[0]) || {} + const data = (request && request.data) || {} + expect(data['ext_p1']).to.equal('ext1') + expect(data['ext_p2']).to.equal('ext2') + }) + + it('bidRequest without params', function() { + const bidderReq = { + bidder: 'videonow', + } + const requests = spec.buildRequests([bidderReq], bidderRequest) + expect(requests.length).to.equal(1) + }) + }) + }) + + describe('onBidWon', function() { + const cpm = 10 + const nurl = 'http://fakedomain.nld?price=${AUCTION_PRICE}' + const imgSrc = replaceAuctionPrice(nurl, cpm) + const foundPixels = () => window.document.body.querySelectorAll(`img[src="${imgSrc}"]`) + + it('Should not create nurl pixel if bid is undefined', function() { + spec.onBidWon() + expect(foundPixels().length).to.equal(0) + }) + + it('Should not create nurl pixel if bid does not contains nurl', function() { + spec.onBidWon({}) + expect(foundPixels().length).to.equal(0) + }) + + it('Should create nurl pixel if bid nurl', function() { + spec.onBidWon({ nurl, cpm }) + expect(foundPixels().length).to.equal(1) + }) + }) + + describe('getUserSyncs', function() { + it('Should return an empty array if not get serverResponses', function() { + expect(spec.getUserSyncs({}).length).to.equal(0) + }) + + it('Should return an empty array if get serverResponses as empty array', function() { + expect(spec.getUserSyncs({}, []).length).to.equal(0) + }) + + it('Should return an empty array if serverResponses has no body', function() { + const serverResp = getValidServerResponse() + delete serverResp.body + const syncs = spec.getUserSyncs({}, [serverResp]) + expect(syncs.length).to.equal(0) + }) + + it('Should return an empty array if serverResponses has no ext', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.ext + const syncs = spec.getUserSyncs({}, [serverResp]) + expect(syncs.length).to.equal(0) + }) + + it('Should return an array', function() { + const serverResp = getValidServerResponse() + const syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [serverResp]) + expect(syncs.length).to.equal(4) + }) + + it('Should return pixels', function() { + const serverResp = getValidServerResponse() + const syncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [serverResp]) + expect(syncs.length).to.equal(2) + expect(syncs[0].type).to.equal('image') + expect(syncs[1].type).to.equal('image') + }) + + it('Should return iframes', function() { + const serverResp = getValidServerResponse() + const syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [serverResp]) + expect(syncs.length).to.equal(2) + expect(syncs[0].type).to.equal('iframe') + expect(syncs[1].type).to.equal('iframe') + }) + }) + + describe('interpretResponse', function() { + const bidRequest = { + method: 'POST', + url: 'http://localhost:8086/bid?profile_id=1', + data: { + id: '217b8ab59a18e8', + cpm: 10, + sizes: [[640, 480], [320, 200]], + cur: 'RUB', + placementId, + ref: 'http://localhost:8086/page', + }, + } + + it('Should have only one bid', function() { + const serverResponse = getValidServerResponse() + const result = spec.interpretResponse(serverResponse, bidRequest) + expect(result.length).to.equal(1) + }) + + it('Should have required keys', function() { + const serverResponse = getValidServerResponse() + const result = spec.interpretResponse(serverResponse, bidRequest) + const bid = serverResponse.body.seatbid[0].bid[0] + const res = result[0] + expect(res.requestId).to.be.eql(bidRequest.data.id) + expect(res.cpm).to.be.eql(bid.price) + expect(res.creativeId).to.be.eql(bid.crid) + expect(res.netRevenue).to.be.a('boolean') + expect(res.ttl).to.be.eql(bid.ttl) + expect(res.renderer).to.be.a('Object') + expect(res.renderer.render).to.be.a('function') + }) + + it('Should return an empty array if empty or no bids in response', function() { + expect(spec.interpretResponse({ body: '' }, {}).length).to.equal(0) + }) + + it('Should return an empty array if bidRequest\'s data is absent', function() { + const serverResponse = getValidServerResponse() + expect(spec.interpretResponse(serverResponse, undefined).length).to.equal(0) + }) + + it('Should return an empty array if bidRequest\'s data is not contains bidId ', function() { + const serverResponse = getValidServerResponse() + expect(spec.interpretResponse(serverResponse, { data: {} }).length).to.equal(0) + }) + + it('Should return an empty array if bidRequest\'s data bidId is undefined', function() { + const serverResponse = getValidServerResponse() + expect(spec.interpretResponse(serverResponse, { data: { id: null } }).length).to.equal(0) + }) + + it('Should return an empty array if serverResponse do not contains seatbid', function() { + expect(spec.interpretResponse({ body: {} }, bidRequest).length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s seatbid is empty', function() { + expect(spec.interpretResponse({ body: { seatbid: [] } }, bidRequest).length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s placementId is undefined', function() { + expect(spec.interpretResponse({ body: { seatbid: [1, 2] } }, bidRequest).length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s id in the bid is undefined', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].id + let res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s price in the bid is undefined', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].price + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s price in the bid is 0', function() { + const serverResp = getValidServerResponse() + serverResp.body.seatbid[0].bid[0].price = 0 + const res = spec.interpretResponse(serverResp, bidRequest) + + expect(res.length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s init in the bid\'s ext is undefined', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].ext.init + const res = spec.interpretResponse(serverResp, bidRequest) + + expect(res.length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s module in the bid\'s ext is undefined', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].ext.module + const res = spec.interpretResponse(serverResp, bidRequest) + + expect(res.length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s adm in the bid is undefined', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].adm + const res = spec.interpretResponse(serverResp, bidRequest) + + expect(res.length).to.equal(0) + }) + + it('Should return an empty array if serverResponse\'s the bid\'s ext is undefined', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].ext + const res = spec.interpretResponse(serverResp, bidRequest) + + expect(res.length).to.equal(0) + }) + + it('Default ttl is 300', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].ttl + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(1) + expect(res[0].ttl).to.equal(300) + }) + + it('Default netRevenue is true', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].netRevenue + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(1) + expect(res[0].netRevenue).to.be.true; + }) + + it('Default currency is RUB', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.cur + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(1) + expect(res[0].currency).to.equal('RUB') + }) + + describe('different module paths', function() { + beforeEach(function() { + window.localStorage && localStorage.setItem(LS_ITEM_NAME, '{}') + }) + + afterEach(function() { + const serverResp = getValidServerResponse() + const { module: { log, min }, init } = serverResp.body.seatbid[0].bid[0].ext + remove(init) + remove(log) + remove(min) + + function remove(src) { + if (!src) return + const d = document.querySelectorAll(`script[src^="${src}"]`) + d && d.length && Array.from(d).forEach(el => el && el.remove()) + } + }) + + it('should use prod module by default', function() { + const serverResp = getValidServerResponse() + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(1) + + const renderer = res[0].renderer + expect(renderer).to.be.an('object') + expect(renderer.url).to.equal(serverResp.body.seatbid[0].bid[0].ext.module.min) + }) + + it('should use "log" module if "prod" is not exists', function() { + const serverResp = getValidServerResponse() + delete serverResp.body.seatbid[0].bid[0].ext.module.min + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(1) + + const renderer = res[0].renderer + expect(renderer).to.be.an('object') + expect(renderer.url).to.equal(serverResp.body.seatbid[0].bid[0].ext.module.log) + }) + + it('should correct combine src for init', function() { + const serverResp = getValidServerResponse() + + const src = `${serverResp.body.seatbid[0].bid[0].ext.init}?profileId=1` + const placementElement = document.createElement('div') + placementElement.setAttribute('id', placementId) + + const resp = spec.interpretResponse(serverResp, bidRequest) + expect(resp.length).to.equal(1) + + const renderer = resp[0].renderer + expect(renderer).to.be.an('object') + + document.body.appendChild(placementElement) + + renderer.render() + + const res = document.querySelectorAll(`script[src="${src}"]`) + expect(res.length).to.equal(1) + }) + + it('should correct combine src for init if init url contains "?"', function() { + const serverResp = getValidServerResponse() + + serverResp.body.seatbid[0].bid[0].ext.init += '?div=1' + const src = `${serverResp.body.seatbid[0].bid[0].ext.init}&profileId=1` + + const placementElement = document.createElement('div') + placementElement.setAttribute('id', placementId) + + const resp = spec.interpretResponse(serverResp, bidRequest) + expect(resp.length).to.equal(1) + + const renderer = resp[0].renderer + expect(renderer).to.be.an('object') + + document.body.appendChild(placementElement) + + renderer.render() + + const res = document.querySelectorAll(`script[src="${src}"]`) + expect(res.length).to.equal(1) + }) + }) + + describe('renderer object', function() { + it('execute renderer.render() should create window.videonow object', function() { + const serverResp = getValidServerResponse() + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(1) + + const renderer = res[0].renderer + expect(renderer).to.be.an('object') + expect(renderer.render).to.a('function') + + const doc = window.document + const placementElement = doc.createElement('div') + placementElement.setAttribute('id', placementId) + doc.body.appendChild(placementElement) + + renderer.render() + expect(window.videonow).to.an('object') + }) + }) + + it('execute renderer.render() should not create window.videonow object if placement element not found', function() { + const serverResp = getValidServerResponse() + const res = spec.interpretResponse(serverResp, bidRequest) + expect(res.length).to.equal(1) + + const renderer = res[0].renderer + expect(renderer).to.be.an('object') + expect(renderer.render).to.a('function') + + renderer.render() + expect(window.videonow).to.be.undefined + }) + }) + }) +}) diff --git a/test/spec/modules/videoreachBidAdapter_spec.js b/test/spec/modules/videoreachBidAdapter_spec.js new file mode 100644 index 00000000000..237821f7102 --- /dev/null +++ b/test/spec/modules/videoreachBidAdapter_spec.js @@ -0,0 +1,141 @@ +import {expect} from 'chai'; +import {spec} from 'modules/videoreachBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT_URL = '//a.videoreach.com/hb/'; + +describe('videoreachBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid = { + 'params': { + 'TagId': 'ABCDE' + }, + 'bidId': '242d506d4e4f15', + 'bidderRequestId': '1893a2136a84a2', + 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'TagId': '' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'videoreach', + 'params': { + 'TagId': 'ABCDE' + }, + 'adUnitCode': 'adzone', + 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', + 'sizes': [[1, 1]], + 'bidId': '242d506d4e4f15', + 'bidderRequestId': '1893a2136a84a2', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', + 'mediaTypes': { + 'banner': { + 'sizes': [1, 1] + }, + } + } + ]; + + it('send bid request to endpoint', function () { + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.equal(ENDPOINT_URL); + expect(request.method).to.equal('POST'); + }); + + it('send bid request with GDPR to endpoint', function () { + let consentString = 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'; + + let bidderRequest = { + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr.consent_required).to.exist; + expect(payload.gdpr.consent_string).to.equal(consentString); + }); + }); + + describe('interpretResponse', function () { + let serverResponse = + { + 'body': { + 'responses': [{ + 'bidId': '242d506d4e4f15', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', + 'cpm': 10.0, + 'width': '1', + 'height': '1', + 'ad': '', + 'ttl': 360, + 'creativeId': '5cb5dc9375c0e', + 'netRevenue': true, + 'currency': 'EUR', + 'sync': ['https:\/\/SYNC_URL'] + }] + } + }; + + it('should handle response', function() { + let expectedResponse = [ + { + cpm: 10.0, + width: '1', + height: '1', + currency: 'EUR', + netRevenue: true, + ttl: 360, + ad: '', + requestId: '242d506d4e4f15', + creativeId: '5cb5dc9375c0e' + } + ]; + + let result = spec.interpretResponse(serverResponse); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('should handles empty response', function() { + let serverResponse = { + 'body': { + 'responses': [] + } + }; + + let result = spec.interpretResponse(serverResponse); + expect(result.length).to.equal(0); + }); + + describe('getUserSyncs', () => { + it('should push user sync images if enabled', () => { + const syncOptions = { pixelEnabled: true }; + const syncs = spec.getUserSyncs(syncOptions, [serverResponse]); + + expect(syncs[0]).to.deep.equal({ + type: 'image', + url: 'https://SYNC_URL' + }); + }) + }); + }); +}); diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 20b056adf6d..09ce92479f9 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -6,13 +6,13 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('VisxAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'visx', 'params': { @@ -25,11 +25,11 @@ describe('VisxAdapter', function () { 'auctionId': '1d1a030790a475', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; bid.params = { @@ -39,7 +39,13 @@ describe('VisxAdapter', function () { }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + const encodedReferrer = encodeURIComponent(bidderRequest.refererInfo.referer); let bidRequests = [ { 'bidder': 'visx', @@ -58,7 +64,7 @@ describe('VisxAdapter', function () { 'uid': '903535' }, 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], + 'sizes': [[728, 90], [300, 250]], 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', @@ -76,93 +82,139 @@ describe('VisxAdapter', function () { } ]; - it('should attach valid params to the tag', () => { - const request = spec.buildRequests([bidRequests[0]]); + it('should attach valid params to the tag', function () { + const request = spec.buildRequests([bidRequests[0]], bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535'); + expect(payload).to.have.property('sizes', '300x250,300x600'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); }); - it('auids must not be duplicated', () => { - const request = spec.buildRequests(bidRequests); + it('sizes must not be duplicated', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferrer); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); }); - it('pt parameter must be "gross" if params.priceType === "gross"', () => { + it('pt parameter must be "net" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); - expect(payload).to.have.property('pt', 'gross'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('cur', 'EUR'); + delete bidRequests[1].params.priceType; + }); + it('pt parameter must be "net" if params.priceType === "net"', function () { + bidRequests[1].params.priceType = 'net'; + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); delete bidRequests[1].params.priceType; }); - it('pt parameter must be "net" or "gross"', () => { - bidRequests[1].params.priceType = 'some'; - const request = spec.buildRequests(bidRequests); + it('pt parameter must be "net" if params.priceType === "undefined"', function () { + bidRequests[1].params.priceType = 'undefined'; + const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferrer); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); delete bidRequests[1].params.priceType; }); - it('should add currency from currency.bidderCurrencyDefault', () => { + + it('should add currency from currency.bidderCurrencyDefault', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'currency.bidderCurrencyDefault.visx' ? 'JPY' : 'USD'); - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferrer); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'JPY'); getConfigStub.restore(); }); - it('should add currency from currency.adServerCurrency', () => { + + it('should add currency from currency.adServerCurrency', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'currency.bidderCurrencyDefault.visx' ? '' : 'USD'); - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferrer); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'USD'); getConfigStub.restore(); }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 1); + }); + + it('if gdprApplies is false gdpr_applies must be 0', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 0); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 1); + }); }); - describe('interpretResponse', () => { + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903536, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 903536, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adm': '
    test content 4
    ', 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903536, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 903535, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 903537, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
    test content 5
    ', 'h': 250, 'w': 300}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, {'seat': '1'}, ]; - it('should get correct bid response', () => { + it('should get correct bid response', function () { const bidRequests = [ { 'bidder': 'visx', @@ -186,6 +238,7 @@ describe('VisxAdapter', function () { 'width': 300, 'height': 250, 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, @@ -196,7 +249,7 @@ describe('VisxAdapter', function () { expect(result).to.deep.equal(expectedResponse); }); - it('should get correct multi bid response', () => { + it('should get correct multi bid response', function () { const bidRequests = [ { 'bidder': 'visx', @@ -242,41 +295,44 @@ describe('VisxAdapter', function () { 'width': 300, 'height': 250, 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, }, { - 'requestId': '5703af74d0472a', - 'cpm': 1.15, - 'creativeId': 903535, + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 903536, 'dealId': undefined, 'width': 300, - 'height': 250, - 'ad': '
    test content 1
    ', + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, }, { - 'requestId': '4dff80cc4ee346', - 'cpm': 0.5, - 'creativeId': 903536, + 'requestId': '5703af74d0472a', + 'cpm': 0.15, + 'creativeId': 903535, 'dealId': undefined, 'width': 728, 'height': 90, - 'ad': '
    test content 2
    ', + 'ad': '
    test content 3
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, } ]; - const result = spec.interpretResponse({'body': {'seatbid': [responses[0], responses[1]]}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); expect(result).to.deep.equal(expectedResponse); }); - it('should return right currency', () => { + it('should return right currency', function () { const bidRequests = [ { 'bidder': 'visx', @@ -301,6 +357,7 @@ describe('VisxAdapter', function () { 'width': 300, 'height': 250, 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', 'currency': 'JPY', 'netRevenue': true, 'ttl': 360, @@ -312,12 +369,12 @@ describe('VisxAdapter', function () { getConfigStub.restore(); }); - it('handles wrong and nobid responses', () => { + it('handles wrong and nobid responses', function () { const bidRequests = [ { 'bidder': 'visx', 'params': { - 'uid': '903536' + 'uid': '903537' }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -349,8 +406,220 @@ describe('VisxAdapter', function () { } ]; const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); expect(result.length).to.equal(0); }); + + it('complicated case', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903536, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 903535, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 4
    ', 'auid': 903535, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 5
    ', 'auid': 903536, 'h': 600, 'w': 350}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2164be6358b9', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '326bde7fbf69', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903536' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4e111f1b66e4', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903536' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '1751cd90161', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '2164be6358b9', + 'cpm': 1.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '4e111f1b66e4', + 'cpm': 0.5, + 'creativeId': 903536, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '26d6f897b516', + 'cpm': 0.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 3
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '326bde7fbf69', + 'cpm': 0.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 4
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '1751cd90161', + 'cpm': 0.5, + 'creativeId': 903536, + 'dealId': undefined, + 'width': 350, + 'height': 600, + 'ad': '
    test content 5
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('dublicate uids and sizes in one slot', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '5126e301f4be', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57b2ebe70e16', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '225fcd44b18c', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '5126e301f4be', + 'cpm': 1.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '57b2ebe70e16', + 'cpm': 0.5, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 2
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); }); }); diff --git a/test/spec/modules/vmgBidAdapter_spec.js b/test/spec/modules/vmgBidAdapter_spec.js new file mode 100644 index 00000000000..688c03577fd --- /dev/null +++ b/test/spec/modules/vmgBidAdapter_spec.js @@ -0,0 +1,98 @@ +import { expect } from 'chai'; +import { spec } from 'modules/vmgBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('VmgAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }) + + describe('isBidRequestValid', function () { + let bidRequest = { + adUnitCode: 'div-0', + auctionId: 'd69cdd3f-75e3-42dc-b313-e54c0a99c757', + bidId: '280e2eb8ac3891', + bidRequestsCount: 1, + bidder: 'vmg', + bidderRequestId: '14690d27b056c8', + mediaTypes: { + banner: { + sizes: [ [ 970, 250 ] ] + } + }, + sizes: [ 970, 250 ], + src: 'client', + transactionId: 'af62f065-dfa7-4564-8cb2-d277dc6069f2' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + }) + + describe('buildRequests', function () { + let validBidRequests = [ + { + adUnitCode: 'div-0', + auctionId: 'd69cdd3f-75e3-42dc-b313-e54c0a99c757', + bidId: '280e2eb8ac3891', + bidRequestsCount: 1, + bidder: 'vmg', + bidderRequestId: '14690d27b056c8', + mediaTypes: { + banner: { + sizes: [ [ 970, 250 ] ] + } + }, + sizes: [ 970, 250 ], + src: 'client', + transactionId: 'af62f065-dfa7-4564-8cb2-d277dc6069f2' + } + ]; + + let bidderRequest = { + auctionId: 'd69cdd3f-75e3-42dc-b313-e54c0a99c757', + auctionStart: 1549316149227, + bidderCode: 'vmg', + bidderRequestId: '14690d27b056c8', + refererInfo: { + canonicalUrl: undefined, + numIframes: 0, + reachedTop: true, + referer: 'https://vmg.nyc/public_assets/adapt/prebid.html', + stack: [ 'https://vmg.nyc/public_assets/adapt/prebid.html' ] + }, + start: 1549316149229, + timeout: 1000 + }; + + it('buildRequests fires', function () { + let request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request).to.exist; + expect(request.method).to.equal('POST'); + expect(request.data).to.exist; + }); + }) + + describe('interpretResponse', function () { + let serverResponse = {}; + serverResponse.body = { + 'div-0': ['test'] + }; + + var bidRequest = { + data: '[{"adUnitCode":"div-0","referer":"https://vmg.nyc/public_assets/adapt/prebid.html","bidId":"280e2eb8ac3891"}]', + method: 'POST', + url: 'https://predict.vmg.nyc' + }; + + it('interpresResponse fires', function () { + let bidResponses = spec.interpretResponse(serverResponse, bidRequest); + expect(bidResponses[0].dealId[0]).to.equal('test'); + }); + }); +}); diff --git a/test/spec/modules/vubleAnalyticsAdapter_spec.js b/test/spec/modules/vubleAnalyticsAdapter_spec.js index 896f6e4ee87..841a53c6dee 100644 --- a/test/spec/modules/vubleAnalyticsAdapter_spec.js +++ b/test/spec/modules/vubleAnalyticsAdapter_spec.js @@ -1,36 +1,36 @@ import vubleAnalytics from 'modules/vubleAnalyticsAdapter'; import { expect } from 'chai'; let events = require('src/events'); -let adaptermanager = require('src/adaptermanager'); +let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); describe('Vuble Prebid Analytic', function () { let xhr; - before(() => { + before(function () { xhr = sinon.useFakeXMLHttpRequest(); }); - after(() => { + after(function () { vubleAnalytics.disableAnalytics(); xhr.restore(); }); describe('enableAnalytics', function () { - beforeEach(() => { + beforeEach(function () { sinon.spy(vubleAnalytics, 'track'); sinon.stub(events, 'getEvents').returns([]); }); - afterEach(() => { + afterEach(function () { vubleAnalytics.track.restore(); events.getEvents.restore(); }); it('should catch all events', function () { - adaptermanager.registerAnalyticsAdapter({ + adapterManager.registerAnalyticsAdapter({ code: 'vuble', adapter: vubleAnalytics }); - adaptermanager.enableAnalytics({ + adapterManager.enableAnalytics({ provider: 'vuble', options: { pubId: 18, diff --git a/test/spec/modules/vubleBidAdapter_spec.js b/test/spec/modules/vubleBidAdapter_spec.js index 61f00ef2c3d..8996c1b4957 100644 --- a/test/spec/modules/vubleBidAdapter_spec.js +++ b/test/spec/modules/vubleBidAdapter_spec.js @@ -4,23 +4,23 @@ import {expect} from 'chai'; import {spec as adapter} from 'modules/vubleBidAdapter'; import * as utils from 'src/utils'; -describe('VubleAdapter', () => { - describe('Check methods existance', () => { - it('exists and is a function', () => { +describe('VubleAdapter', function () { + describe('Check methods existance', function () { + it('exists and is a function', function () { expect(adapter.isBidRequestValid).to.exist.and.to.be.a('function'); }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.buildRequests).to.exist.and.to.be.a('function'); }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.interpretResponse).to.exist.and.to.be.a('function'); }); - it('exists and is a function', () => { + it('exists and is a function', function () { expect(adapter.getUserSyncs).to.exist.and.to.be.a('function'); }); }); - describe('Check method isBidRequestValid return', () => { + describe('Check method isBidRequestValid return', function () { let bid = { bidder: 'vuble', params: { @@ -37,11 +37,11 @@ describe('VubleAdapter', () => { }, }; - it('should be true', () => { + it('should be true', function () { expect(adapter.isBidRequestValid(bid)).to.be.true; }); - it('should be false because the sizes are missing or in the wrong format', () => { + it('should be false because the sizes are missing or in the wrong format', function () { let wrongBid = utils.deepClone(bid); wrongBid.sizes = '640360'; expect(adapter.isBidRequestValid(wrongBid)).to.be.false; @@ -51,7 +51,7 @@ describe('VubleAdapter', () => { expect(adapter.isBidRequestValid(wrongBid)).to.be.false; }); - it('should be false because the mediaType is missing or wrong', () => { + it('should be false because the mediaType is missing or wrong', function () { let wrongBid = utils.deepClone(bid); wrongBid.mediaTypes = {}; expect(adapter.isBidRequestValid(wrongBid)).to.be.false; @@ -61,7 +61,7 @@ describe('VubleAdapter', () => { expect(adapter.isBidRequestValid(wrongBid)).to.be.false; }); - it('should be false because the env is missing or wrong', () => { + it('should be false because the env is missing or wrong', function () { let wrongBid = utils.deepClone(bid); wrongBid.params.env = 'us'; expect(adapter.isBidRequestValid(wrongBid)).to.be.false; @@ -71,22 +71,22 @@ describe('VubleAdapter', () => { expect(adapter.isBidRequestValid(wrongBid)).to.be.false; }); - it('should be false because params.pubId is missing', () => { + it('should be false because params.pubId is missing', function () { let wrongBid = utils.deepClone(bid); delete wrongBid.params.pubId; expect(adapter.isBidRequestValid(wrongBid)).to.be.false; }); - it('should be false because params.zoneId is missing', () => { + it('should be false because params.zoneId is missing', function () { let wrongBid = utils.deepClone(bid); delete wrongBid.params.zoneId; expect(adapter.isBidRequestValid(wrongBid)).to.be.false; }); }); - describe('Check buildRequests method', () => { + describe('Check buildRequests method', function () { let sandbox; - before(() => { + before(function () { sandbox = sinon.sandbox.create(); sandbox.stub(utils, 'getTopWindowUrl').returns('http://www.vuble.tv/'); }); @@ -106,7 +106,8 @@ describe('VubleAdapter', () => { context: 'instream' } }, - bidId: 'abdc' + bidId: 'abdc', + adUnitCode: '' }; let bid2 = { bidder: 'vuble', @@ -122,7 +123,8 @@ describe('VubleAdapter', () => { context: 'outstream' } }, - bidId: 'efgh' + bidId: 'efgh', + adUnitCode: 'code' }; // Formatted requets @@ -138,7 +140,8 @@ describe('VubleAdapter', () => { floor_price: 5.5, url: 'http://www.vuble.tv/', env: 'net', - bid_id: 'abdc' + bid_id: 'abdc', + adUnitCode: '' } }; let request2 = { @@ -153,21 +156,22 @@ describe('VubleAdapter', () => { floor_price: 0, url: 'http://www.vuble.fr/', env: 'com', - bid_id: 'efgh' + bid_id: 'efgh', + adUnitCode: 'code' } }; - it('must return the right formatted requests', () => { + it('must return the right formatted requests', function () { let rs = adapter.buildRequests([bid1, bid2]); expect(adapter.buildRequests([bid1, bid2])).to.deep.equal([request1, request2]); }); - after(() => { + after(function () { sandbox.restore(); }); }); - describe('Check interpretResponse method return', () => { + describe('Check interpretResponse method return', function () { // Server's response let response = { body: { @@ -188,7 +192,8 @@ describe('VubleAdapter', () => { pub_id: '3', zone_id: '12345', bid_id: 'abdc', - floor_price: 5.50 // optional + floor_price: 5.50, // optional + adUnitCode: 'code' }, method: 'POST', url: '//player.mediabong.net/prebid/request' @@ -208,11 +213,11 @@ describe('VubleAdapter', () => { mediaType: 'video' }; - it('should equal to the expected formatted result', () => { + it('should equal to the expected formatted result', function () { expect(adapter.interpretResponse(response, bid)).to.deep.equal([result]); }); - it('should be empty because the status is missing or wrong', () => { + it('should be empty because the status is missing or wrong', function () { let wrongResponse = utils.deepClone(response); wrongResponse.body.status = 'ko'; expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; @@ -222,7 +227,7 @@ describe('VubleAdapter', () => { expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; }); - it('should be empty because the body is missing or wrong', () => { + it('should be empty because the body is missing or wrong', function () { let wrongResponse = utils.deepClone(response); wrongResponse.body = [1, 2, 3]; expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; @@ -231,9 +236,16 @@ describe('VubleAdapter', () => { delete wrongResponse.body; expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; }); + + it('should equal to the expected formatted result', function () { + response.body.renderer_url = 'vuble_renderer.js'; + result.adUnitCode = 'code'; + let formattedResponses = adapter.interpretResponse(response, bid); + expect(formattedResponses[0].adUnitCode).to.equal(result.adUnitCode); + }); }); - describe('Check getUserSyncs method return', () => { + describe('Check getUserSyncs method return', function () { // Sync options let syncOptions = { iframeEnabled: false @@ -253,7 +265,7 @@ describe('VubleAdapter', () => { url: 'http://player.mediabong.net/csifr?1234' }; - it('should return an empty array', () => { + it('should return an empty array', function () { expect(adapter.getUserSyncs({}, [])).to.be.empty; expect(adapter.getUserSyncs({}, [])).to.be.empty; expect(adapter.getUserSyncs(syncOptions, [response])).to.be.empty; @@ -263,7 +275,7 @@ describe('VubleAdapter', () => { expect(adapter.getUserSyncs(syncOptions, [response])).to.be.empty; }); - it('should be equal to the expected result', () => { + it('should be equal to the expected result', function () { response.body.iframeSync = 'http://player.mediabong.net/csifr?1234'; expect(adapter.getUserSyncs(syncOptions, [response])).to.deep.equal([result]); }) diff --git a/test/spec/modules/weboramaBidAdapter_spec.js b/test/spec/modules/weboramaBidAdapter_spec.js new file mode 100644 index 00000000000..d0b119825a6 --- /dev/null +++ b/test/spec/modules/weboramaBidAdapter_spec.js @@ -0,0 +1,118 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/weboramaBidAdapter'; + +describe('WeboramaAdapter', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'weborama', + bidderRequestId: '145e1d6a7837c9', + params: { + placementId: 123, + traffic: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + describe('isBidRequestValid', function () { + it('Should return true when placementId can be cast to a number', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when placementId is not a number', function () { + bid.params.placementId = 'aaa'; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('//supply.nl.weborama.fr/?c=o&m=multi'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + let placements = data['placements']; + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.all.keys('placementId', 'bidId', 'traffic', 'sizes'); + expect(placement.placementId).to.be.a('number'); + expect(placement.bidId).to.be.a('string'); + expect(placement.traffic).to.be.a('string'); + expect(placement.sizes).to.be.an('array'); + } + }); + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

    Hello ad

    ', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + + describe('getUserSyncs', function () { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and `', function () { + 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('//supply.nl.weborama.fr/?c=o&m=cookie'); + }); + }); +}); diff --git a/test/spec/modules/widespaceBidAdapter_spec.js b/test/spec/modules/widespaceBidAdapter_spec.js index e1c922c2b9c..b3884a90b84 100644 --- a/test/spec/modules/widespaceBidAdapter_spec.js +++ b/test/spec/modules/widespaceBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec } from 'modules/widespaceBidAdapter'; import includes from 'core-js/library/fn/array/includes'; -describe('+widespaceAdatperTest', () => { +describe('+widespaceAdatperTest', function () { // Dummy bid request const bidRequest = [{ 'adUnitCode': 'div-gpt-ad-1460505748561-0', @@ -45,6 +45,22 @@ describe('+widespaceAdatperTest', () => { 'transactionId': '4f68b713-04ba-4d7f-8df9-643bcdab5efv' }]; + // Dummy bidderRequest object + const bidderRequest = { + auctionId: 'bf1e57ee-fff2-4304-8143-91aaf423a944', + auctionStart: 1527418994278, + bidderCode: 'widespace', + bidderRequestId: '37a5f053efef34', + timeout: 3000, + gdprConsent: { + consentString: 'consentString', + gdprApplies: true, + vendorData: { + hasGlobalScope: false + } + } + }; + // Dummy bid response with ad code const bidResponse = { body: [{ @@ -94,8 +110,8 @@ describe('+widespaceAdatperTest', () => { navigator.connection.type = 'wifi'; } - describe('+bidRequestValidity', () => { - it('bidRequest with sid and currency params', () => { + describe('+bidRequestValidity', function () { + it('bidRequest with sid and currency params', function () { expect(spec.isBidRequestValid({ bidder: 'widespace', params: { @@ -105,7 +121,7 @@ describe('+widespaceAdatperTest', () => { })).to.equal(true); }); - it('-bidRequest with missing sid', () => { + it('-bidRequest with missing sid', function () { expect(spec.isBidRequestValid({ bidder: 'widespace', params: { @@ -114,7 +130,7 @@ describe('+widespaceAdatperTest', () => { })).to.equal(false); }); - it('-bidRequest with missing currency', () => { + it('-bidRequest with missing currency', function () { expect(spec.isBidRequestValid({ bidder: 'widespace', params: { @@ -124,37 +140,66 @@ describe('+widespaceAdatperTest', () => { }); }); - describe('+bidRequest', () => { - const request = spec.buildRequests(bidRequest); + describe('+bidRequest', function () { + const request = spec.buildRequests(bidRequest, bidderRequest); const UrlRegExp = /^((ftp|http|https):)?\/\/[^ "]+$/; - it('-bidRequest method is POST', () => { + let fakeLocalStorage = {}; + let lsSetStub; + let lsGetStub; + let lsRemoveStub; + + beforeEach(function() { + lsSetStub = sinon.stub(window.localStorage, 'setItem').callsFake(function (name, value) { + fakeLocalStorage[name] = value; + }); + + lsGetStub = sinon.stub(window.localStorage, 'getItem').callsFake(function (key) { + return fakeLocalStorage[key] || null; + }); + + lsRemoveStub = sinon.stub(window.localStorage, 'removeItem').callsFake(function (key) { + if (key && (fakeLocalStorage[key] !== null || fakeLocalStorage[key] !== undefined)) { + delete fakeLocalStorage[key]; + } + return true; + }); + }); + + afterEach(function() { + lsSetStub.restore(); + lsGetStub.restore(); + lsRemoveStub.restore(); + fakeLocalStorage = {}; + }); + + it('-bidRequest method is POST', function () { expect(request[0].method).to.equal('POST'); }); - it('-bidRequest url is valid', () => { + it('-bidRequest url is valid', function () { expect(UrlRegExp.test(request[0].url)).to.equal(true); }); - it('-bidRequest data exist', () => { - expect(request[0].data).to.exists; + it('-bidRequest data exist', function () { + expect(request[0].data).to.exist; }); - it('-bidRequest data is form data', () => { + it('-bidRequest data is form data', function () { expect(typeof request[0].data).to.equal('string'); }); - it('-bidRequest options have header type', () => { - expect(request[0].options.contentType).to.exists; + it('-bidRequest options have header type', function () { + expect(request[0].options.contentType).to.exist; }); - it('-cookie test for wsCustomData ', () => { + it('-cookie test for wsCustomData ', function () { expect(request[0].data.indexOf('hb.cd') > -1).to.equal(true); }); }); - describe('+interpretResponse', () => { - it('-required params available in response', () => { + describe('+interpretResponse', function () { + it('-required params available in response', function () { const result = spec.interpretResponse(bidResponse, bidRequest); let requiredKeys = [ 'requestId', @@ -185,19 +230,19 @@ describe('+widespaceAdatperTest', () => { }); }); - it('-empty result if noad responded', () => { + it('-empty result if noad responded', function () { const noAdResult = spec.interpretResponse(bidResponseNoAd, bidRequest); expect(noAdResult.length).to.equal(0); }); - it('-empty response should not breake anything in adapter', () => { + it('-empty response should not breake anything in adapter', function () { const noResponse = spec.interpretResponse({}, bidRequest); expect(noResponse.length).to.equal(0); }); }); - describe('+getUserSyncs', () => { - it('-always return an array', () => { + describe('+getUserSyncs', function () { + it('-always return an array', function () { const userSync_test1 = spec.getUserSyncs({}, [bidResponse]); expect(Array.isArray(userSync_test1)).to.equal(true); diff --git a/test/spec/modules/xendizBidAdapter_spec.js b/test/spec/modules/xendizBidAdapter_spec.js index 66b9dc62b88..4d1aa3c935f 100644 --- a/test/spec/modules/xendizBidAdapter_spec.js +++ b/test/spec/modules/xendizBidAdapter_spec.js @@ -34,39 +34,39 @@ const bidResponse = { const noBidResponse = { body: { id: '1d1a030790a475', bids: [] } }; -describe('xendizBidAdapter', () => { +describe('xendizBidAdapter', function () { const adapter = newBidder(spec); - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', () => { - it('should return false', () => { + describe('isBidRequestValid', function () { + it('should return false', function () { let bid = Object.assign({}, bidRequest); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return true', () => { + it('should return true', function () { expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); }); - describe('buildRequests', () => { - it('should format valid url', () => { + describe('buildRequests', function () { + it('should format valid url', function () { const request = spec.buildRequests([bidRequest]); expect(request.url).to.equal(VALID_ENDPOINT); }); - it('should format valid url', () => { + it('should format valid url', function () { const request = spec.buildRequests([bidRequest]); expect(request.url).to.equal(VALID_ENDPOINT); }); - it('should format valid request body', () => { + it('should format valid request body', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); expect(payload.id).to.exist; @@ -74,7 +74,7 @@ describe('xendizBidAdapter', () => { expect(payload.device).to.exist; }); - it('should attach valid device info', () => { + it('should attach valid device info', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); expect(payload.device).to.deep.equal([ @@ -84,7 +84,7 @@ describe('xendizBidAdapter', () => { ]); }); - it('should transform sizes', () => { + it('should transform sizes', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); const item = payload.items[0]; @@ -92,8 +92,8 @@ describe('xendizBidAdapter', () => { }); }); - describe('interpretResponse', () => { - it('should get correct bid response', () => { + describe('interpretResponse', function () { + it('should get correct bid response', function () { const result = spec.interpretResponse(bidResponse); const validResponse = [{ requestId: '30b31c1838de1e', @@ -111,7 +111,7 @@ describe('xendizBidAdapter', () => { expect(result).to.deep.equal(validResponse); }); - it('handles nobid responses', () => { + it('handles nobid responses', function () { let result = spec.interpretResponse(noBidResponse); expect(result.length).to.equal(0); }); diff --git a/test/spec/modules/xhbBidAdapter_spec.js b/test/spec/modules/xhbBidAdapter_spec.js new file mode 100644 index 00000000000..e48d3011ed2 --- /dev/null +++ b/test/spec/modules/xhbBidAdapter_spec.js @@ -0,0 +1,495 @@ +import { expect } from 'chai'; +import { spec } from 'modules/xhbBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { deepClone } from 'src/utils'; + +const ENDPOINT = '//ib.adnxs.com/ut/v3/prebid'; + +describe('xhbAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'xhb', + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when required params found', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'member': '1234', + 'invCode': 'ABCD' + }; + + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'xhb', + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should parse out private sizes', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + privateSizes: [300, 250] + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].private_sizes).to.exist; + expect(payload.tags[0].private_sizes).to.deep.equal([{width: 300, height: 250}]); + }); + + it('should add source and verison to the tag', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.sdk).to.exist; + expect(payload.sdk).to.deep.equal({ + source: 'pbjs', + version: '$prebid.version$' + }); + }); + + it('should populate the ad_types array on all requests', function () { + ['banner', 'video', 'native'].forEach(type => { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes[type] = {}; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.deep.equal([type]); + }); + }); + + it('should populate the ad_types array on outstream requests', function () { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes.video = {context: 'outstream'}; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.deep.equal(['video']); + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should attach valid video params to the tag', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + video: { + id: 123, + minduration: 100, + foobar: 'invalid' + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ + id: 123, + minduration: 100 + }); + }); + + it('should attach valid user params to the tag', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + user: { + external_uid: '123', + foobar: 'invalid' + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.user).to.exist; + expect(payload.user).to.deep.equal({ + external_uid: '123', + }); + }); + + it('should attach native params to the request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + title: {required: true}, + body: {required: true}, + image: {required: true, sizes: [{ width: 100, height: 100 }]}, + cta: {required: false}, + sponsoredBy: {required: true} + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].native.layouts[0]).to.deep.equal({ + title: {required: true}, + description: {required: true}, + main_image: {required: true, sizes: [{ width: 100, height: 100 }]}, + ctatext: {required: false}, + sponsored_by: {required: true} + }); + }); + + it('sets minimum native asset params when not provided on adunit', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + image: {required: true}, + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].native.layouts[0]).to.deep.equal({ + main_image: {required: true, sizes: [{}]}, + }); + }); + + it('does not overwrite native ad unit params with mimimum params', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + image: { + aspect_ratios: [{ + min_width: 100, + ratio_width: 2, + ratio_height: 3, + }] + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].native.layouts[0]).to.deep.equal({ + main_image: { + required: true, + aspect_ratios: [{ + min_width: 100, + ratio_width: 2, + ratio_height: 3, + }] + }, + }); + }); + + it('should convert keyword params to proper form and attaches to request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + keywords: { + single: 'val', + singleArr: ['val'], + singleArrNum: [5], + multiValMixed: ['value1', 2, 'value3'], + singleValNum: 123, + badValue: {'foo': 'bar'} // should be dropped + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }]); + }); + + it('should add payment rules to the request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + usePaymentRule: true + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].use_pmt_rule).to.equal(true); + }); + + it('should add gdpr consent information to the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'xhb', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_consent).to.exist; + expect(payload.gdpr_consent.consent_string).to.exist.and.to.equal(consentString); + expect(payload.gdpr_consent.consent_required).to.exist.and.to.be.true; + }); + }); + + describe('interpretResponse', function () { + let response = { + 'version': '3.0.0', + 'tags': [ + { + 'uuid': '3db3773286ee59', + 'tag_id': 10433394, + 'auction_id': '4534722592064951574', + 'nobid': false, + 'no_ad_url': 'http://lax1-ib.adnxs.com/no-ad', + 'timeout_ms': 10000, + 'ad_profile_id': 27079, + 'ads': [ + { + 'content_source': 'rtb', + 'ad_type': 'banner', + 'buyer_member_id': 958, + 'creative_id': 29681110, + 'media_type_id': 1, + 'media_subtype_id': 1, + 'cpm': 0.5, + 'cpm_publisher_currency': 0.5, + 'publisher_currency_code': '$', + 'client_initiated_ad_counting': true, + 'rtb': { + 'banner': { + 'content': '', + 'width': 300, + 'height': 250 + }, + 'trackers': [ + { + 'impression_urls': [ + 'http://lax1-ib.adnxs.com/impression' + ], + 'video_events': {} + } + ] + } + } + ] + } + ] + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '3db3773286ee59', + 'cpm': 0.5, + 'creativeId': 29681110, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '', + 'mediaType': 'banner', + 'currency': 'USD', + 'ttl': 300, + 'netRevenue': true, + 'appnexus': { + 'buyerMemberId': 958 + } + } + ]; + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + let response = { + 'version': '0.0.1', + 'tags': [{ + 'uuid': '84ab500420319d', + 'tag_id': 5976557, + 'auction_id': '297492697822162468', + 'nobid': true + }] + }; + let bidderRequest; + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + + it('handles non-banner media responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'content': '' + } + } + }] + }] + }; + let bidderRequest; + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastUrl'); + expect(result[0]).to.have.property('vastImpUrl'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles native responses', function () { + let response1 = deepClone(response); + response1.tags[0].ads[0].ad_type = 'native'; + response1.tags[0].ads[0].rtb.native = { + 'title': 'Native Creative', + 'desc': 'Cool description great stuff', + 'ctatext': 'Do it', + 'sponsored': 'AppNexus', + 'icon': { + 'width': 0, + 'height': 0, + 'url': 'http://cdn.adnxs.com/icon.png' + }, + 'main_img': { + 'width': 2352, + 'height': 1516, + 'url': 'http://cdn.adnxs.com/img.png' + }, + 'link': { + 'url': 'https://www.appnexus.com', + 'fallback_url': '', + 'click_trackers': ['http://nym1-ib.adnxs.com/click'] + }, + 'impression_trackers': ['http://example.com'], + }; + let bidderRequest; + + let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); + expect(result[0].native.title).to.equal('Native Creative'); + expect(result[0].native.body).to.equal('Cool description great stuff'); + expect(result[0].native.cta).to.equal('Do it'); + expect(result[0].native.image.url).to.equal('http://cdn.adnxs.com/img.png'); + }); + + it('supports configuring outstream renderers', function () { + const outstreamResponse = deepClone(response); + outstreamResponse.tags[0].ads[0].rtb.video = {}; + outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js'; + + const bidderRequest = { + bids: [{ + renderer: { + options: { + adText: 'configured' + } + } + }] + }; + + const result = spec.interpretResponse({ body: outstreamResponse }, {bidderRequest}); + expect(result[0].renderer.config).to.deep.equal( + bidderRequest.bids[0].renderer.options + ); + }); + }); +}); diff --git a/test/spec/modules/yieldbotBidAdapter_spec.js b/test/spec/modules/yieldbotBidAdapter_spec.js index 206645acd95..2548bb31fdc 100644 --- a/test/spec/modules/yieldbotBidAdapter_spec.js +++ b/test/spec/modules/yieldbotBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import find from 'core-js/library/fn/array/find'; import { newBidder } from 'src/adapters/bidderFactory'; -import AdapterManager from 'src/adaptermanager'; +import AdapterManager from 'src/adapterManager'; import { newAuctionManager } from 'src/auctionManager'; import * as utils from 'src/utils'; import * as urlUtils from 'src/url'; @@ -1059,6 +1059,16 @@ describe('Yieldbot Adapter Unit Tests', function() { expect(edgeServerUrlPrefix).to.match(beginsRegex); expect(responses[0].ad).to.match(containsRegex); }); + + it('should not use document.open() in ad markup', function() { + FIXTURE_SERVER_RESPONSE.body.url_prefix = 'http://close.edge.adserver.com/'; + const responses = YieldbotAdapter.interpretResponse( + FIXTURE_SERVER_RESPONSE, + FIXTURE_BID_REQUEST + ); + expect(responses[0].ad).to.not.match(/var innerFrameDoc=innerFrame\.contentWindow\.document;innerFrameDoc\.open\(\);innerFrameDoc\.write\(iframeHtml\);innerFrameDoc\.close\(\);/); + expect(responses[0].ad).to.match(/var innerFrameDoc=innerFrame\.contentWindow\.document;innerFrameDoc\.write\(iframeHtml\);innerFrameDoc\.close\(\);/); + }); }); describe('getUserSyncs', function() { @@ -1323,4 +1333,51 @@ describe('Yieldbot Adapter Unit Tests', function() { done(); }); }); + + describe('Adapter Request Timestamps', function() { + let sandbox; + beforeEach(function() { + sandbox = sinon.sandbox.create(); + sandbox.stub(Date, 'now').callsFake(() => { + return new Date(); + }); + }); + + afterEach(function() { + sandbox.restore(); + }); + + it('should have overridden Date.now() function', function() { + expect(Date.now().getTime()).to.match(/^[0-9]+/); + }); + + it('should be milliseconds past epoch query param values', function() { + const request = YieldbotAdapter.buildRequests(FIXTURE_BID_REQUESTS)[0]; + expect(request.data).to.not.equal(undefined); + + const timestampParams = [ + 'cts_ns', + 'cts_js', + 'cts_ini' + ]; + + timestampParams.forEach((item) => { + expect(!isNaN(request.data[item])).to.equal(true); + expect(request.data[item] > 0).to.equal(true); + expect(request.data[item]).to.match(/^[0-9]+/); + }); + }); + + it('should use (new Date()).getTime() for timestamps in ad markup', function() { + FIXTURE_SERVER_RESPONSE.body.url_prefix = 'http://close.edge.adserver.com/'; + const responses = YieldbotAdapter.interpretResponse( + FIXTURE_SERVER_RESPONSE, + FIXTURE_BID_REQUEST + ); + + expect(responses[0].ad).to.match(/cts_rend_.*='\+\(new Date\(\)\)\.getTime\(\)/); + expect(responses[0].ad).to.match(/cts_ad='\+\(new Date\(\)\)\.getTime\(\)/); + expect(responses[0].ad).to.match(/cts_imp='\+\(new Date\(\)\)\.getTime\(\)/); + }); + }); }); diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 1a5ca59d3a2..c8709969e00 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -7,7 +7,12 @@ const REQUEST = { 'params': { 'adslotId': '1111', 'supplyId': '2222', - 'adSize': '728x90' + 'adSize': '728x90', + 'targeting': { + 'key1': 'value1', + 'key2': 'value2' + }, + 'extId': 'abc' }, 'bidderRequestId': '143346cf0f1731', 'auctionId': '2e41f65424c87c', @@ -25,17 +30,17 @@ const RESPONSE = { pid: 2222 } -describe('yieldlabBidAdapter', () => { +describe('yieldlabBidAdapter', function () { const adapter = newBidder(spec) - describe('inherited functions', () => { - it('exists and is a function', () => { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function') }) }) - describe('isBidRequestValid', () => { - it('should return true when required params found', () => { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { const request = { 'params': { 'adslotId': '1111', @@ -46,36 +51,72 @@ describe('yieldlabBidAdapter', () => { expect(spec.isBidRequestValid(request)).to.equal(true) }) - it('should return false when required params are not passed', () => { + it('should return false when required params are not passed', function () { expect(spec.isBidRequestValid({})).to.equal(false) }) }) - describe('buildRequests', () => { + describe('buildRequests', function () { const bidRequests = [REQUEST] const request = spec.buildRequests(bidRequests) - it('sends bid request to ENDPOINT via GET', () => { + it('sends bid request to ENDPOINT via GET', function () { expect(request.method).to.equal('GET') }) - it('returns a list of valid requests', () => { + it('returns a list of valid requests', function () { expect(request.validBidRequests).to.eql([REQUEST]) }) - }) - describe('interpretResponse', () => { - const validRequests = { - validBidRequests: [REQUEST] - } + it('passes targeting to bid request', function () { + expect(request.url).to.include('t=key1%3Dvalue1%26key2%3Dvalue2') + }) + + const gdprRequest = spec.buildRequests(bidRequests, { + gdprConsent: { + consentString: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA', + gdprApplies: true + } + }) + + it('passes gdpr flag and consent if present', function () { + expect(gdprRequest.url).to.include('consent=BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA') + expect(gdprRequest.url).to.include('gdpr=true') + }) + }) - it('handles nobid responses', () => { + describe('interpretResponse', function () { + it('handles nobid responses', function () { expect(spec.interpretResponse({body: {}}, {validBidRequests: []}).length).to.equal(0) expect(spec.interpretResponse({body: []}, {validBidRequests: []}).length).to.equal(0) }) - it('should get correct bid response', () => { - const result = spec.interpretResponse({body: [RESPONSE]}, validRequests) + it('should get correct bid response', function () { + const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST]}) + + expect(result[0].requestId).to.equal('2d925f27f5079f') + expect(result[0].cpm).to.equal(0.01) + expect(result[0].width).to.equal(728) + expect(result[0].height).to.equal(90) + expect(result[0].creativeId).to.equal('1111') + expect(result[0].dealId).to.equal(2222) + expect(result[0].currency).to.equal('EUR') + expect(result[0].netRevenue).to.equal(false) + expect(result[0].ttl).to.equal(300) + expect(result[0].referrer).to.equal('') + expect(result[0].ad).to.include('
    ', - creativeId: '9874652394875' + creative_id: '9874652394875' }], header: 'header?' }; }) - it('should correctly reorder the server response', () => { + it('should correctly reorder the server response', function () { const newResponse = spec.interpretResponse(serverResponse); expect(newResponse.length).to.be.equal(1); expect(newResponse[0]).to.deep.equal({ @@ -126,7 +184,7 @@ describe('YieldmoAdapter', () => { }); }); - it('should not add responses if the cpm is 0 or null', () => { + it('should not add responses if the cpm is 0 or null', function () { serverResponse.body[0].cpm = 0; let response = spec.interpretResponse(serverResponse); expect(response).to.deep.equal([]); @@ -137,14 +195,14 @@ describe('YieldmoAdapter', () => { }); }); - describe('getUserSync', () => { + describe('getUserSync', function () { const SYNC_ENDPOINT = 'https://static.yieldmo.com/blank.min.html?orig='; let options = { iframeEnabled: true, pixelEnabled: true }; - it('should return a tracker with type and url as parameters', () => { + it('should return a tracker with type and url as parameters', function () { if (/iPhone|iPad|iPod/i.test(window.navigator.userAgent)) { expect(spec.getUserSync(options)).to.deep.equal([{ type: 'iframe', diff --git a/test/spec/modules/yieldnexusBidAdapter_spec.js b/test/spec/modules/yieldnexusBidAdapter_spec.js new file mode 100644 index 00000000000..8f2e40d1810 --- /dev/null +++ b/test/spec/modules/yieldnexusBidAdapter_spec.js @@ -0,0 +1,418 @@ +import {expect} from 'chai'; +import {spec} from 'modules/yieldnexusBidAdapter'; +import * as utils from 'src/utils'; + +const spid = '123'; + +describe('YieldNexusAdapter', () => { + describe('isBidRequestValid', () => { + it('should validate supply', () => { + expect(spec.isBidRequestValid({params: {}})).to.equal(false); + expect(spec.isBidRequestValid({params: {spid: 123}})).to.equal(false); + expect(spec.isBidRequestValid({params: {spid: '123'}})).to.equal(true); + }); + it('should validate bid floor', () => { + expect(spec.isBidRequestValid({params: {spid: '123'}})).to.equal(true); // bidfloor has a default + expect(spec.isBidRequestValid({params: {spid: '123', bidfloor: '123'}})).to.equal(false); + expect(spec.isBidRequestValid({params: {spid: '123', bidfloor: 0.1}})).to.equal(true); + }); + it('should validate adpos', () => { + expect(spec.isBidRequestValid({params: {spid: '123'}})).to.equal(true); // adpos has a default + expect(spec.isBidRequestValid({params: {spid: '123', adpos: '123'}})).to.equal(false); + expect(spec.isBidRequestValid({params: {spid: '123', adpos: 0.1}})).to.equal(true); + }); + it('should validate instl', () => { + expect(spec.isBidRequestValid({params: {spid: '123'}})).to.equal(true); // adpos has a default + expect(spec.isBidRequestValid({params: {spid: '123', instl: '123'}})).to.equal(false); + expect(spec.isBidRequestValid({params: {spid: '123', instl: -1}})).to.equal(false); + expect(spec.isBidRequestValid({params: {spid: '123', instl: 0}})).to.equal(true); + expect(spec.isBidRequestValid({params: {spid: '123', instl: 1}})).to.equal(true); + expect(spec.isBidRequestValid({params: {spid: '123', instl: 2}})).to.equal(false); + }); + }); + describe('buildRequests', () => { + const bidRequest = { + 'adUnitCode': 'adunit-code', + 'auctionId': 'fdkhjg3s7ahjja', + 'mediaTypes': { + banner: {} + }, + 'params': {spid}, + 'sizes': [[300, 250], [300, 600]] + }; + + it('returns an array', () => { + let response; + + response = spec.buildRequests([]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + + response = spec.buildRequests([bidRequest]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + + const adUnit1 = Object.assign({}, utils.deepClone(bidRequest), {auctionId: '1', adUnitCode: 'a'}); + const adUnit2 = Object.assign({}, utils.deepClone(bidRequest), {auctionId: '1', adUnitCode: 'b'}); + response = spec.buildRequests([adUnit1, adUnit2]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(2); + }); + + it('uses yieldnexus dns', () => { + const response = spec.buildRequests([bidRequest])[0]; + expect(response.method).to.equal('POST'); + expect(response.url).to.match(new RegExp(`^https://ssp\\.ynxs\\.io/r/${spid}/bidr\\?bidder=prebid&rformat=open_rtb&reqformat=rtb_json$`, 'g')); + expect(response.data.id).to.equal(bidRequest.auctionId); + }); + + it('builds request correctly', () => { + let stub = sinon.stub(utils, 'getTopWindowUrl').returns('http://www.test.com/page.html'); + + let response; + response = spec.buildRequests([bidRequest])[0]; + expect(response.data.site.domain).to.equal('www.test.com'); + expect(response.data.site.page).to.equal('http://www.test.com/page.html'); + expect(response.data.site.ref).to.equal(''); + expect(response.data.imp.length).to.equal(1); + expect(response.data.imp[0].id).to.equal(bidRequest.transactionId); + expect(response.data.imp[0].instl).to.equal(0); + expect(response.data.imp[0].tagid).to.equal(bidRequest.adUnitCode); + expect(response.data.imp[0].bidfloor).to.equal(0); + expect(response.data.imp[0].bidfloorcur).to.equal('USD'); + + const bidRequestWithInstlEquals1 = utils.deepClone(bidRequest); + bidRequestWithInstlEquals1.params.instl = 1; + response = spec.buildRequests([bidRequestWithInstlEquals1])[0]; + expect(response.data.imp[0].instl).to.equal(bidRequestWithInstlEquals1.params.instl); + + const bidRequestWithInstlEquals0 = utils.deepClone(bidRequest); + bidRequestWithInstlEquals0.params.instl = 1; + response = spec.buildRequests([bidRequestWithInstlEquals0])[0]; + expect(response.data.imp[0].instl).to.equal(bidRequestWithInstlEquals0.params.instl); + + const bidRequestWithBidfloorEquals1 = utils.deepClone(bidRequest); + bidRequestWithBidfloorEquals1.params.bidfloor = 1; + response = spec.buildRequests([bidRequestWithBidfloorEquals1])[0]; + expect(response.data.imp[0].bidfloor).to.equal(bidRequestWithBidfloorEquals1.params.bidfloor); + + stub.restore(); + }); + + it('builds request banner object correctly', () => { + let response; + + const bidRequestWithBanner = utils.deepClone(bidRequest); + bidRequestWithBanner.mediaTypes = { + banner: { + sizes: [[300, 250], [120, 600]] + } + }; + + response = spec.buildRequests([bidRequestWithBanner])[0]; + expect(response.data.imp[0].banner.w).to.equal(bidRequestWithBanner.mediaTypes.banner.sizes[0][0]); + expect(response.data.imp[0].banner.h).to.equal(bidRequestWithBanner.mediaTypes.banner.sizes[0][1]); + expect(response.data.imp[0].banner.pos).to.equal(0); + expect(response.data.imp[0].banner.topframe).to.equal(0); + + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithBanner); + bidRequestWithPosEquals1.params.pos = 1; + response = spec.buildRequests([bidRequestWithPosEquals1])[0]; + expect(response.data.imp[0].banner.pos).to.equal(bidRequestWithPosEquals1.params.pos); + }); + + it('builds request video object correctly', () => { + let response; + + const bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes = { + video: { + sizes: [[300, 250], [120, 600]] + } + }; + + response = spec.buildRequests([bidRequestWithVideo])[0]; + expect(response.data.imp[0].video.w).to.equal(bidRequestWithVideo.mediaTypes.video.sizes[0][0]); + expect(response.data.imp[0].video.h).to.equal(bidRequestWithVideo.mediaTypes.video.sizes[0][1]); + expect(response.data.imp[0].video.pos).to.equal(0); + expect(response.data.imp[0].video.topframe).to.equal(0); + + const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); + bidRequestWithPosEquals1.params.pos = 1; + response = spec.buildRequests([bidRequestWithPosEquals1])[0]; + expect(response.data.imp[0].video.pos).to.equal(bidRequestWithPosEquals1.params.pos); + }); + + it('builds request video object correctly with multi-dimensions size array', function () { + let bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes.video = { + playerSize: [[304, 254], [305, 255]], + context: 'instream' + }; + + let response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[0].video.w).to.equal(304); + expect(response.data.imp[0].video.h).to.equal(254); + + bidRequestWithVideo = utils.deepClone(bidRequest); + bidRequestWithVideo.mediaTypes.video = { + playerSize: [304, 254] + }; + + response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; + expect(response.data.imp[0].video.w).to.equal(304); + expect(response.data.imp[0].video.h).to.equal(254); + }); + }); + describe('interpretResponse', () => { + const bannerBidRequest = { + 'adUnitCode': 'adunit-code', + 'auctionId': 'fdkhjg3s7ahjja', + 'mediaTypes': { + banner: {} + }, + 'params': { + 'spid': spid + }, + 'sizes': [[300, 250], [300, 600]], + 'bidId': '111' + }; + const videoBidRequest = { + 'adUnitCode': 'adunit-code', + 'auctionId': 'fdkhjg3s7ahjja', + 'mediaTypes': { + video: {} + }, + 'params': { + 'spid': spid + }, + 'sizes': [[300, 250], [300, 600]], + 'bidId': '111' + }; + const rtbResponse = { + 'id': 'imp_5b05b9fde4b09084267a556f', + 'bidid': 'imp_5b05b9fde4b09084267a556f', + 'cur': 'USD', + 'ext': { + 'utrk': [ + {'type': 'iframe', 'url': '//ssp.ynxs.io/user/sync/1'}, + {'type': 'image', 'url': '//ssp.ynxs.io/user/sync/2'} + ] + }, + 'seatbid': [ + { + 'seat': 'testSeatBidA', + 'bid': [ + { + 'id': '0', + 'impid': '1', + 'price': 2.016, + 'adm': '', + 'adomain': ['nike.com'], + 'h': 600, + 'w': 120, + 'ext': { + 'vast_url': 'http://vast.tag.com', + 'utrk': [ + {'type': 'iframe', 'url': '//pix.usersync.io/user-sync'} + ] + } + } + ] + }, + { + 'seat': 'testSeatBidB', + 'bid': [ + { + 'id': '1', + 'impid': '1', + 'price': 3, + 'adid': '542jlhdfd2112jnjf3x', + 'adm': '', + 'adomain': ['adidas.com'], + 'h': 250, + 'w': 300, + 'ext': { + 'utrk': [ + {'type': 'image', 'url': '//pix.usersync.io/user-sync'} + ] + } + } + ] + } + ] + }; + it('fails gracefully on empty response body', () => { + let response; + + response = spec.interpretResponse(undefined, {bidRequest: bannerBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + + response = spec.interpretResponse({}, {bidRequest: bannerBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(0); + }); + it('collects banner bids', () => { + const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: bannerBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + + const ad0 = response[0]; + expect(ad0.requestId).to.equal(bannerBidRequest.bidId); + expect(ad0.cpm).to.equal(rtbResponse.seatbid[1].bid[0].price); + expect(ad0.width).to.equal(rtbResponse.seatbid[1].bid[0].w); + expect(ad0.height).to.equal(rtbResponse.seatbid[1].bid[0].h); + expect(ad0.ttl).to.equal(15 * 60); + expect(ad0.creativeId).to.equal(rtbResponse.seatbid[1].bid[0].crid); + expect(ad0.netRevenue).to.equal(true); + expect(ad0.currency).to.equal(rtbResponse.seatbid[1].bid[0].cur || rtbResponse.cur || 'USD'); + expect(ad0.ad).to.equal(rtbResponse.seatbid[1].bid[0].adm); + expect(ad0.vastXml).to.be.an('undefined'); + expect(ad0.vastUrl).to.be.an('undefined'); + }); + it('collects video bids', () => { + const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: videoBidRequest}); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(1); + + const ad0 = response[0]; + expect(ad0.requestId).to.equal(videoBidRequest.bidId); + expect(ad0.cpm).to.equal(rtbResponse.seatbid[0].bid[0].price); + expect(ad0.width).to.equal(rtbResponse.seatbid[0].bid[0].w); + expect(ad0.height).to.equal(rtbResponse.seatbid[0].bid[0].h); + expect(ad0.ttl).to.equal(15 * 60); + expect(ad0.creativeId).to.equal(rtbResponse.seatbid[0].bid[0].crid); + expect(ad0.netRevenue).to.equal(true); + expect(ad0.currency).to.equal(rtbResponse.seatbid[0].bid[0].cur || rtbResponse.cur || 'USD'); + expect(ad0.ad).to.be.an('undefined'); + expect(ad0.vastXml).to.equal(rtbResponse.seatbid[0].bid[0].adm); + expect(ad0.vastUrl).to.equal(rtbResponse.seatbid[0].bid[0].ext.vast_url); + }); + + it('applies user-syncs', () => { + const response = spec.getUserSyncs({}, [{body: rtbResponse}]); + expect(Array.isArray(response)).to.equal(true); + expect(response.length).to.equal(4); + expect(response[0].type).to.equal(rtbResponse.ext.utrk[0].type); + expect(response[0].url).to.equal(rtbResponse.ext.utrk[0].url + '?gc=missing'); + expect(response[1].type).to.equal(rtbResponse.ext.utrk[1].type); + expect(response[1].url).to.equal(rtbResponse.ext.utrk[1].url + '?gc=missing'); + expect(response[2].type).to.equal(rtbResponse.seatbid[0].bid[0].ext.utrk[0].type); + expect(response[2].url).to.equal(rtbResponse.seatbid[0].bid[0].ext.utrk[0].url + '?gc=missing'); + expect(response[3].type).to.equal(rtbResponse.seatbid[1].bid[0].ext.utrk[0].type); + expect(response[3].url).to.equal(rtbResponse.seatbid[1].bid[0].ext.utrk[0].url + '?gc=missing'); + }); + + it('supports outstream renderers', function () { + const videoResponse = { + 'id': '64f32497-b2f7-48ec-9205-35fc39894d44', + 'bidid': 'imp_5c24924de4b0d106447af333', + 'cur': 'USD', + 'seatbid': [ + { + 'seat': '3668', + 'group': 0, + 'bid': [ + { + 'id': 'gb_1', + 'impid': 'afbb5852-7cea-4a81-aa9a-a41aab505c23', + 'price': 5.0, + 'adid': '1274', + 'nurl': 'https://rtb.gamoshi.io/pix/1275/win_notice/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&p=${AUCTION_PRICE}', + 'adomain': [], + 'adm': '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', + 'cid': '3668', + 'crid': '1274', + 'cat': [], + 'attr': [], + 'h': 250, + 'w': 300, + 'ext': { + 'vast_url': 'https://rtb.gamoshi.io/pix/1275/vast_o/imp_5c24924de4b0d106447af333/im.xml?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&w=300&h=250&vatu=aHR0cHM6Ly9zdGF0aWMuZ2FtYmlkLmlvL2RlbW8vdmFzdC54bWw&vwarv', + 'imptrackers': [ + 'https://rtb.gamoshi.io/pix/1275/imp/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1'] + } + } + ] + } + ], + 'ext': { + 'utrk': [{ + 'type': 'image', + 'url': 'https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675' + }] + } + }; + const videoRequest = utils.deepClone(videoBidRequest); + videoRequest.mediaTypes.video.context = 'outstream'; + const result = spec.interpretResponse({body: videoResponse}, {bidRequest: videoRequest}); + expect(result[0].renderer).to.not.equal(undefined); + }); + + it('supports gdpr consent', function () { + let videoResponse = { + 'id': '64f32497-b2f7-48ec-9205-35fc39894d44', + 'bidid': 'imp_5c24924de4b0d106447af333', + 'cur': 'USD', + 'seatbid': [ + { + 'seat': '3668', + 'group': 0, + 'bid': [ + { + 'id': 'gb_1', + 'impid': 'afbb5852-7cea-4a81-aa9a-a41aab505c23', + 'price': 5.0, + 'adid': '1274', + 'nurl': 'https://rtb.gamoshi.io/pix/1275/win_notice/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&p=${AUCTION_PRICE}', + 'adomain': [], + 'adm': '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', + 'cid': '3668', + 'crid': '1274', + 'cat': [], + 'attr': [], + 'h': 250, + 'w': 300, + 'ext': { + 'vast_url': 'https://rtb.gamoshi.io/pix/1275/vast_o/imp_5c24924de4b0d106447af333/im.xml?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&w=300&h=250&vatu=aHR0cHM6Ly9zdGF0aWMuZ2FtYmlkLmlvL2RlbW8vdmFzdC54bWw&vwarv', + 'imptrackers': [ + 'https://rtb.gamoshi.io/pix/1275/imp/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1'] + } + } + ] + } + ], + 'ext': { + 'utrk': [{ + 'type': 'image', + 'url': 'https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675' + }] + } + }; + let gdprConsent = { + gdprApplies: true, + consentString: 'consent string' + }; + let result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal('https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675&gc=consent%20string'); + + gdprConsent.gdprApplies = false; + result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal('https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675&gc=missing'); + + videoResponse.ext.utrk[0].url = 'https://rtb.gamoshi.io/pix/1275/scm'; + result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + expect(result).to.be.an('array'); + expect(result.length).to.equal(1); + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.equal('https://rtb.gamoshi.io/pix/1275/scm?gc=missing'); + }); + }); +}); diff --git a/test/spec/modules/yieldoneBidAdapter_spec.js b/test/spec/modules/yieldoneBidAdapter_spec.js index e763257f097..abc579514ef 100644 --- a/test/spec/modules/yieldoneBidAdapter_spec.js +++ b/test/spec/modules/yieldoneBidAdapter_spec.js @@ -2,53 +2,51 @@ import { expect } from 'chai'; import { spec } from 'modules/yieldoneBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -const ENDPOINT = '//y.one.impact-ad.jp/h_bid'; +const ENDPOINT = 'https://y.one.impact-ad.jp/h_bid'; +const USER_SYNC_URL = 'https://y.one.impact-ad.jp/push_sync'; +const VIDEO_PLAYER_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/dac-video-prebid.min.js'; describe('yieldoneBidAdapter', function() { const adapter = newBidder(spec); - describe('isBidRequestValid', () => { + describe('isBidRequestValid', function () { let bid = { 'bidder': 'yieldone', 'params': { - placementId: '44082' + placementId: '36891' }, 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], + 'sizes': [[300, 250], [336, 280]], 'bidId': '23beaa6af6cdde', 'bidderRequestId': '19c0c1efdf37e7', 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', }; - it('should return true when required params found', () => { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when placementId not passed correctly', () => { + it('should return false when placementId not passed correctly', function () { bid.params.placementId = ''; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when require params are not passed', () => { + it('should return false when require params are not passed', function () { let bid = Object.assign({}, bid); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests', () => { + describe('buildRequests', function () { let bidRequests = [ { 'bidder': 'yieldone', 'params': { - placementId: '44082' + placementId: '36891' }, 'adUnitCode': 'adunit-code1', - 'sizes': [ - [300, 250] - ], + 'sizes': [[300, 250], [336, 280]], 'bidId': '23beaa6af6cdde', 'bidderRequestId': '19c0c1efdf37e7', 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', @@ -56,12 +54,10 @@ describe('yieldoneBidAdapter', function() { { 'bidder': 'yieldone', 'params': { - placementId: '44337' + placementId: '47919' }, 'adUnitCode': 'adunit-code2', - 'sizes': [ - [300, 250] - ], + 'sizes': [[300, 250]], 'bidId': '382091349b149f"', 'bidderRequestId': '"1f9c98192de251"', 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', @@ -70,27 +66,45 @@ describe('yieldoneBidAdapter', function() { const request = spec.buildRequests(bidRequests); - it('sends bid request to our endpoint via GET', () => { + it('sends bid request to our endpoint via GET', function () { expect(request[0].method).to.equal('GET'); expect(request[1].method).to.equal('GET'); }); - it('attaches source and version to endpoint URL as query params', () => { + it('attaches source and version to endpoint URL as query params', function () { expect(request[0].url).to.equal(ENDPOINT); expect(request[1].url).to.equal(ENDPOINT); }); + + it('parameter sz has more than one size on banner requests', function () { + expect(request[0].data.sz).to.equal('300x250,336x280'); + expect(request[1].data.sz).to.equal('300x250'); + }); + + it('width and height should be set as separate parameters on outstream requests', function () { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes.video = {context: 'outstream'}; + const request = spec.buildRequests([bidRequest]); + expect(request[0].data.w).to.equal('300'); + expect(request[0].data.h).to.equal('250'); + }); + + it('adUnitCode should be sent as uc parameters on any requests', function () { + expect(request[0].data.uc).to.equal('adunit-code1'); + expect(request[1].data.uc).to.equal('adunit-code2'); + }); }); - describe('interpretResponse', () => { - let bidRequest = [ + describe('interpretResponse', function () { + let bidRequestBanner = [ { 'method': 'GET', - 'url': '//y.one.impact-ad.jp/h_bid', + 'url': 'https://y.one.impact-ad.jp/h_bid', 'data': { 'v': 'hb1', - 'p': '44082', - 'w': '300', - 'h': '250', + 'p': '36891', + 'sz': '300x250,336x280', 'cb': 12892917383, 'r': 'http%3A%2F%2Flocalhost%3A9876%2F%3Fid%3D74552836', 'uid': '23beaa6af6cdde', @@ -99,37 +113,98 @@ describe('yieldoneBidAdapter', function() { } ]; - let serverResponse = { + let serverResponseBanner = { body: { 'adTag': '', + 'uid': '23beaa6af6cdde', + 'height': 250, + 'width': 300, 'cpm': 0.0536616, 'crid': '2494768', + 'currency': 'JPY', 'statusMessage': 'Bid available', - 'uid': '23beaa6af6cdde', - 'width': 300, - 'height': 250 + 'dealId': 'P1-FIX-7800-DSP-MON' } }; - it('should get the correct bid response', () => { + it('should get the correct bid response for banner', function () { let expectedResponse = [{ 'requestId': '23beaa6af6cdde', 'cpm': 53.6616, 'width': 300, 'height': 250, 'creativeId': '2494768', - 'dealId': '', + 'dealId': 'P1-FIX-7800-DSP-MON', 'currency': 'JPY', 'netRevenue': true, 'ttl': 3000, 'referrer': '', + 'mediaType': 'banner', 'ad': '' }]; - let result = spec.interpretResponse(serverResponse, bidRequest[0]); - expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + let result = spec.interpretResponse(serverResponseBanner, bidRequestBanner[0]); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].mediaType).to.equal(expectedResponse[0].mediaType); + }); + + let serverResponseVideo = { + body: { + 'uid': '23beaa6af6cdde', + 'height': 360, + 'width': 640, + 'cpm': 0.0536616, + 'dealId': 'P1-FIX-766-DSP-MON', + 'crid': '2494768', + 'currency': 'JPY', + 'statusMessage': 'Bid available', + 'adm': '' + } + }; + + let bidRequestVideo = [ + { + 'method': 'GET', + 'url': 'https://y.one.impact-ad.jp/h_bid', + 'data': { + 'v': 'hb1', + 'p': '41993', + 'w': '640', + 'h': '360', + 'cb': 12892917383, + 'r': 'http%3A%2F%2Flocalhost%3A9876%2F%3Fid%3D74552836', + 'uid': '23beaa6af6cdde', + 't': 'i' + } + } + ]; + + it('should get the correct bid response for video', function () { + let expectedResponse = [{ + 'requestId': '23beaa6af6cdde', + 'cpm': 53.6616, + 'width': 640, + 'height': 360, + 'creativeId': '2494768', + 'dealId': 'P1-FIX-7800-DSP-MON', + 'currency': 'JPY', + 'netRevenue': true, + 'ttl': 3000, + 'referrer': '', + 'mediaType': 'video', + 'vastXml': '', + 'renderer': { + id: '23beaa6af6cdde', + url: VIDEO_PLAYER_URL + } + }]; + let result = spec.interpretResponse(serverResponseVideo, bidRequestVideo[0]); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].mediaType).to.equal(expectedResponse[0].mediaType); + expect(result[0].renderer.id).to.equal(expectedResponse[0].renderer.id); + expect(result[0].renderer.url).to.equal(expectedResponse[0].renderer.url); }); - it('handles empty bid response', () => { + it('handles empty bid response', function () { let response = { body: { 'uid': '2c0b634db95a01', @@ -140,8 +215,22 @@ describe('yieldoneBidAdapter', function() { 'cpm': 0 } }; - let result = spec.interpretResponse(response, bidRequest[0]); + let result = spec.interpretResponse(response, bidRequestBanner[0]); expect(result.length).to.equal(0); }); }); + + describe('getUserSyncs', function () { + it('handles empty sync options', function () { + expect(spec.getUserSyncs({})).to.be.undefined; + }); + + it('should return a sync url if iframe syncs are enabled', function () { + expect(spec.getUserSyncs({ + 'iframeEnabled': true + })).to.deep.equal([{ + type: 'iframe', url: USER_SYNC_URL + }]); + }); + }); }); diff --git a/test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js b/test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js index 29d23d66bd2..f6efa077f35 100644 --- a/test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js +++ b/test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js @@ -1,48 +1,48 @@ import yuktamediaAnalyticsAdapter from 'modules/yuktamediaAnalyticsAdapter'; import { expect } from 'chai'; -let adaptermanager = require('src/adaptermanager'); +let adapterManager = require('src/adapterManager').default; let events = require('src/events'); let constants = require('src/constants.json'); -describe('YuktaMedia analytics adapter', () => { +describe('YuktaMedia analytics adapter', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = request => requests.push(request); sinon.stub(events, 'getEvents').returns([]); }); - afterEach(() => { + afterEach(function () { xhr.restore(); events.getEvents.restore(); }); - describe('track', () => { + describe('track', function () { let initOptions = { pubId: '1', pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==' }; - adaptermanager.registerAnalyticsAdapter({ + adapterManager.registerAnalyticsAdapter({ code: 'yuktamedia', adapter: yuktamediaAnalyticsAdapter }); - beforeEach(() => { - adaptermanager.enableAnalytics({ + beforeEach(function () { + adapterManager.enableAnalytics({ provider: 'yuktamedia', options: initOptions }); }); - afterEach(() => { + afterEach(function () { yuktamediaAnalyticsAdapter.disableAnalytics(); }); - it('builds and sends auction data', () => { + it('builds and sends auction data', function () { let auctionTimestamp = 1496510254313; let bidRequest = { 'bidderCode': 'appnexus', diff --git a/test/spec/modules/zedoBidAdapter_spec.js b/test/spec/modules/zedoBidAdapter_spec.js new file mode 100644 index 00000000000..76301ba9018 --- /dev/null +++ b/test/spec/modules/zedoBidAdapter_spec.js @@ -0,0 +1,354 @@ +import { expect } from 'chai'; +import { spec } from 'modules/zedoBidAdapter'; + +describe('The ZEDO bidding adapter', function () { + describe('isBidRequestValid', function () { + it('should return false when given an invalid bid', function () { + const bid = { + bidder: 'zedo', + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }); + + it('should return true when given a channelcode bid', function () { + const bid = { + bidder: 'zedo', + params: { + channelCode: 20000000, + dimId: 9 + }, + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const bidderRequest = { + timeout: 3000, + }; + + it('should properly build a channelCode request for dim Id with type not defined', function () { + const bidRequests = [ + { + bidder: 'zedo', + adUnitCode: 'p12345', + transactionId: '12345667', + sizes: [[300, 200]], + params: { + channelCode: 20000000, + dimId: 10, + pubId: 1 + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.match(/^\/\/saxp.zedo.com\/asw\/fmh.json/); + expect(request.method).to.equal('GET'); + const zedoRequest = request.data; + expect(zedoRequest).to.equal('g={"placements":[{"network":20,"channel":0,"publisher":1,"width":300,"height":200,"dimension":10,"version":"$prebid.version$","keyword":"","transactionId":"12345667","renderers":[{"name":"display"}]}]}'); + }); + + it('should properly build a channelCode request for video with type defined', function () { + const bidRequests = [ + { + bidder: 'zedo', + adUnitCode: 'p12345', + transactionId: '12345667', + sizes: [640, 480], + mediaTypes: { + video: { + context: 'instream', + }, + }, + params: { + channelCode: 20000000, + dimId: 85 + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.match(/^\/\/saxp.zedo.com\/asw\/fmh.json/); + expect(request.method).to.equal('GET'); + const zedoRequest = request.data; + expect(zedoRequest).to.equal('g={"placements":[{"network":20,"channel":0,"publisher":0,"width":640,"height":480,"dimension":85,"version":"$prebid.version$","keyword":"","transactionId":"12345667","renderers":[{"name":"Inarticle"}]}]}'); + }); + + describe('buildGDPRRequests', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + const bidderRequest = { + timeout: 3000, + gdprConsent: { + 'consentString': consentString, + 'gdprApplies': true + } + }; + + it('should properly build request with gdpr consent', function () { + const bidRequests = [ + { + bidder: 'zedo', + adUnitCode: 'p12345', + transactionId: '12345667', + sizes: [[300, 200]], + params: { + channelCode: 20000000, + dimId: 10 + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.method).to.equal('GET'); + const zedoRequest = request.data; + expect(zedoRequest).to.equal('g={"placements":[{"network":20,"channel":0,"publisher":0,"width":300,"height":200,"dimension":10,"version":"$prebid.version$","keyword":"","transactionId":"12345667","renderers":[{"name":"display"}]}],"gdpr":1,"gdpr_consent":"BOJ8RZsOJ8RZsABAB8AAAAAZ+A=="}'); + }); + }); + }); + describe('interpretResponse', function () { + it('should return an empty array when there is bid response', function () { + const response = {}; + const request = { bidRequests: [] }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(0); + }); + + it('should properly parse a bid response with no valid creative', function () { + const response = { + body: { + ad: [ + { + 'slotId': 'ad1d762', + 'network': '2000', + 'creatives': [ + { + 'adId': '12345', + 'height': '600', + 'width': '160', + 'isFoc': true, + 'creativeDetails': { + 'type': 'StdBanner', + 'adContent': { + 'focImage': { + 'url': 'https://c13.zedo.com/OzoDB/0/0/0/blank.gif', + 'target': '_blank', + } + } + }, + 'cpm': '0' + } + ] + } + ] + } + }; + const request = { + bidRequests: [{ + bidder: 'zedo', + adUnitCode: 'p12345', + bidId: 'test-bidId', + params: { + channelCode: 2000000, + dimId: 9 + } + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(0); + }); + + it('should properly parse a bid response with valid display creative', function () { + const response = { + body: { + ad: [ + { + 'slotId': 'ad1d762', + 'network': '2000', + 'creatives': [ + { + 'adId': '12345', + 'height': '600', + 'width': '160', + 'isFoc': true, + 'creativeDetails': { + 'type': 'StdBanner', + 'adContent': '' + }, + 'cpm': '1200000' + } + ] + } + ] + } + }; + const request = { + bidRequests: [{ + bidder: 'zedo', + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + params: { + channelCode: 2000000, + dimId: 9 + }, + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('ad1d762'); + expect(bids[0].cpm).to.equal(0.72); + expect(bids[0].width).to.equal('160'); + expect(bids[0].height).to.equal('600'); + }); + + it('should properly parse a bid response with valid video creative', function () { + const response = { + body: { + ad: [ + { + 'slotId': 'ad1d762', + 'network': '2000', + 'creatives': [ + { + 'adId': '12345', + 'height': '480', + 'width': '640', + 'isFoc': true, + 'creativeDetails': { + 'type': 'VAST', + 'adContent': '' + }, + 'cpm': '1200000' + } + ] + } + ] + } + }; + const request = { + bidRequests: [{ + bidder: 'zedo', + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + params: { + channelCode: 2000000, + dimId: 85 + }, + }] + }; + + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('ad1d762'); + expect(bids[0].cpm).to.equal(0.78); + expect(bids[0].width).to.equal('640'); + expect(bids[0].height).to.equal('480'); + expect(bids[0].adType).to.equal('VAST'); + expect(bids[0].vastXml).to.not.equal(''); + expect(bids[0].ad).to.be.an('undefined'); + expect(bids[0].renderer).not.to.be.an('undefined'); + }); + }); + + describe('user sync', function () { + it('should register the iframe sync url', function () { + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + }); + + it('should pass gdpr params', function () { + let syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { + gdprApplies: false, consentString: 'test' + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.contains('gdpr=0'); + }); + }); + + describe('bid events', function () { + it('should trigger a win pixel', function () { + const bid = { + 'bidderCode': 'zedo', + 'width': '300', + 'height': '250', + 'statusMessage': 'Bid available', + 'adId': '148018fe5e', + 'cpm': 0.5, + 'ad': 'dummy data', + 'ad_id': '12345', + 'sizeId': '15', + 'adResponse': + { + 'creatives': [ + { + 'adId': '12345', + 'height': '480', + 'width': '640', + 'isFoc': true, + 'creativeDetails': { + 'type': 'VAST', + 'adContent': '' + }, + 'seeder': { + 'network': 1234, + 'servedChan': 1234567, + }, + 'cpm': '1200000', + 'servedChan': 1234, + }] + }, + 'params': [{ + 'channelCode': '123456', + 'dimId': '85' + }], + 'requestTimestamp': 1540401686, + 'responseTimestamp': 1540401687, + 'timeToRespond': 6253, + 'pbLg': '0.50', + 'pbMg': '0.50', + 'pbHg': '0.53', + 'adUnitCode': '/123456/header-bid-tag-0', + 'bidder': 'zedo', + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'zedo', + 'hb_adid': '148018fe5e', + 'hb_pb': '10.00', + } + }; + spec.onBidWon(bid); + spec.onTimeout(bid); + }); + it('should trigger a timeout pixel', function () { + const bid = { + 'bidderCode': 'zedo', + 'width': '300', + 'height': '250', + 'statusMessage': 'Bid available', + 'adId': '148018fe5e', + 'cpm': 0.5, + 'ad': 'dummy data', + 'ad_id': '12345', + 'sizeId': '15', + 'params': [{ + 'channelCode': '123456', + 'dimId': '85' + }], + 'timeout': 1, + 'requestTimestamp': 1540401686, + 'responseTimestamp': 1540401687, + 'timeToRespond': 6253, + 'adUnitCode': '/123456/header-bid-tag-0', + 'bidder': 'zedo', + 'size': '300x250', + }; + spec.onBidWon(bid); + spec.onTimeout(bid); + }); + }); +}); diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 653f858576f..2fb2fd810fa 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -1,12 +1,24 @@ import { expect } from 'chai'; -import { fireNativeTrackers, getNativeTargeting, nativeBidIsValid } from 'src/native'; +import { fireNativeTrackers, getNativeTargeting, nativeBidIsValid, getAssetMessage } from 'src/native'; +import CONSTANTS from 'src/constants.json'; const utils = require('src/utils'); const bid = { + adId: '123', native: { title: 'Native Creative', body: 'Cool description great stuff', cta: 'Do it', + image: { + url: 'http://cdn.example.com/p/creative-image/image.png', + height: 83, + width: 127 + }, + icon: { + url: 'http://cdn.example.com/p/creative-image/icon.jpg', + height: 742, + width: 989 + }, sponsoredBy: 'AppNexus', clickUrl: 'https://www.link.example', clickTrackers: ['https://tracker.example'], @@ -15,42 +27,107 @@ const bid = { } }; -describe('native.js', () => { +const bidWithUndefinedFields = { + native: { + title: 'Native Creative', + body: undefined, + cta: undefined, + sponsoredBy: 'AppNexus', + clickUrl: 'https://www.link.example', + clickTrackers: ['https://tracker.example'], + impressionTrackers: ['https://impression.example'], + javascriptTrackers: '' + } +}; + +describe('native.js', function () { let triggerPixelStub; let insertHtmlIntoIframeStub; - beforeEach(() => { + beforeEach(function () { triggerPixelStub = sinon.stub(utils, 'triggerPixel'); insertHtmlIntoIframeStub = sinon.stub(utils, 'insertHtmlIntoIframe'); }); - afterEach(() => { + afterEach(function () { utils.triggerPixel.restore(); utils.insertHtmlIntoIframe.restore(); }); - it('gets native targeting keys', () => { + it('gets native targeting keys', function () { const targeting = getNativeTargeting(bid); - expect(targeting.hb_native_title).to.equal(bid.native.title); - expect(targeting.hb_native_body).to.equal(bid.native.body); - expect(targeting.hb_native_linkurl).to.equal(bid.native.clickUrl); + expect(targeting[CONSTANTS.NATIVE_KEYS.title]).to.equal(bid.native.title); + expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal(bid.native.body); + expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal(bid.native.clickUrl); + }); + + it('sends placeholders for configured assets', function () { + const bidRequest = { + mediaTypes: { + native: { + body: { sendId: true }, + clickUrl: { sendId: true }, + } + } + }; + const targeting = getNativeTargeting(bid, bidRequest); + + expect(targeting[CONSTANTS.NATIVE_KEYS.title]).to.equal(bid.native.title); + expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal('hb_native_body:123'); + expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal('hb_native_linkurl:123'); + }); + + it('should only include native targeting keys with values', function () { + const targeting = getNativeTargeting(bidWithUndefinedFields); + + expect(Object.keys(targeting)).to.deep.equal([ + CONSTANTS.NATIVE_KEYS.title, + CONSTANTS.NATIVE_KEYS.sponsoredBy, + CONSTANTS.NATIVE_KEYS.clickUrl + ]); }); - it('fires impression trackers', () => { + it('fires impression trackers', function () { fireNativeTrackers({}, bid); sinon.assert.calledOnce(triggerPixelStub); sinon.assert.calledWith(triggerPixelStub, bid.native.impressionTrackers[0]); sinon.assert.calledWith(insertHtmlIntoIframeStub, bid.native.javascriptTrackers); }); - it('fires click trackers', () => { - fireNativeTrackers({ action: 'click' }, bid); + it('fires click trackers', function () { + const trackerType = fireNativeTrackers({ action: 'click' }, bid); + expect(trackerType).to.equal('click'); sinon.assert.calledOnce(triggerPixelStub); sinon.assert.calledWith(triggerPixelStub, bid.native.clickTrackers[0]); }); + + it('creates native asset message', function() { + const messageRequest = { + message: 'Prebid Native', + action: 'assetRequest', + adId: '123', + assets: ['hb_native_body', 'hb_native_image', 'hb_native_linkurl'], + }; + + const message = getAssetMessage(messageRequest, bid); + + expect(message.assets.length).to.equal(3); + expect(message.assets).to.deep.include({ + key: 'body', + value: bid.native.body + }); + expect(message.assets).to.deep.include({ + key: 'image', + value: bid.native.image.url + }); + expect(message.assets).to.deep.include({ + key: 'clickUrl', + value: bid.native.clickUrl + }); + }); }); -describe('validate native', () => { +describe('validate native', function () { let bidReq = [{ bids: [{ bidderCode: 'test_bidder', @@ -78,7 +155,8 @@ describe('validate native', () => { }]; let validBid = { - adId: 'test_bid_id', + adId: 'abc123', + requestId: 'test_bid_id', adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { @@ -102,7 +180,8 @@ describe('validate native', () => { }; let noIconDimBid = { - adId: 'test_bid_id', + adId: 'abc234', + requestId: 'test_bid_id', adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { @@ -126,7 +205,8 @@ describe('validate native', () => { }; let noImgDimBid = { - adId: 'test_bid_id', + adId: 'abc345', + requestId: 'test_bid_id', adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { @@ -149,11 +229,11 @@ describe('validate native', () => { } }; - beforeEach(() => {}); + beforeEach(function () {}); - afterEach(() => {}); + afterEach(function () {}); - it('should reject bid if no image sizes are defined', () => { + it('should reject bid if no image sizes are defined', function () { let result = nativeBidIsValid(validBid, bidReq); expect(result).to.be.true; result = nativeBidIsValid(noIconDimBid, bidReq); diff --git a/test/spec/refererDetection_spec.js b/test/spec/refererDetection_spec.js new file mode 100644 index 00000000000..b2ef4e2058f --- /dev/null +++ b/test/spec/refererDetection_spec.js @@ -0,0 +1,87 @@ +import { detectReferer } from 'src/refererDetection'; +import { expect } from 'chai'; + +var mocks = { + createFakeWindow: function (referrer, href) { + return { + document: { + referrer: referrer + }, + location: { + href: href, + // TODO: add ancestorOrigins to increase test coverage + }, + parent: null, + top: null + }; + } +} + +describe('referer detection', () => { + it('should return referer details in nested friendly iframes', function() { + // Fake window object to test friendly iframes + // - Main page http://example.com/page.html + // - - Iframe1 http://example.com/iframe1.html + // - - - Iframe2 http://example.com/iframe2.html + let mockIframe2WinObject = mocks.createFakeWindow('http://example.com/iframe1.html', 'http://example.com/iframe2.html'); + let mockIframe1WinObject = mocks.createFakeWindow('http://example.com/page.html', 'http://example.com/iframe1.html'); + let mainWinObject = mocks.createFakeWindow('http://example.com/page.html', 'http://example.com/page.html'); + mainWinObject.document.querySelector = function() { + return { + href: 'http://prebid.org' + } + } + mockIframe2WinObject.parent = mockIframe1WinObject; + mockIframe2WinObject.top = mainWinObject; + mockIframe1WinObject.parent = mainWinObject; + mockIframe1WinObject.top = mainWinObject; + mainWinObject.top = mainWinObject; + + const getRefererInfo = detectReferer(mockIframe2WinObject); + let result = getRefererInfo(); + let expectedResult = { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2, + stack: [ + 'http://example.com/page.html', + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ], + canonicalUrl: 'http://prebid.org' + }; + expect(result).to.deep.equal(expectedResult); + }); + + it('should return referer details in nested cross domain iframes', function() { + // Fake window object to test cross domain iframes. + // - Main page http://example.com/page.html + // - - Iframe1 http://aaa.com/iframe1.html + // - - - Iframe2 http://bbb.com/iframe2.html + let mockIframe2WinObject = mocks.createFakeWindow('http://aaa.com/iframe1.html', 'http://bbb.com/iframe2.html'); + // Sinon cannot throw exception when accessing a propery so passing null to create cross domain + // environment for refererDetection module + let mockIframe1WinObject = mocks.createFakeWindow(null, null); + let mainWinObject = mocks.createFakeWindow(null, null); + mockIframe2WinObject.parent = mockIframe1WinObject; + mockIframe2WinObject.top = mainWinObject; + mockIframe1WinObject.parent = mainWinObject; + mockIframe1WinObject.top = mainWinObject; + mainWinObject.top = mainWinObject; + + const getRefererInfo = detectReferer(mockIframe2WinObject); + let result = getRefererInfo(); + let expectedResult = { + referer: 'http://aaa.com/iframe1.html', + reachedTop: false, + numIframes: 2, + stack: [ + null, + 'http://aaa.com/iframe1.html', + 'http://bbb.com/iframe2.html' + ], + canonicalUrl: undefined + }; + expect(result).to.deep.equal(expectedResult); + }); +}); diff --git a/test/spec/renderer_spec.js b/test/spec/renderer_spec.js index a511a1b214c..f9a670c1315 100644 --- a/test/spec/renderer_spec.js +++ b/test/spec/renderer_spec.js @@ -1,107 +1,131 @@ import { expect } from 'chai'; import { Renderer } from 'src/Renderer'; -const adloader = require('../../src/adloader'); - -describe('Renderer: A renderer installed on a bid response', () => { - let testRenderer1; - let testRenderer2; - let spyRenderFn; - let spyEventHandler; - - let loadScriptStub; - - beforeEach(() => { - loadScriptStub = sinon.stub(adloader, 'loadScript').callsFake((...args) => { - args[1](); +import * as utils from 'src/utils'; + +describe('Renderer', function () { + describe('Renderer: A renderer installed on a bid response', function () { + let testRenderer1; + let testRenderer2; + let spyRenderFn; + let spyEventHandler; + + beforeEach(function () { + testRenderer1 = Renderer.install({ + url: 'https://httpbin.org/post', + config: { test: 'config1' }, + id: 1 + }); + testRenderer2 = Renderer.install({ + url: 'https://httpbin.org/post', + config: { test: 'config2' }, + id: 2 + }); + + spyRenderFn = sinon.spy(); + spyEventHandler = sinon.spy(); }); - testRenderer1 = Renderer.install({ - url: 'https://httpbin.org/post', - config: { test: 'config1' }, - id: 1 + it('is an instance of Renderer', function () { + expect(testRenderer1 instanceof Renderer).to.equal(true); }); - testRenderer2 = Renderer.install({ - url: 'https://httpbin.org/post', - config: { test: 'config2' }, - id: 2 + + it('has expected properties ', function () { + expect(testRenderer1.url).to.equal('https://httpbin.org/post'); + expect(testRenderer1.config).to.deep.equal({ test: 'config1' }); + expect(testRenderer1.id).to.equal(1); }); - spyRenderFn = sinon.spy(); - spyEventHandler = sinon.spy(); - }); + it('returns config from getConfig method', function () { + expect(testRenderer1.getConfig()).to.deep.equal({ test: 'config1' }); + expect(testRenderer2.getConfig()).to.deep.equal({ test: 'config2' }); + }); - afterEach(() => { - loadScriptStub.restore(); - }); + it('sets a render function with setRender method', function () { + testRenderer1.setRender(spyRenderFn); + expect(typeof testRenderer1.render).to.equal('function'); + testRenderer1.render(); + expect(spyRenderFn.called).to.equal(true); + }); - it('is an instance of Renderer', () => { - expect(testRenderer1 instanceof Renderer).to.equal(true); - }); + it('sets event handlers with setEventHandlers method and handles events with installed handlers', function () { + testRenderer1.setEventHandlers({ + testEvent: spyEventHandler + }); - it('has expected properties ', () => { - expect(testRenderer1.url).to.equal('https://httpbin.org/post'); - expect(testRenderer1.config).to.deep.equal({ test: 'config1' }); - expect(testRenderer1.id).to.equal(1); - }); + expect(testRenderer1.handlers).to.deep.equal({ + testEvent: spyEventHandler + }); - it('returns config from getConfig method', () => { - expect(testRenderer1.getConfig()).to.deep.equal({ test: 'config1' }); - expect(testRenderer2.getConfig()).to.deep.equal({ test: 'config2' }); - }); + testRenderer1.handleVideoEvent({ id: 1, eventName: 'testEvent' }); + expect(spyEventHandler.called).to.equal(true); + }); - it('sets a render function with setRender method', () => { - testRenderer1.setRender(spyRenderFn); - expect(typeof testRenderer1.render).to.equal('function'); - testRenderer1.render(); - expect(spyRenderFn.called).to.equal(true); - }); + it('pushes commands to queue if renderer is not loaded', function () { + testRenderer1.loaded = false; + testRenderer1.push(spyRenderFn); + expect(testRenderer1.cmd.length).to.equal(1); - it('sets event handlers with setEventHandlers method and handles events with installed handlers', () => { - testRenderer1.setEventHandlers({ - testEvent: spyEventHandler + // clear queue for next tests + testRenderer1.cmd = []; }); - expect(testRenderer1.handlers).to.deep.equal({ - testEvent: spyEventHandler - }); + it('fires commands immediately if the renderer is loaded', function () { + const func = sinon.spy(); - testRenderer1.handleVideoEvent({ id: 1, eventName: 'testEvent' }); - expect(spyEventHandler.called).to.equal(true); - }); + testRenderer1.loaded = true; + testRenderer1.push(func); - it('pushes commands to queue if renderer is not loaded', () => { - testRenderer1.loaded = false; - testRenderer1.push(spyRenderFn); - expect(testRenderer1.cmd.length).to.equal(1); + expect(testRenderer1.cmd.length).to.equal(0); - // clear queue for next tests - testRenderer1.cmd = []; - }); + sinon.assert.calledOnce(func); + }); - it('fires commands immediately if the renderer is loaded', () => { - const func = sinon.spy(); + it('processes queue by calling each function in queue', function () { + testRenderer1.loaded = false; + const func1 = sinon.spy(); + const func2 = sinon.spy(); - testRenderer1.loaded = true; - testRenderer1.push(func); + testRenderer1.push(func1); + testRenderer1.push(func2); + expect(testRenderer1.cmd.length).to.equal(2); - expect(testRenderer1.cmd.length).to.equal(0); + testRenderer1.process(); - sinon.assert.calledOnce(func); + sinon.assert.calledOnce(func1); + sinon.assert.calledOnce(func2); + expect(testRenderer1.cmd.length).to.equal(0); + }); }); - it('processes queue by calling each function in queue', () => { - testRenderer1.loaded = false; - const func1 = sinon.spy(); - const func2 = sinon.spy(); - - testRenderer1.push(func1); - testRenderer1.push(func2); - expect(testRenderer1.cmd.length).to.equal(2); + describe('3rd party renderer', function () { + let adUnitsOld; + let utilsSpy; + before(function () { + adUnitsOld = $$PREBID_GLOBAL$$.adUnits; + utilsSpy = sinon.spy(utils, 'logWarn'); + }); - testRenderer1.process(); + after(function() { + $$PREBID_GLOBAL$$.adUnits = adUnitsOld; + utilsSpy.restore(); + }); - sinon.assert.calledOnce(func1); - sinon.assert.calledOnce(func2); - expect(testRenderer1.cmd.length).to.equal(0); + it('should not load renderer and log warn message', function() { + $$PREBID_GLOBAL$$.adUnits = [{ + code: 'video1', + renderer: { + url: 'http://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + render: sinon.spy() + } + }] + + let testRenderer = Renderer.install({ + url: 'https://httpbin.org/post', + config: { test: 'config1' }, + id: 1, + adUnitCode: 'video1' + }); + expect(utilsSpy.callCount).to.equal(1); + }); }); }); diff --git a/test/spec/sizeMapping_spec.js b/test/spec/sizeMapping_spec.js index 74b86a8c5aa..254dcb8003e 100644 --- a/test/spec/sizeMapping_spec.js +++ b/test/spec/sizeMapping_spec.js @@ -1,12 +1,16 @@ import { expect } from 'chai'; -import { resolveStatus, setSizeConfig } from 'src/sizeMapping'; +import { resolveStatus, setSizeConfig, sizeSupported } from 'src/sizeMapping'; import includes from 'core-js/library/fn/array/includes'; let utils = require('src/utils'); let deepClone = utils.deepClone; -describe('sizeMapping', () => { - var testSizes = [[970, 90], [728, 90], [300, 250], [300, 100], [80, 80]]; +describe('sizeMapping', function () { + var testSizes = { + banner: { + sizes: [[970, 90], [728, 90], [300, 250], [300, 100], [80, 80]] + } + }; var sizeConfig = [{ 'mediaQuery': '(min-width: 1200px)', @@ -49,161 +53,280 @@ describe('sizeMapping', () => { let sandbox, matchMediaOverride; - beforeEach(() => { + beforeEach(function () { setSizeConfig(sizeConfig); sandbox = sinon.sandbox.create(); matchMediaOverride = {matches: false}; - sandbox.stub(window, 'matchMedia').callsFake((...args) => { + sandbox.stub(utils.getWindowTop(), 'matchMedia').callsFake((...args) => { if (typeof matchMediaOverride === 'function') { - return matchMediaOverride.apply(window, args); + return matchMediaOverride.apply(utils.getWindowTop(), args); } return matchMediaOverride; }); }); - afterEach(() => { + afterEach(function () { setSizeConfig([]); sandbox.restore(); }); - describe('when handling sizes', () => { - it('should log a warning when mediaQuery property missing from sizeConfig', () => { + describe('when handling sizes', function () { + it('should allow us to validate a single size', function() { + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + + expect(sizeSupported([300, 250])).to.equal(true); + expect(sizeSupported([80, 80])).to.equal(false); + }); + + it('should log a warning when mediaQuery property missing from sizeConfig', function () { let errorConfig = deepClone(sizeConfig); delete errorConfig[0].mediaQuery; sandbox.stub(utils, 'logWarn'); - resolveStatus(undefined, testSizes, errorConfig); + resolveStatus(undefined, testSizes, undefined, errorConfig); expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); }); - it('when one mediaQuery block matches, it should filter the adUnit.sizes passed in', () => { + it('should allow deprecated adUnit.sizes', function() { matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfig); + let status = resolveStatus(undefined, undefined, testSizes.banner.sizes, sizeConfig); - expect(status).to.deep.equal({ - active: true, - sizes: [[970, 90], [728, 90], [300, 250]] - }) + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[970, 90], [728, 90], [300, 250]] + } + }); }); - it('when multiple mediaQuery block matches, it should filter a union of the matched sizesSupported', () => { + it('when one mediaQuery block matches, it should filter the adUnit.sizes passed in', function () { + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[970, 90], [728, 90], [300, 250]] + } + }); + }); + + it('when multiple mediaQuery block matches, it should filter a union of the matched sizesSupported', function () { matchMediaOverride = (str) => includes([ '(min-width: 1200px)', '(min-width: 768px) and (max-width: 1199px)' ], str) ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfig); - expect(status).to.deep.equal({ - active: true, - sizes: [[970, 90], [728, 90], [300, 250], [300, 100]] - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[970, 90], [728, 90], [300, 250], [300, 100]] + } + }); }); - it('if no mediaQueries match, it should allow all sizes specified', () => { + it('if no mediaQueries match, it should allow all sizes specified', function () { matchMediaOverride = () => ({matches: false}); - let status = resolveStatus(undefined, testSizes, sizeConfig); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); }); - it('if a mediaQuery matches and has sizesSupported: [], it should filter all sizes', () => { + it('if a mediaQuery matches and has sizesSupported: [], it should filter all sizes', function () { matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfig); - expect(status).to.deep.equal({ - active: false, - sizes: [] - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [] + } + }); }); - it('if a mediaQuery matches and no sizesSupported specified, it should not effect adUnit.sizes', () => { + it('should filter all banner sizes and should disable the adUnit even if other mediaTypes are present', function () { + matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; + let status = resolveStatus(undefined, Object.assign({}, testSizes, { + native: { + type: 'image' + } + }), undefined, sizeConfig); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [] + }, + native: { + type: 'image' + } + }); + }); + + it('if a mediaQuery matches and no sizesSupported specified, it should not affect adUnit.sizes', function () { matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfigWithLabels); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); }); }); - describe('when handling labels', () => { - it('should activate/deactivate adUnits/bidders based on sizeConfig.labels', () => { + describe('when handling labels', function () { + it('should activate/deactivate adUnits/bidders based on sizeConfig.labels', function () { matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; let status = resolveStatus({ labels: ['desktop'] - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); expect(status).to.deep.equal({ active: true, - sizes: testSizes + mediaTypes: testSizes }); status = resolveStatus({ labels: ['tablet'] - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: false, - sizes: testSizes + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(testSizes); + }); + + it('should activate/decactivate adUnits/bidders based on labels with multiformat ads', function () { + matchMediaOverride = (str) => str === '(min-width: 768px) and (max-width: 1199px)' ? {matches: true} : {matches: false}; + + let multiFormatSizes = { + banner: { + sizes: [[728, 90], [300, 300]] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [300, 300] + } + }; + + let status = resolveStatus({ + labels: ['tablet', 'test'], + labelAll: true + }, multiFormatSizes, undefined, sizeConfigWithLabels); + + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[728, 90]] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [300, 300] + } + }); + + status = resolveStatus({ + labels: ['tablet'] + }, multiFormatSizes, undefined, sizeConfigWithLabels); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[728, 90]] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [300, 300] + } + }); + + multiFormatSizes.banner.sizes.splice(0, 1, [728, 80]); + status = resolveStatus({ + labels: ['tablet'] + }, multiFormatSizes, undefined, sizeConfigWithLabels); + + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [] + }, + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [300, 300] + } + }); + + delete multiFormatSizes.banner; + status = resolveStatus({ + labels: ['tablet'] + }, multiFormatSizes, undefined, sizeConfigWithLabels); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + native: { + type: 'image' + }, + video: { + context: 'outstream', + playerSize: [300, 300] + } }); }); - it('should active/deactivate adUnits/bidders based on requestBids labels', () => { + it('should active/deactivate adUnits/bidders based on requestBids labels', function () { let activeLabels = ['us-visitor', 'desktop', 'smart']; let status = resolveStatus({ - labels: ['uk-visitor'], - activeLabels - }, testSizes, sizeConfigWithLabels); + labels: ['uk-visitor'], // from adunit + activeLabels // from requestBids.labels + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: false, - sizes: testSizes - }); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(testSizes); status = resolveStatus({ labels: ['us-visitor'], activeLabels - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); status = resolveStatus({ labels: ['us-visitor', 'tablet'], labelAll: true, activeLabels - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: false, - sizes: testSizes - }); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(testSizes); status = resolveStatus({ labels: ['us-visitor', 'desktop'], labelAll: true, activeLabels - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); }); }); }); diff --git a/test/spec/unit/adServerManager_spec.js b/test/spec/unit/adServerManager_spec.js index 72a3b61ce41..4ae475ac013 100644 --- a/test/spec/unit/adServerManager_spec.js +++ b/test/spec/unit/adServerManager_spec.js @@ -4,12 +4,16 @@ import { registerVideoSupport } from 'src/adServerManager'; const prebid = getGlobal(); -describe('The ad server manager', () => { - beforeEach(() => { +describe('The ad server manager', function () { + before(function () { delete prebid.adServers; }); - it('should register video support to the proper place on the API', () => { + afterEach(function () { + delete prebid.adServers; + }); + + it('should register video support to the proper place on the API', function () { function videoSupport() { } registerVideoSupport('dfp', { buildVideoUrl: videoSupport }); @@ -18,7 +22,7 @@ describe('The ad server manager', () => { expect(prebid.adServers.dfp).to.have.property('buildVideoUrl', videoSupport); }); - it('should keep the first function when we try to add a second', () => { + it('should keep the first function when we try to add a second', function () { function videoSupport() { } registerVideoSupport('dfp', { buildVideoUrl: videoSupport }); registerVideoSupport('dfp', { buildVideoUrl: function noop() { } }); @@ -27,4 +31,13 @@ describe('The ad server manager', () => { expect(prebid.adServers).to.have.property('dfp'); expect(prebid.adServers.dfp).to.have.property('buildVideoUrl', videoSupport); }); + + it('should support any custom named property in the public API', function () { + function getTestAdServerTargetingKeys() { }; + registerVideoSupport('testAdServer', { getTargetingKeys: getTestAdServerTargetingKeys }); + + expect(prebid).to.have.property('adServers'); + expect(prebid.adServers).to.have.property('testAdServer'); + expect(prebid.adServers.testAdServer).to.have.property('getTargetingKeys', getTestAdServerTargetingKeys); + }); }); diff --git a/test/spec/unit/adUnits_spec.js b/test/spec/unit/adUnits_spec.js new file mode 100644 index 00000000000..ff77315ba4a --- /dev/null +++ b/test/spec/unit/adUnits_spec.js @@ -0,0 +1,23 @@ +import { expect } from 'chai'; +import { adunitCounter } from 'src/adUnits'; + +describe('Adunit Counter', function () { + const ADUNIT_ID_1 = 'test1'; + const ADUNIT_ID_2 = 'test2'; + + it('increments and checks counter of adunit 1', function () { + adunitCounter.incrementCounter(ADUNIT_ID_1); + expect(adunitCounter.getCounter(ADUNIT_ID_1)).to.be.equal(1); + }); + it('checks counter of adunit 2', function () { + expect(adunitCounter.getCounter(ADUNIT_ID_2)).to.be.equal(0); + }); + it('increments and checks counter of adunit 1', function () { + adunitCounter.incrementCounter(ADUNIT_ID_1); + expect(adunitCounter.getCounter(ADUNIT_ID_1)).to.be.equal(2); + }); + it('increments and checks counter of adunit 2', function () { + adunitCounter.incrementCounter(ADUNIT_ID_2); + expect(adunitCounter.getCounter(ADUNIT_ID_2)).to.be.equal(1); + }); +}); diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index 8b1c164a804..1933e4a736d 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -1,7 +1,10 @@ import { expect } from 'chai'; -import AdapterManager from 'src/adaptermanager'; -import { checkBidRequestSizes } from 'src/adaptermanager'; -import { getAdUnits } from 'test/fixtures/fixtures'; +import adapterManager, { gdprDataHandler } from 'src/adapterManager'; +import { + getAdUnits, + getServerTestingConfig, + getServerTestingsAds +} from 'test/fixtures/fixtures'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils'; import { config } from 'src/config'; @@ -9,9 +12,8 @@ import { registerBidder } from 'src/adapters/bidderFactory'; import { setSizeConfig } from 'src/sizeMapping'; import find from 'core-js/library/fn/array/find'; import includes from 'core-js/library/fn/array/includes'; -var s2sTesting = require('../../../../modules/s2sTesting'); +import s2sTesting from 'modules/s2sTesting'; var events = require('../../../../src/events'); -const adloader = require('../../../../src/adloader'); const CONFIG = { enabled: true, @@ -19,7 +21,8 @@ const CONFIG = { timeout: 1000, maxBids: 1, adapter: 'prebidServer', - bidders: ['appnexus'] + bidders: ['appnexus'], + accountId: 'abc' }; var prebidServerAdapterMock = { bidder: 'prebidServer', @@ -38,49 +41,44 @@ var rubiconAdapterMock = { bidder: 'rubicon', callBids: sinon.stub() }; -let loadScriptStub; -describe('adapterManager tests', () => { +describe('adapterManager tests', function () { let orgAppnexusAdapter; let orgAdequantAdapter; let orgPrebidServerAdapter; let orgRubiconAdapter; - before(() => { - orgAppnexusAdapter = AdapterManager.bidderRegistry['appnexus']; - orgAdequantAdapter = AdapterManager.bidderRegistry['adequant']; - orgPrebidServerAdapter = AdapterManager.bidderRegistry['prebidServer']; - orgRubiconAdapter = AdapterManager.bidderRegistry['rubicon']; - loadScriptStub = sinon.stub(adloader, 'loadScript').callsFake((...args) => { - args[1](); - }); + before(function () { + orgAppnexusAdapter = adapterManager.bidderRegistry['appnexus']; + orgAdequantAdapter = adapterManager.bidderRegistry['adequant']; + orgPrebidServerAdapter = adapterManager.bidderRegistry['prebidServer']; + orgRubiconAdapter = adapterManager.bidderRegistry['rubicon']; }); - after(() => { - AdapterManager.bidderRegistry['appnexus'] = orgAppnexusAdapter; - AdapterManager.bidderRegistry['adequant'] = orgAdequantAdapter; - AdapterManager.bidderRegistry['prebidServer'] = orgPrebidServerAdapter; - AdapterManager.bidderRegistry['rubicon'] = orgRubiconAdapter; - loadScriptStub.restore(); + after(function () { + adapterManager.bidderRegistry['appnexus'] = orgAppnexusAdapter; + adapterManager.bidderRegistry['adequant'] = orgAdequantAdapter; + adapterManager.bidderRegistry['prebidServer'] = orgPrebidServerAdapter; + adapterManager.bidderRegistry['rubicon'] = orgRubiconAdapter; config.setConfig({s2sConfig: { enabled: false }}); }); - describe('callBids', () => { - before(() => { + describe('callBids', function () { + before(function () { config.setConfig({s2sConfig: { enabled: false }}); }); - beforeEach(() => { + beforeEach(function () { sinon.stub(utils, 'logError'); appnexusAdapterMock.callBids.reset(); - AdapterManager.bidderRegistry['appnexus'] = appnexusAdapterMock; + adapterManager.bidderRegistry['appnexus'] = appnexusAdapterMock; }); - afterEach(() => { + afterEach(function () { utils.logError.restore(); - delete AdapterManager.bidderRegistry['appnexus']; + delete adapterManager.bidderRegistry['appnexus']; }); - it('should log an error if a bidder is used that does not exist', () => { + it('should log an error if a bidder is used that does not exist', function () { const adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], @@ -90,12 +88,13 @@ describe('adapterManager tests', () => { ] }]; - let bidRequests = AdapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); - AdapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); + let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); + expect(bidRequests.length).to.equal(1); + expect(bidRequests[0].bidderCode).to.equal('appnexus'); sinon.assert.called(utils.logError); }); - it('should emit BID_REQUESTED event', () => { + it('should emit BID_REQUESTED event', function () { // function to count BID_REQUESTED events let cnt = 0; let count = () => cnt++; @@ -131,26 +130,129 @@ describe('adapterManager tests', () => { {bidder: 'appnexus', params: {placementId: 'id'}}, ] }]; - AdapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); + adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(1); sinon.assert.calledOnce(appnexusAdapterMock.callBids); events.off(CONSTANTS.EVENTS.BID_REQUESTED, count); }); }); - describe('S2S tests', () => { - beforeEach(() => { + describe('callTimedOutBidders', function () { + var criteoSpec = { onTimeout: sinon.stub() } + var criteoAdapter = { + bidder: 'criteo', + getSpec: function() { return criteoSpec; } + } + before(function () { + config.setConfig({s2sConfig: { enabled: false }}); + }); + + beforeEach(function () { + adapterManager.bidderRegistry['criteo'] = criteoAdapter; + }); + + afterEach(function () { + delete adapterManager.bidderRegistry['criteo']; + }); + + it('should call spec\'s onTimeout callback when callTimedOutBidders is called', function () { + const adUnits = [{ + code: 'adUnit-code', + sizes: [[728, 90]], + bids: [ + {bidder: 'criteo', params: {placementId: 'id'}}, + ] + }]; + const timedOutBidders = [{ + bidId: 'bidId', + bidder: 'criteo', + adUnitCode: adUnits[0].code, + auctionId: 'auctionId', + }]; + adapterManager.callTimedOutBidders(adUnits, timedOutBidders, CONFIG.timeout); + sinon.assert.called(criteoSpec.onTimeout); + }); + }); // end callTimedOutBidders + + describe('onBidWon', function () { + var criteoSpec = { onBidWon: sinon.stub() } + var criteoAdapter = { + bidder: 'criteo', + getSpec: function() { return criteoSpec; } + } + before(function () { + config.setConfig({s2sConfig: { enabled: false }}); + }); + + beforeEach(function () { + adapterManager.bidderRegistry['criteo'] = criteoAdapter; + }); + + afterEach(function () { + delete adapterManager.bidderRegistry['criteo']; + }); + + it('should call spec\'s onBidWon callback when a bid is won', function () { + const bids = [ + {bidder: 'criteo', params: {placementId: 'id'}}, + ]; + const adUnits = [{ + code: 'adUnit-code', + sizes: [[728, 90]], + bids + }]; + + adapterManager.callBidWonBidder(bids[0].bidder, bids[0], adUnits); + sinon.assert.called(criteoSpec.onBidWon); + }); + }); // end onBidWon + + describe('onSetTargeting', function () { + var criteoSpec = { onSetTargeting: sinon.stub() } + var criteoAdapter = { + bidder: 'criteo', + getSpec: function() { return criteoSpec; } + } + before(function () { + config.setConfig({s2sConfig: { enabled: false }}); + }); + + beforeEach(function () { + adapterManager.bidderRegistry['criteo'] = criteoAdapter; + }); + + afterEach(function () { + delete adapterManager.bidderRegistry['criteo']; + }); + + it('should call spec\'s onSetTargeting callback when setTargeting is called', function () { + const bids = [ + {bidder: 'criteo', params: {placementId: 'id'}}, + ]; + const adUnits = [{ + code: 'adUnit-code', + sizes: [[728, 90]], + bids + }]; + adapterManager.callSetTargetingBidder(bids[0].bidder, bids[0], adUnits); + sinon.assert.called(criteoSpec.onSetTargeting); + }); + }); // end onSetTargeting + + describe('S2S tests', function () { + beforeEach(function () { config.setConfig({s2sConfig: CONFIG}); - AdapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; + adapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; prebidServerAdapterMock.callBids.reset(); }); - it('invokes callBids on the S2S adapter', () => { + it('invokes callBids on the S2S adapter', function () { let bidRequests = [{ 'bidderCode': 'appnexus', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', 'tid': '34566b569352ef2', + 'timeout': 1000, 'src': 's2s', 'adUnitsS2SCopy': [ { @@ -172,7 +274,7 @@ describe('adapterManager tests', () => { 'placementId': '543221', 'test': 'me' }, - 'placementCode': '/19968336/header-bid-tag1', + 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, @@ -188,7 +290,7 @@ describe('adapterManager tests', () => { 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220995, 'status': 1, - 'bid_id': '378a8914450b334' + 'bid_id': '68136e1c47023d' } ] }, @@ -210,7 +312,7 @@ describe('adapterManager tests', () => { 'params': { 'placementId': '5324321' }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -225,7 +327,7 @@ describe('adapterManager tests', () => { 'bidderRequestId': '55e24a66bed717', 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220996, - 'bid_id': '387d9d9c32ca47c' + 'bid_id': '7e5d6af25ed188' } ] } @@ -282,7 +384,7 @@ describe('adapterManager tests', () => { 'start': 1462918897460 }]; - AdapterManager.callBids( + adapterManager.callBids( getAdUnits(), bidRequests, () => {}, @@ -292,7 +394,7 @@ describe('adapterManager tests', () => { }); // Enable this test when prebidServer adapter is made 1.0 compliant - it('invokes callBids with only s2s bids', () => { + it('invokes callBids with only s2s bids', function () { const adUnits = getAdUnits(); // adUnit without appnexus bidder adUnits.push({ @@ -315,6 +417,7 @@ describe('adapterManager tests', () => { 'bidderRequestId': '2946b569352ef2', 'tid': '34566b569352ef2', 'src': 's2s', + 'timeout': 1000, 'adUnitsS2SCopy': [ { 'code': '/19968336/header-bid-tag1', @@ -335,7 +438,7 @@ describe('adapterManager tests', () => { 'placementId': '543221', 'test': 'me' }, - 'placementCode': '/19968336/header-bid-tag1', + 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, @@ -373,7 +476,7 @@ describe('adapterManager tests', () => { 'params': { 'placementId': '5324321' }, - 'placementCode': '/19968336/header-bid-tag-0', + 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, @@ -444,7 +547,8 @@ describe('adapterManager tests', () => { ], 'start': 1462918897460 }]; - AdapterManager.callBids( + + adapterManager.callBids( adUnits, bidRequests, () => {}, @@ -455,49 +559,49 @@ describe('adapterManager tests', () => { sinon.assert.calledOnce(prebidServerAdapterMock.callBids); }); - describe('BID_REQUESTED event', () => { + describe('BID_REQUESTED event', function () { // function to count BID_REQUESTED events let cnt, count = () => cnt++; - beforeEach(() => { + beforeEach(function () { prebidServerAdapterMock.callBids.reset(); cnt = 0; events.on(CONSTANTS.EVENTS.BID_REQUESTED, count); }); - afterEach(() => { + afterEach(function () { events.off(CONSTANTS.EVENTS.BID_REQUESTED, count); }); - it('should fire for s2s requests', () => { + it('should fire for s2s requests', function () { let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['appnexus'], bid.bidder)); return adUnit; }) - let bidRequests = AdapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); - AdapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); + let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); + adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(1); sinon.assert.calledOnce(prebidServerAdapterMock.callBids); }); - it('should fire for simultaneous s2s and client requests', () => { - AdapterManager.bidderRegistry['adequant'] = adequantAdapterMock; + it('should fire for simultaneous s2s and client requests', function () { + adapterManager.bidderRegistry['adequant'] = adequantAdapterMock; let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['adequant', 'appnexus'], bid.bidder)); return adUnit; }) - let bidRequests = AdapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); - AdapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); + let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); + adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(2); sinon.assert.calledOnce(prebidServerAdapterMock.callBids); sinon.assert.calledOnce(adequantAdapterMock.callBids); adequantAdapterMock.callBids.reset(); - delete AdapterManager.bidderRegistry['adequant']; + delete adapterManager.bidderRegistry['adequant']; }); }); }); // end s2s tests - describe('s2sTesting', () => { + describe('s2sTesting', function () { let doneStub = sinon.stub(); let ajaxStub = sinon.stub(); @@ -511,8 +615,8 @@ describe('adapterManager tests', () => { } function callBids(adUnits = getTestAdUnits()) { - let bidRequests = AdapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); - AdapterManager.callBids(adUnits, bidRequests, doneStub, ajaxStub); + let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); + adapterManager.callBids(adUnits, bidRequests, doneStub, ajaxStub); } function checkServerCalled(numAdUnits, numBids) { @@ -538,12 +642,12 @@ describe('adapterManager tests', () => { }); let stubGetSourceBidderMap; - beforeEach(() => { + beforeEach(function () { config.setConfig({s2sConfig: TESTING_CONFIG}); - AdapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; - AdapterManager.bidderRegistry['adequant'] = adequantAdapterMock; - AdapterManager.bidderRegistry['appnexus'] = appnexusAdapterMock; - AdapterManager.bidderRegistry['rubicon'] = rubiconAdapterMock; + adapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; + adapterManager.bidderRegistry['adequant'] = adequantAdapterMock; + adapterManager.bidderRegistry['appnexus'] = appnexusAdapterMock; + adapterManager.bidderRegistry['rubicon'] = rubiconAdapterMock; stubGetSourceBidderMap = sinon.stub(s2sTesting, 'getSourceBidderMap'); @@ -553,12 +657,12 @@ describe('adapterManager tests', () => { rubiconAdapterMock.callBids.reset(); }); - afterEach(() => { + afterEach(function () { config.setConfig({s2sConfig: {}}); s2sTesting.getSourceBidderMap.restore(); }); - it('calls server adapter if no sources defined', () => { + it('calls server adapter if no sources defined', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: [], [s2sTesting.SERVER]: []}); callBids(); @@ -572,7 +676,7 @@ describe('adapterManager tests', () => { sinon.assert.notCalled(adequantAdapterMock.callBids); }); - it('calls client adapter if one client source defined', () => { + it('calls client adapter if one client source defined', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus'], [s2sTesting.SERVER]: []}); callBids(); @@ -586,7 +690,7 @@ describe('adapterManager tests', () => { sinon.assert.notCalled(adequantAdapterMock.callBids); }); - it('calls client adapters if client sources defined', () => { + it('calls client adapters if client sources defined', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); callBids(); @@ -600,7 +704,7 @@ describe('adapterManager tests', () => { checkClientCalled(adequantAdapterMock, 2); }); - it('calls client adapters if client sources defined', () => { + it('calls client adapters if client sources defined', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); callBids(); @@ -614,7 +718,7 @@ describe('adapterManager tests', () => { checkClientCalled(adequantAdapterMock, 2); }); - it('does not call server adapter for bidders that go to client', () => { + it('does not call server adapter for bidders that go to client', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); adUnits[0].bids[0].finalSource = s2sTesting.CLIENT; @@ -633,7 +737,7 @@ describe('adapterManager tests', () => { checkClientCalled(adequantAdapterMock, 2); }); - it('does not call client adapters for bidders that go to server', () => { + it('does not call client adapters for bidders that go to server', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); adUnits[0].bids[0].finalSource = s2sTesting.SERVER; @@ -652,13 +756,13 @@ describe('adapterManager tests', () => { sinon.assert.notCalled(adequantAdapterMock.callBids); }); - it('calls client and server adapters for bidders that go to both', () => { + it('calls client and server adapters for bidders that go to both', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); - adUnits[0].bids[0].finalSource = s2sTesting.BOTH; - adUnits[0].bids[1].finalSource = s2sTesting.BOTH; - adUnits[1].bids[0].finalSource = s2sTesting.BOTH; - adUnits[1].bids[1].finalSource = s2sTesting.BOTH; + // adUnits[0].bids[0].finalSource = s2sTesting.BOTH; + // adUnits[0].bids[1].finalSource = s2sTesting.BOTH; + // adUnits[1].bids[0].finalSource = s2sTesting.BOTH; + // adUnits[1].bids[1].finalSource = s2sTesting.BOTH; callBids(adUnits); // server adapter @@ -671,7 +775,7 @@ describe('adapterManager tests', () => { checkClientCalled(adequantAdapterMock, 2); }); - it('makes mixed client/server adapter calls for mixed bidder sources', () => { + it('makes mixed client/server adapter calls for mixed bidder sources', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); adUnits[0].bids[0].finalSource = s2sTesting.CLIENT; @@ -697,7 +801,7 @@ describe('adapterManager tests', () => { describe('using bidderFactory', function() { let spec; - beforeEach(() => { + beforeEach(function () { spec = { code: CODE, isBidRequestValid: () => {}, @@ -711,35 +815,85 @@ describe('adapterManager tests', () => { let thisSpec = Object.assign(spec, { supportedMediaTypes: ['video'] }); registerBidder(thisSpec); const alias = 'aliasBidder'; - AdapterManager.aliasBidAdapter(CODE, alias); - expect(AdapterManager.bidderRegistry).to.have.property(alias); - expect(AdapterManager.videoAdapters).to.include(alias); + adapterManager.aliasBidAdapter(CODE, alias); + expect(adapterManager.bidderRegistry).to.have.property(alias); + expect(adapterManager.videoAdapters).to.include(alias); + }); + }); + + describe('special case for s2s-only bidders', function () { + beforeEach(function () { + sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + config.resetConfig(); + utils.logError.restore(); + }); + + it('should allow an alias if alias is part of s2sConfig.bidders', function () { + let testS2sConfig = utils.deepClone(CONFIG); + testS2sConfig.bidders = ['s2sAlias']; + config.setConfig({s2sConfig: testS2sConfig}); + + adapterManager.aliasBidAdapter('s2sBidder', 's2sAlias'); + expect(adapterManager.aliasRegistry).to.have.property('s2sAlias'); + }); + + it('should throw an error if alias + bidder are unknown and not part of s2sConfig.bidders', function () { + let testS2sConfig = utils.deepClone(CONFIG); + testS2sConfig.bidders = ['s2sAlias']; + config.setConfig({s2sConfig: testS2sConfig}); + + adapterManager.aliasBidAdapter('s2sBidder1', 's2sAlias1'); + sinon.assert.calledOnce(utils.logError); + expect(adapterManager.aliasRegistry).to.not.have.property('s2sAlias1'); }); }); }); - describe('makeBidRequests', () => { + describe('makeBidRequests', function () { let adUnits; - beforeEach(() => { + beforeEach(function () { adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['appnexus', 'rubicon'], bid.bidder)); return adUnit; }) }); - describe('setBidderSequence', () => { - beforeEach(() => { + it('should make separate bidder request objects for each bidder', () => { + adUnits = [utils.deepClone(getAdUnits()[0])]; + + let bidRequests = adapterManager.makeBidRequests( + adUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + [] + ); + + let sizes1 = bidRequests[1].bids[0].sizes; + let sizes2 = bidRequests[0].bids[0].sizes; + + // mutate array + sizes1.splice(0, 1); + + expect(sizes1).not.to.deep.equal(sizes2); + }); + + describe('setBidderSequence', function () { + beforeEach(function () { sinon.spy(utils, 'shuffle'); }); - afterEach(() => { + afterEach(function () { config.resetConfig(); utils.shuffle.restore(); }); - it('setting to `random` uses shuffled order of adUnits', () => { + it('setting to `random` uses shuffled order of adUnits', function () { config.setConfig({ bidderSequence: 'random' }); - let bidRequests = AdapterManager.makeBidRequests( + let bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), @@ -750,18 +904,19 @@ describe('adapterManager tests', () => { }); }); - describe('sizeMapping', () => { - beforeEach(() => { + describe('sizeMapping', function () { + beforeEach(function () { sinon.stub(window, 'matchMedia').callsFake(() => ({matches: true})); }); - afterEach(() => { + afterEach(function () { matchMedia.restore(); + config.resetConfig(); setSizeConfig([]); }); - it('should not filter bids w/ no labels', () => { - let bidRequests = AdapterManager.makeBidRequests( + it('should not filter banner bids w/ no labels', function () { + let bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), @@ -772,15 +927,94 @@ describe('adapterManager tests', () => { expect(bidRequests.length).to.equal(2); let rubiconBidRequests = find(bidRequests, bidRequest => bidRequest.bidderCode === 'rubicon'); expect(rubiconBidRequests.bids.length).to.equal(1); - expect(rubiconBidRequests.bids[0].sizes).to.deep.equal(find(adUnits, adUnit => adUnit.code === rubiconBidRequests.bids[0].adUnitCode).sizes); + expect(rubiconBidRequests.bids[0].mediaTypes).to.deep.equal(find(adUnits, adUnit => adUnit.code === rubiconBidRequests.bids[0].adUnitCode).mediaTypes); let appnexusBidRequests = find(bidRequests, bidRequest => bidRequest.bidderCode === 'appnexus'); expect(appnexusBidRequests.bids.length).to.equal(2); - expect(appnexusBidRequests.bids[0].sizes).to.deep.equal(find(adUnits, adUnit => adUnit.code === appnexusBidRequests.bids[0].adUnitCode).sizes); - expect(appnexusBidRequests.bids[1].sizes).to.deep.equal(find(adUnits, adUnit => adUnit.code === appnexusBidRequests.bids[1].adUnitCode).sizes); + expect(appnexusBidRequests.bids[0].mediaTypes).to.deep.equal(find(adUnits, adUnit => adUnit.code === appnexusBidRequests.bids[0].adUnitCode).mediaTypes); + expect(appnexusBidRequests.bids[1].mediaTypes).to.deep.equal(find(adUnits, adUnit => adUnit.code === appnexusBidRequests.bids[1].adUnitCode).mediaTypes); + }); + + it('should not filter video bids', function () { + setSizeConfig([{ + 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', + 'sizesSupported': [ + [728, 90], + [300, 250] + ], + 'labels': ['tablet', 'phone'] + }]); + + let videoAdUnits = [{ + code: 'test_video', + mediaTypes: { + video: { + playerSize: [300, 300], + context: 'outstream' + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13232385, + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + } + } + }] + }]; + let bidRequests = adapterManager.makeBidRequests( + videoAdUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + [] + ); + expect(bidRequests[0].bids[0].sizes).to.deep.equal([300, 300]); }); - it('should filter sizes using size config', () => { + it('should not filter native bids', function () { + setSizeConfig([{ + 'mediaQuery': '(min-width: 768px) and (max-width: 1199px)', + 'sizesSupported': [ + [728, 90], + [300, 250] + ], + 'labels': ['tablet', 'phone'] + }]); + + let nativeAdUnits = [{ + code: 'test_native', + sizes: [[1, 1]], + mediaTypes: { + native: { + title: { required: true }, + body: { required: false }, + image: { required: true }, + icon: { required: false }, + sponsoredBy: { required: true }, + clickUrl: { required: true }, + }, + }, + bids: [ + { + bidder: 'appnexus', + params: { placementId: 13232354 } + }, + ] + }]; + let bidRequests = adapterManager.makeBidRequests( + nativeAdUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + [] + ); + expect(bidRequests[0].bids[0].sizes).to.deep.equal([]); + }); + + it('should filter sizes using size config', function () { let validSizes = [ [728, 90], [300, 250] @@ -797,7 +1031,7 @@ describe('adapterManager tests', () => { 'labels': ['tablet', 'phone'] }]); - let bidRequests = AdapterManager.makeBidRequests( + let bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), @@ -820,7 +1054,7 @@ describe('adapterManager tests', () => { 'labels': ['tablet', 'phone'] }]); - bidRequests = AdapterManager.makeBidRequests( + bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), @@ -832,13 +1066,13 @@ describe('adapterManager tests', () => { expect(bidRequests.length).to.equal(0); }); - it('should filter adUnits/bidders based on applied labels', () => { + it('should filter adUnits/bidders based on applied labels', function () { adUnits[0].labelAll = ['visitor-uk', 'mobile']; adUnits[1].labelAny = ['visitor-uk', 'desktop']; adUnits[1].bids[0].labelAny = ['mobile']; adUnits[1].bids[1].labelAll = ['desktop']; - let bidRequests = AdapterManager.makeBidRequests( + let bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), @@ -852,16 +1086,41 @@ describe('adapterManager tests', () => { expect(bidRequests[0].bids.length).to.equal(1); expect(bidRequests[0].bids[0].adUnitCode).to.equal(adUnits[1].code); }); + + it('should filter adUnits/bidders based on applid labels for s2s requests', function () { + adUnits[0].labelAll = ['visitor-uk', 'mobile']; + adUnits[1].labelAny = ['visitor-uk', 'desktop']; + adUnits[1].bids[0].labelAny = ['mobile']; + adUnits[1].bids[1].labelAll = ['desktop']; + + let TESTING_CONFIG = utils.deepClone(CONFIG); + TESTING_CONFIG.bidders = ['appnexus', 'rubicon']; + config.setConfig({ s2sConfig: TESTING_CONFIG }); + + let bidRequests = adapterManager.makeBidRequests( + adUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + ['visitor-uk', 'desktop'] + ); + + expect(bidRequests.length).to.equal(1); + expect(bidRequests[0].adUnitsS2SCopy.length).to.equal(1); + expect(bidRequests[0].adUnitsS2SCopy[0].bids.length).to.equal(1); + expect(bidRequests[0].adUnitsS2SCopy[0].bids[0].bidder).to.equal('rubicon'); + expect(bidRequests[0].adUnitsS2SCopy[0].labelAny).to.deep.equal(['visitor-uk', 'desktop']); + }); }); - describe('gdpr consent module', () => { - it('inserts gdprConsent object to bidRequest only when module was enabled', () => { - AdapterManager.gdprDataHandler.setConsentData({ + describe('gdpr consent module', function () { + it('inserts gdprConsent object to bidRequest only when module was enabled', function () { + gdprDataHandler.setConsentData({ consentString: 'abc123def456', consentRequired: true }); - let bidRequests = AdapterManager.makeBidRequests( + let bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), @@ -871,9 +1130,9 @@ describe('adapterManager tests', () => { expect(bidRequests[0].gdprConsent.consentString).to.equal('abc123def456'); expect(bidRequests[0].gdprConsent.consentRequired).to.be.true; - AdapterManager.gdprDataHandler.setConsentData(null); + gdprDataHandler.setConsentData(null); - bidRequests = AdapterManager.makeBidRequests( + bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), @@ -883,197 +1142,128 @@ describe('adapterManager tests', () => { expect(bidRequests[0].gdprConsent).to.be.undefined; }); }); - }); - describe('isValidBidRequest', () => { - describe('positive tests for validating bid request', () => { + describe('s2sTesting - testServerOnly', () => { beforeEach(() => { - sinon.stub(utils, 'logInfo'); + config.setConfig({ s2sConfig: getServerTestingConfig(CONFIG) }); }); - afterEach(() => { - utils.logInfo.restore(); - }); + afterEach(() => config.resetConfig()); - it('should maintain adUnit structure and adUnits.sizes is replaced', () => { - let fullAdUnit = [{ - sizes: [[300, 250], [300, 600]], - mediaTypes: { - banner: { - sizes: [[300, 250]] - }, - video: { - playerSize: [[640, 480]] - }, - native: { - image: { - sizes: [150, 150], - aspect_ratios: [140, 140] - }, - icon: { - sizes: [75, 75] - } - } - } - }]; - let result = checkBidRequestSizes(fullAdUnit); - expect(result[0].sizes).to.deep.equal([[640, 480]]); - expect(result[0].mediaTypes.video.playerSize).to.deep.equal([[640, 480]]); - expect(result[0].mediaTypes.native.image.sizes).to.deep.equal([150, 150]); - expect(result[0].mediaTypes.native.icon.sizes).to.deep.equal([75, 75]); - expect(result[0].mediaTypes.native.image.aspect_ratios).to.deep.equal([140, 140]); - - let noOptnlFieldAdUnit = [{ - sizes: [[300, 250], [300, 600]], - mediaTypes: { - banner: { - sizes: [[300, 250]] - }, - video: { - context: 'outstream' - }, - native: { - image: { - required: true - }, - icon: { - required: true - } - } - } - }]; - result = checkBidRequestSizes(noOptnlFieldAdUnit); - expect(result[0].sizes).to.deep.equal([[300, 250]]); - expect(result[0].mediaTypes.video).to.exist; + const makeBidRequests = ads => { + let bidRequests = adapterManager.makeBidRequests( + ads, 1111, 2222, 1000 + ); - let mixedAdUnit = [{ - sizes: [[300, 250], [300, 600]], - mediaTypes: { - video: { - context: 'outstream', - playerSize: [[400, 350]] - }, - native: { - image: { - aspect_ratios: [200, 150], - required: true - } - } - } - }]; - result = checkBidRequestSizes(mixedAdUnit); - expect(result[0].sizes).to.deep.equal([[400, 350]]); - expect(result[0].mediaTypes.video).to.exist; + bidRequests.sort((a, b) => { + if (a.bidderCode < b.bidderCode) return -1; + if (a.bidderCode > b.bidderCode) return 1; + return 0; + }); - let altVideoPlayerSize = [{ - sizes: [[600, 600]], - mediaTypes: { - video: { - playerSize: [640, 480] - } - } - }]; - result = checkBidRequestSizes(altVideoPlayerSize); - expect(result[0].sizes).to.deep.equal([[640, 480]]); - expect(result[0].mediaTypes.video.playerSize).to.deep.equal([[640, 480]]); - expect(result[0].mediaTypes.video).to.exist; - sinon.assert.calledOnce(utils.logInfo); + return bidRequests; + }; + + const removeAdUnitsBidSource = adUnits => adUnits.map(adUnit => { + const newAdUnit = { ...adUnit }; + newAdUnit.bids = newAdUnit.bids.map(bid => { + if (bid.bidSource) delete bid.bidSource; + return bid; + }); + return newAdUnit; }); - }); - describe('negative tests for validating bid requests', () => { - beforeEach(() => { - sinon.stub(utils, 'logError'); + it('suppresses all client bids if there are server bids resulting from bidSource at the adUnit Level', () => { + const bidRequests = makeBidRequests(getServerTestingsAds()); + + expect(bidRequests).lengthOf(2); + + expect(bidRequests[0].bids).lengthOf(1); + expect(bidRequests[0].bids[0].bidder).equals('openx'); + expect(bidRequests[0].bids[0].finalSource).equals('server'); + + expect(bidRequests[0].bids).lengthOf(1); + expect(bidRequests[1].bids[0].bidder).equals('rubicon'); + expect(bidRequests[1].bids[0].finalSource).equals('server'); }); - afterEach(() => { - utils.logError.restore(); + // todo: update description + it('suppresses all, and only, client bids if there are bids resulting from bidSource at the adUnit Level', () => { + const ads = getServerTestingsAds(); + + // change this adUnit to be server based + ads[1].bids[1].bidSource.client = 0; + ads[1].bids[1].bidSource.server = 100; + + const bidRequests = makeBidRequests(ads); + + expect(bidRequests).lengthOf(3); + + expect(bidRequests[0].bids).lengthOf(1); + expect(bidRequests[0].bids[0].bidder).equals('appnexus'); + expect(bidRequests[0].bids[0].finalSource).equals('server'); + + expect(bidRequests[1].bids).lengthOf(1); + expect(bidRequests[1].bids[0].bidder).equals('openx'); + expect(bidRequests[1].bids[0].finalSource).equals('server'); + + expect(bidRequests[2].bids).lengthOf(1); + expect(bidRequests[2].bids[0].bidder).equals('rubicon'); + expect(bidRequests[2].bids[0].finalSource).equals('server'); }); - it('should throw error message and delete an object/property', () => { - let badBanner = [{ - sizes: [[300, 250], [300, 600]], - mediaTypes: { - banner: { - name: 'test' - } - } - }]; - let result = checkBidRequestSizes(badBanner); - expect(result[0].sizes).to.deep.equal([[300, 250], [300, 600]]); - expect(result[0].mediaTypes.banner).to.be.undefined; - sinon.assert.called(utils.logError); + // we have a server call now + it('does not suppress client bids if no "test case" bids result in a server bid', () => { + const ads = getServerTestingsAds(); - let badVideo1 = [{ - sizes: [[600, 600]], - mediaTypes: { - video: { - playerSize: ['600x400'] - } - } - }]; - result = checkBidRequestSizes(badVideo1); - expect(result[0].sizes).to.deep.equal([[600, 600]]); - expect(result[0].mediaTypes.video.playerSize).to.be.undefined; - expect(result[0].mediaTypes.video).to.exist; - sinon.assert.called(utils.logError); - - let badVideo2 = [{ - sizes: [[600, 600]], - mediaTypes: { - video: { - playerSize: [['300', '200']] - } - } - }]; - result = checkBidRequestSizes(badVideo2); - expect(result[0].sizes).to.deep.equal([[600, 600]]); - expect(result[0].mediaTypes.video.playerSize).to.be.undefined; - expect(result[0].mediaTypes.video).to.exist; - sinon.assert.called(utils.logError); + // change this adUnit to be client based + ads[0].bids[0].bidSource.client = 100; + ads[0].bids[0].bidSource.server = 0; - let badNativeImgSize = [{ - mediaTypes: { - native: { - image: { - sizes: '300x250' - } - } - } - }]; - result = checkBidRequestSizes(badNativeImgSize); - expect(result[0].mediaTypes.native.image.sizes).to.be.undefined; - expect(result[0].mediaTypes.native.image).to.exist; - sinon.assert.called(utils.logError); + const bidRequests = makeBidRequests(ads); - let badNativeImgAspRat = [{ - mediaTypes: { - native: { - image: { - aspect_ratios: '300x250' - } - } - } - }]; - result = checkBidRequestSizes(badNativeImgAspRat); - expect(result[0].mediaTypes.native.image.aspect_ratios).to.be.undefined; - expect(result[0].mediaTypes.native.image).to.exist; - sinon.assert.called(utils.logError); + expect(bidRequests).lengthOf(4); - let badNativeIcon = [{ - mediaTypes: { - native: { - icon: { - sizes: '300x250' - } - } - } - }]; - result = checkBidRequestSizes(badNativeIcon); - expect(result[0].mediaTypes.native.icon.sizes).to.be.undefined; - expect(result[0].mediaTypes.native.icon).to.exist; - sinon.assert.called(utils.logError); + expect(bidRequests[0].bids).lengthOf(1); + expect(bidRequests[0].bids[0].bidder).equals('adequant'); + expect(bidRequests[0].bids[0].finalSource).equals('client'); + + expect(bidRequests[1].bids).lengthOf(2); + expect(bidRequests[1].bids[0].bidder).equals('appnexus'); + expect(bidRequests[1].bids[0].finalSource).equals('client'); + expect(bidRequests[1].bids[1].bidder).equals('appnexus'); + expect(bidRequests[1].bids[1].finalSource).equals('client'); + + expect(bidRequests[2].bids).lengthOf(1); + expect(bidRequests[2].bids[0].bidder).equals('openx'); + expect(bidRequests[2].bids[0].finalSource).equals('server'); + + expect(bidRequests[3].bids).lengthOf(2); + expect(bidRequests[3].bids[0].bidder).equals('rubicon'); + expect(bidRequests[3].bids[0].finalSource).equals('client'); + expect(bidRequests[3].bids[1].bidder).equals('rubicon'); + expect(bidRequests[3].bids[1].finalSource).equals('client'); }); + + it( + 'should surpress client side bids if no ad unit bidSources are set, ' + + 'but bidderControl resolves to server', + () => { + const ads = removeAdUnitsBidSource(getServerTestingsAds()); + + const bidRequests = makeBidRequests(ads); + + expect(bidRequests).lengthOf(2); + + expect(bidRequests[0].bids).lengthOf(1); + expect(bidRequests[0].bids[0].bidder).equals('openx'); + expect(bidRequests[0].bids[0].finalSource).equals('server'); + + expect(bidRequests[1].bids).lengthOf(2); + expect(bidRequests[1].bids[0].bidder).equals('rubicon'); + expect(bidRequests[1].bids[0].finalSource).equals('server'); + } + ); }); }); }); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index ca2a9afc103..fd54d2911e1 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1,5 +1,5 @@ -import { newBidder, registerBidder } from 'src/adapters/bidderFactory'; -import adaptermanager from 'src/adaptermanager'; +import { newBidder, registerBidder, preloadBidderMappingFile } from 'src/adapters/bidderFactory'; +import adapterManager from 'src/adapterManager'; import * as ajax from 'src/ajax'; import { expect } from 'chai'; import { STATUS } from 'src/constants'; @@ -29,13 +29,17 @@ const MOCK_BIDS_REQUEST = { ] } -describe('bidders created by newBidder', () => { +function onTimelyResponseStub() { + +} + +describe('bidders created by newBidder', function () { let spec; let bidder; let addBidResponseStub; let doneStub; - beforeEach(() => { + beforeEach(function () { spec = { code: CODE, isBidRequestValid: sinon.stub(), @@ -48,26 +52,26 @@ describe('bidders created by newBidder', () => { doneStub = sinon.stub(); }); - describe('when the ajax response is irrelevant', () => { + describe('when the ajax response is irrelevant', function () { let ajaxStub; - beforeEach(() => { + beforeEach(function () { ajaxStub = sinon.stub(ajax, 'ajax'); addBidResponseStub.reset(); doneStub.reset(); }); - afterEach(() => { + afterEach(function () { ajaxStub.restore(); }); - it('should handle bad bid requests gracefully', () => { + it('should handle bad bid requests gracefully', function () { const bidder = newBidder(spec); spec.getUserSyncs.returns([]); bidder.callBids({}); - bidder.callBids({ bids: 'nothing useful' }, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids({ bids: 'nothing useful' }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.called).to.equal(false); @@ -75,13 +79,13 @@ describe('bidders created by newBidder', () => { expect(spec.interpretResponse.called).to.equal(false); }); - it('should call buildRequests(bidRequest) the params are valid', () => { + it('should call buildRequests(bidRequest) the params are valid', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.calledTwice).to.equal(true); @@ -89,27 +93,27 @@ describe('bidders created by newBidder', () => { expect(spec.buildRequests.firstCall.args[0]).to.deep.equal(MOCK_BIDS_REQUEST.bids); }); - it('should not call buildRequests the params are invalid', () => { + it('should not call buildRequests the params are invalid', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(false); spec.buildRequests.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.calledTwice).to.equal(true); expect(spec.buildRequests.called).to.equal(false); }); - it('should filter out invalid bids before calling buildRequests', () => { + it('should filter out invalid bids before calling buildRequests', function () { const bidder = newBidder(spec); spec.isBidRequestValid.onFirstCall().returns(true); spec.isBidRequestValid.onSecondCall().returns(false); spec.buildRequests.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.called).to.equal(false); expect(spec.isBidRequestValid.calledTwice).to.equal(true); @@ -117,18 +121,18 @@ describe('bidders created by newBidder', () => { expect(spec.buildRequests.firstCall.args[0]).to.deep.equal([MOCK_BIDS_REQUEST.bids[0]]); }); - it('should make no server requests if the spec doesn\'t return any', () => { + it('should make no server requests if the spec doesn\'t return any', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.called).to.equal(false); }); - it('should make the appropriate POST request', () => { + it('should make the appropriate POST request', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; @@ -139,7 +143,7 @@ describe('bidders created by newBidder', () => { data: data }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(url); @@ -151,7 +155,7 @@ describe('bidders created by newBidder', () => { }); }); - it('should make the appropriate POST request when options are passed', () => { + it('should make the appropriate POST request when options are passed', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; @@ -164,7 +168,7 @@ describe('bidders created by newBidder', () => { options: options }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(url); @@ -176,7 +180,7 @@ describe('bidders created by newBidder', () => { }); }); - it('should make the appropriate GET request', () => { + it('should make the appropriate GET request', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; @@ -187,7 +191,7 @@ describe('bidders created by newBidder', () => { data: data }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2&`); @@ -198,7 +202,7 @@ describe('bidders created by newBidder', () => { }); }); - it('should make the appropriate GET request when options are passed', () => { + it('should make the appropriate GET request when options are passed', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; @@ -211,7 +215,7 @@ describe('bidders created by newBidder', () => { options: opt }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2&`); @@ -222,7 +226,7 @@ describe('bidders created by newBidder', () => { }); }); - it('should make multiple calls if the spec returns them', () => { + it('should make multiple calls if the spec returns them', function () { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; @@ -240,12 +244,12 @@ describe('bidders created by newBidder', () => { } ]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(ajaxStub.calledTwice).to.equal(true); }); - it('should not add bids for each placement code if no requests are given', () => { + it('should not add bids for each placement code if no requests are given', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -253,18 +257,18 @@ describe('bidders created by newBidder', () => { spec.interpretResponse.returns([]); spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(addBidResponseStub.callCount).to.equal(0); }); }); - describe('when the ajax call succeeds', () => { + describe('when the ajax call succeeds', function () { let ajaxStub; let userSyncStub; let logErrorSpy; - beforeEach(() => { + beforeEach(function () { ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { const fakeResponse = sinon.stub(); fakeResponse.returns('headerContent'); @@ -276,13 +280,13 @@ describe('bidders created by newBidder', () => { logErrorSpy = sinon.spy(utils, 'logError'); }); - afterEach(() => { + afterEach(function () { ajaxStub.restore(); userSyncStub.restore(); utils.logError.restore(); }); - it('should call spec.interpretResponse() with the response content', () => { + it('should call spec.interpretResponse() with the response content', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -293,7 +297,7 @@ describe('bidders created by newBidder', () => { }); spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(spec.interpretResponse.calledOnce).to.equal(true); const response = spec.interpretResponse.firstCall.args[0] @@ -307,7 +311,7 @@ describe('bidders created by newBidder', () => { expect(doneStub.calledOnce).to.equal(true); }); - it('should call spec.interpretResponse() once for each request made', () => { + it('should call spec.interpretResponse() once for each request made', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -325,13 +329,13 @@ describe('bidders created by newBidder', () => { ]); spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(spec.interpretResponse.calledTwice).to.equal(true); expect(doneStub.calledOnce).to.equal(true); }); - it('should only add bids for valid adUnit code into the auction, even if the bidder doesn\'t bid on all of them', () => { + it('should only add bids for valid adUnit code into the auction, even if the bidder doesn\'t bid on all of them', function () { const bidder = newBidder(spec); const bid = { @@ -356,7 +360,7 @@ describe('bidders created by newBidder', () => { spec.interpretResponse.returns(bid); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); @@ -364,7 +368,7 @@ describe('bidders created by newBidder', () => { expect(logErrorSpy.callCount).to.equal(0); }); - it('should call spec.getUserSyncs() with the response', () => { + it('should call spec.getUserSyncs() with the response', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -375,7 +379,7 @@ describe('bidders created by newBidder', () => { }); spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(spec.getUserSyncs.calledOnce).to.equal(true); expect(spec.getUserSyncs.firstCall.args[1].length).to.equal(1); @@ -384,7 +388,7 @@ describe('bidders created by newBidder', () => { expect(spec.getUserSyncs.firstCall.args[1][0].headers.get).to.be.a('function'); }); - it('should register usersync pixels', () => { + it('should register usersync pixels', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(false); @@ -394,7 +398,7 @@ describe('bidders created by newBidder', () => { url: 'usersync.com' }]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(userSyncStub.called).to.equal(true); expect(userSyncStub.firstCall.args[0]).to.equal('iframe'); @@ -402,7 +406,7 @@ describe('bidders created by newBidder', () => { expect(userSyncStub.firstCall.args[2]).to.equal('usersync.com'); }); - it('should logError when required bid response params are missing', () => { + it('should logError when required bid response params are missing', function () { const bidder = newBidder(spec); const bid = { @@ -423,16 +427,46 @@ describe('bidders created by newBidder', () => { spec.interpretResponse.returns(bid); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); + + expect(logErrorSpy.calledOnce).to.equal(true); + }); + + it('should logError when required bid response params are undefined', function () { + const bidder = newBidder(spec); + + const bid = { + 'ad': 'creative', + 'cpm': '1.99', + 'width': 300, + 'height': 250, + 'requestId': '1', + 'creativeId': 'some-id', + 'currency': undefined, + 'netRevenue': true, + 'ttl': 360 + }; + + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} + }); + spec.getUserSyncs.returns([]); + + spec.interpretResponse.returns(bid); + + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(logErrorSpy.calledOnce).to.equal(true); }); }); - describe('when the ajax call fails', () => { + describe('when the ajax call fails', function () { let ajaxStub; - beforeEach(() => { + beforeEach(function () { ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { callbacks.error('ajax call failed.'); }); @@ -440,11 +474,11 @@ describe('bidders created by newBidder', () => { doneStub.reset(); }); - afterEach(() => { + afterEach(function () { ajaxStub.restore(); }); - it('should not spec.interpretResponse()', () => { + it('should not spec.interpretResponse()', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -455,13 +489,13 @@ describe('bidders created by newBidder', () => { }); spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(spec.interpretResponse.called).to.equal(false); expect(doneStub.calledOnce).to.equal(true); }); - it('should not add bids for each adunit code into the auction', () => { + it('should not add bids for each adunit code into the auction', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -473,13 +507,13 @@ describe('bidders created by newBidder', () => { spec.interpretResponse.returns([]); spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(addBidResponseStub.callCount).to.equal(0); expect(doneStub.calledOnce).to.equal(true); }); - it('should call spec.getUserSyncs() with no responses', () => { + it('should call spec.getUserSyncs() with no responses', function () { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -490,7 +524,7 @@ describe('bidders created by newBidder', () => { }); spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(spec.getUserSyncs.calledOnce).to.equal(true); expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); @@ -499,16 +533,16 @@ describe('bidders created by newBidder', () => { }); }); -describe('registerBidder', () => { +describe('registerBidder', function () { let registerBidAdapterStub; let aliasBidAdapterStub; - beforeEach(() => { - registerBidAdapterStub = sinon.stub(adaptermanager, 'registerBidAdapter'); - aliasBidAdapterStub = sinon.stub(adaptermanager, 'aliasBidAdapter'); + beforeEach(function () { + registerBidAdapterStub = sinon.stub(adapterManager, 'registerBidAdapter'); + aliasBidAdapterStub = sinon.stub(adapterManager, 'aliasBidAdapter'); }); - afterEach(() => { + afterEach(function () { registerBidAdapterStub.restore(); aliasBidAdapterStub.restore(); }); @@ -522,7 +556,7 @@ describe('registerBidder', () => { }; } - it('should register a bidder with the adapterManager', () => { + it('should register a bidder with the adapterManager', function () { registerBidder(newEmptySpec()); expect(registerBidAdapterStub.calledOnce).to.equal(true); expect(registerBidAdapterStub.firstCall.args[0]).to.have.property('callBids'); @@ -532,14 +566,14 @@ describe('registerBidder', () => { expect(registerBidAdapterStub.firstCall.args[2]).to.be.undefined; }); - it('should register a bidder with the appropriate mediaTypes', () => { + it('should register a bidder with the appropriate mediaTypes', function () { const thisSpec = Object.assign(newEmptySpec(), { supportedMediaTypes: ['video'] }); registerBidder(thisSpec); expect(registerBidAdapterStub.calledOnce).to.equal(true); expect(registerBidAdapterStub.firstCall.args[2]).to.deep.equal({supportedMediaTypes: ['video']}); }); - it('should register bidders with the appropriate aliases', () => { + it('should register bidders with the appropriate aliases', function () { const thisSpec = Object.assign(newEmptySpec(), { aliases: ['foo', 'bar'] }); registerBidder(thisSpec); @@ -556,7 +590,7 @@ describe('registerBidder', () => { }); }) -describe('validate bid response: ', () => { +describe('validate bid response: ', function () { let spec; let bidder; let addBidResponseStub; @@ -576,7 +610,7 @@ describe('validate bid response: ', () => { 'ttl': 360 }]; - beforeEach(() => { + beforeEach(function () { spec = { code: CODE, isBidRequestValid: sinon.stub(), @@ -601,15 +635,15 @@ describe('validate bid response: ', () => { logErrorSpy = sinon.spy(utils, 'logError'); }); - afterEach(() => { + afterEach(function () { ajaxStub.restore(); logErrorSpy.restore(); }); - it('should add native bids that do have required assets', () => { + it('should add native bids that do have required assets', function () { let bidRequest = { bids: [{ - bidId: 1, + bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { @@ -636,17 +670,17 @@ describe('validate bid response: ', () => { const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); expect(logErrorSpy.callCount).to.equal(0); }); - it('should not add native bids that do not have required assets', () => { + it('should not add native bids that do not have required assets', function () { let bidRequest = { bids: [{ - bidId: 1, + bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { @@ -673,16 +707,16 @@ describe('validate bid response: ', () => { const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(addBidResponseStub.calledOnce).to.equal(false); expect(logErrorSpy.callCount).to.equal(1); }); - it('should add bid when renderer is present on outstream bids', () => { + it('should add bid when renderer is present on outstream bids', function () { let bidRequest = { bids: [{ - bidId: 1, + bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { @@ -706,18 +740,18 @@ describe('validate bid response: ', () => { const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); expect(logErrorSpy.callCount).to.equal(0); }); - it('should add banner bids that have no width or height but single adunit size', () => { + it('should add banner bids that have no width or height but single adunit size', function () { let bidRequest = { bids: [{ bidder: CODE, - bidId: 1, + bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', params: { @@ -738,10 +772,135 @@ describe('validate bid response: ', () => { const bidder = newBidder(spec); spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); expect(logErrorSpy.callCount).to.equal(0); }); }); + +describe('preload mapping url hook', function() { + let fakeTranslationServer; + let getLocalStorageStub; + let adapterManagerStub; + + beforeEach(function () { + fakeTranslationServer = sinon.fakeServer.create(); + getLocalStorageStub = sinon.stub(utils, 'getDataFromLocalStorage'); + adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); + }); + + afterEach(function() { + getLocalStorageStub.restore(); + adapterManagerStub.restore(); + config.resetConfig(); + }); + + it('should preload mapping url file', function() { + config.setConfig({ + 'adpod': { + 'brandCategoryExclusion': true + } + }); + let adUnits = [{ + code: 'midroll_1', + mediaTypes: { + video: { + context: 'adpod' + } + }, + bids: [ + { + bidder: 'sampleBidder1', + params: { + placementId: 14542875, + } + } + ] + }]; + getLocalStorageStub.returns(null); + adapterManagerStub.withArgs('sampleBidder1').returns({ + getSpec: function() { + return { + 'getMappingFileInfo': function() { + return { + url: 'http://sample.com', + refreshInDays: 7, + key: `sampleBidder1MappingFile` + } + } + } + } + }); + preloadBidderMappingFile(sinon.spy(), adUnits); + expect(fakeTranslationServer.requests.length).to.equal(1); + }); + + it('should preload mapping url file for all bidders', function() { + config.setConfig({ + 'adpod': { + 'brandCategoryExclusion': true + } + }); + let adUnits = [{ + code: 'midroll_1', + mediaTypes: { + video: { + context: 'adpod' + } + }, + bids: [ + { + bidder: 'sampleBidder1', + params: { + placementId: 14542875, + } + }, + { + bidder: 'sampleBidder2', + params: { + placementId: 123456, + } + } + ] + }]; + getLocalStorageStub.returns(null); + adapterManagerStub.withArgs('sampleBidder1').returns({ + getSpec: function() { + return { + 'getMappingFileInfo': function() { + return { + url: 'http://sample.com', + refreshInDays: 7, + key: `sampleBidder1MappingFile` + } + } + } + } + }); + adapterManagerStub.withArgs('sampleBidder2').returns({ + getSpec: function() { + return { + 'getMappingFileInfo': function() { + return { + url: 'http://sample.com', + refreshInDays: 7, + key: `sampleBidder2MappingFile` + } + } + } + } + }); + preloadBidderMappingFile(sinon.spy(), adUnits); + expect(fakeTranslationServer.requests.length).to.equal(2); + + config.setConfig({ + 'adpod': { + 'brandCategoryExclusion': false + } + }); + preloadBidderMappingFile(sinon.spy(), adUnits); + expect(fakeTranslationServer.requests.length).to.equal(2); + }); +}); diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 1fa648e9825..b165a2a21fb 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -1,10 +1,9 @@ import { expect } from 'chai'; -import { targeting as targetingInstance } from 'src/targeting'; +import { targeting as targetingInstance, filters, sortByDealAndPriceBucket } from 'src/targeting'; import { config } from 'src/config'; import { getAdUnits, createBidReceived } from 'test/fixtures/fixtures'; import CONSTANTS from 'src/constants.json'; import { auctionManager } from 'src/auctionManager'; -import * as targetingModule from 'src/targeting'; import * as utils from 'src/utils'; const bid1 = { @@ -27,11 +26,13 @@ const bid1 = { 'bidder': 'rubicon', 'size': '300x250', 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '148018fe5e', - 'hb_pb': '0.53', - 'foobar': '300x250' + 'foobar': '300x250', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'rubicon', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '148018fe5e', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '0.53', + [CONSTANTS.TARGETING_KEYS.DEAL]: '1234' }, + 'dealId': '1234', 'netRevenue': true, 'currency': 'USD', 'ttl': 300 @@ -57,10 +58,10 @@ const bid2 = { 'bidder': 'rubicon', 'size': '300x250', 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '5454545', - 'hb_pb': '0.25', - 'foobar': '300x250' + 'foobar': '300x250', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'rubicon', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '5454545', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '0.25' }, 'netRevenue': true, 'currency': 'USD', @@ -87,70 +88,451 @@ const bid3 = { 'bidder': 'rubicon', 'size': '300x600', 'adserverTargeting': { - 'hb_bidder': 'rubicon', - 'hb_adid': '48747745', - 'hb_pb': '0.75', - 'foobar': '300x600' + 'foobar': '300x600', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'rubicon', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '48747745', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '0.75' }, 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }; -describe('targeting tests', () => { - describe('getAllTargeting', () => { +const nativeBid1 = { + 'bidderCode': 'appnexus', + 'width': 0, + 'height': 0, + 'statusMessage': 'Bid available', + 'adId': '591e7c9354b633', + 'requestId': '24aae81e32d6f6', + 'mediaType': 'native', + 'source': 'client', + 'cpm': 10, + 'creativeId': 97494403, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': '/19968336/prebid_native_example_1', + 'appnexus': { + 'buyerMemberId': 9325 + }, + 'meta': { + 'advertiserId': 2529885 + }, + 'native': { + 'title': 'This is a Prebid Native Creative', + 'body': 'This is a Prebid Native Creative. There are many like it, but this one is mine.', + 'sponsoredBy': 'Prebid.org', + 'clickUrl': 'http://prebid.org/dev-docs/show-native-ads.html', + 'clickTrackers': ['http://www.clickUrl.com/404'], + 'impressionTrackers': ['http://imp.trackerUrl.com/it1'], + 'javascriptTrackers': '', + 'image': { + 'url': 'http://vcdn.adnxs.com/p/creative-image/94/22/cd/0f/9422cd0f-f400-45d3-80f5-2b92629d9257.jpg', + 'height': 2250, + 'width': 3000 + }, + 'icon': { + 'url': 'http://vcdn.adnxs.com/p/creative-image/bd/59/a6/c6/bd59a6c6-0851-411d-a16d-031475a51312.png', + 'height': 83, + 'width': 127 + } + }, + 'auctionId': '72138a4a-b747-4192-9192-dcc41d675de8', + 'responseTimestamp': 1565785219461, + 'requestTimestamp': 1565785219405, + 'bidder': 'appnexus', + 'timeToRespond': 56, + 'pbLg': '5.00', + 'pbMg': '10.00', + 'pbHg': '10.00', + 'pbAg': '10.00', + 'pbDg': '10.00', + 'pbCg': '', + 'size': '0x0', + 'adserverTargeting': { + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '591e7c9354b633', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.SIZE]: '0x0', + [CONSTANTS.TARGETING_KEYS.SOURCE]: 'client', + [CONSTANTS.TARGETING_KEYS.FORMAT]: 'native', + [CONSTANTS.NATIVE_KEYS.title]: 'This is a Prebid Native Creative', + [CONSTANTS.NATIVE_KEYS.body]: 'This is a Prebid Native Creative. There are many like it, but this one is mine.', + [CONSTANTS.NATIVE_KEYS.sponsoredBy]: 'Prebid.org', + [CONSTANTS.NATIVE_KEYS.clickUrl]: 'http://prebid.org/dev-docs/show-native-ads.html', + [CONSTANTS.NATIVE_KEYS.image]: 'http://vcdn.adnxs.com/p/creative-image/94/22/cd/0f/9422cd0f-f400-45d3-80f5-2b92629d9257.jpg', + [CONSTANTS.NATIVE_KEYS.icon]: 'http://vcdn.adnxs.com/p/creative-image/bd/59/a6/c6/bd59a6c6-0851-411d-a16d-031475a51312.png' + } +}; +const nativeBid2 = { + 'bidderCode': 'dgads', + 'width': 0, + 'height': 0, + 'statusMessage': 'Bid available', + 'adId': '6e0aba55ed54e5', + 'requestId': '4de26ec83d9661', + 'mediaType': 'native', + 'source': 'client', + 'cpm': 1.90909091, + 'creativeId': 'xuidx6c901261b0x2b2', + 'currency': 'JPY', + 'netRevenue': true, + 'ttl': 60, + 'referrer': 'http://test.localhost:9999/integrationExamples/gpt/demo_native.html?pbjs_debug=true', + 'native': { + 'image': { + 'url': 'https://ads-tr.bigmining.com/img/300x250.png', + 'width': 300, + 'height': 250 + }, + 'title': 'Test Title', + 'body': 'Test Description', + 'sponsoredBy': 'test.com', + 'clickUrl': 'http://prebid.org/', + 'clickTrackers': ['https://www.clickUrl.com/404'], + 'impressionTrackers': [ + 'http://imp.trackerUrl.com/it2' + ] + }, + 'auctionId': '72138a4a-b747-4192-9192-dcc41d675de8', + 'responseTimestamp': 1565785219607, + 'requestTimestamp': 1565785219409, + 'bidder': 'dgads', + 'adUnitCode': '/19968336/prebid_native_example_1', + 'timeToRespond': 198, + 'pbLg': '1.50', + 'pbMg': '1.90', + 'pbHg': '1.90', + 'pbAg': '1.90', + 'pbDg': '1.90', + 'pbCg': '', + 'size': '0x0', + 'adserverTargeting': { + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'dgads', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '6e0aba55ed54e5', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '1.90', + [CONSTANTS.TARGETING_KEYS.SIZE]: '0x0', + [CONSTANTS.TARGETING_KEYS.SOURCE]: 'client', + [CONSTANTS.TARGETING_KEYS.FORMAT]: 'native', + [CONSTANTS.NATIVE_KEYS.image]: 'https://ads-tr.bigmining.com/img/300x250.png', + [CONSTANTS.NATIVE_KEYS.title]: 'Test Title', + [CONSTANTS.NATIVE_KEYS.body]: 'Test Description', + [CONSTANTS.NATIVE_KEYS.sponsoredBy]: 'test.com', + [CONSTANTS.NATIVE_KEYS.clickUrl]: 'http://prebid.org/' + } +}; + +describe('targeting tests', function () { + let sandbox; + let enableSendAllBids = false; + let useBidCache; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + + useBidCache = true; + + let origGetConfig = config.getConfig; + sandbox.stub(config, 'getConfig').callsFake(function (key) { + if (key === 'enableSendAllBids') { + return enableSendAllBids; + } + if (key === 'useBidCache') { + return useBidCache; + } + return origGetConfig.apply(config, arguments); + }); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('getAllTargeting', function () { let amBidsReceivedStub; let amGetAdUnitsStub; let bidExpiryStub; + let logWarnStub; + let logErrorStub; + let bidsReceived; + + beforeEach(function () { + bidsReceived = [bid1, bid2, bid3]; - beforeEach(() => { - $$PREBID_GLOBAL$$._sendAllBids = false; - amBidsReceivedStub = sinon.stub(auctionManager, 'getBidsReceived').callsFake(function() { - return [bid1, bid2, bid3]; + amBidsReceivedStub = sandbox.stub(auctionManager, 'getBidsReceived').callsFake(function() { + return bidsReceived; }); - amGetAdUnitsStub = sinon.stub(auctionManager, 'getAdUnitCodes').callsFake(function() { + amGetAdUnitsStub = sandbox.stub(auctionManager, 'getAdUnitCodes').callsFake(function() { return ['/123456/header-bid-tag-0']; }); - bidExpiryStub = sinon.stub(targetingModule, 'isBidExpired').returns(true); + bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); + logWarnStub = sinon.stub(utils, 'logWarn'); + logErrorStub = sinon.stub(utils, 'logError'); }); - afterEach(() => { - auctionManager.getBidsReceived.restore(); - auctionManager.getAdUnitCodes.restore(); - targetingModule.isBidExpired.restore(); + afterEach(function() { + config.resetConfig(); + logWarnStub.restore(); + logErrorStub.restore(); + amBidsReceivedStub.restore(); + amGetAdUnitsStub.restore(); + bidExpiryStub.restore(); + }); + + describe('when hb_deal is present in bid.adserverTargeting', function () { + let bid4; + + beforeEach(function() { + bid4 = utils.deepClone(bid1); + bid4.adserverTargeting['hb_bidder'] = bid4.bidder = bid4.bidderCode = 'appnexus'; + bid4.cpm = 0; + enableSendAllBids = true; + + bidsReceived.push(bid4); + }); + + it('returns targeting with both hb_deal and hb_deal_{bidder_code}', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + // We should add both keys rather than one or the other + expect(targeting['/123456/header-bid-tag-0']).to.contain.keys('hb_deal', `hb_deal_${bid1.bidderCode}`, `hb_deal_${bid4.bidderCode}`); + + // We should assign both keys the same value + expect(targeting['/123456/header-bid-tag-0']['hb_deal']).to.deep.equal(targeting['/123456/header-bid-tag-0'][`hb_deal_${bid1.bidderCode}`]); + }); + }); + + it('will enforce a limit on the number of auction keys when auctionKeyMaxChars setting is active', function () { + config.setConfig({ + targetingControls: { + auctionKeyMaxChars: 150 + } + }); + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0', '/123456/header-bid-tag-1']); + expect(targeting['/123456/header-bid-tag-1']).to.deep.equal({}); + expect(targeting['/123456/header-bid-tag-0']).to.contain.keys('hb_pb', 'hb_adid', 'hb_bidder', 'hb_deal'); + expect(targeting['/123456/header-bid-tag-0']['hb_adid']).to.equal(bid1.adId); + expect(logWarnStub.calledOnce).to.be.true; + }); + + it('will return an error when auctionKeyMaxChars setting is set too low for any auction keys to be allowed', function () { + config.setConfig({ + targetingControls: { + auctionKeyMaxChars: 50 + } + }); + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0', '/123456/header-bid-tag-1']); + expect(targeting['/123456/header-bid-tag-1']).to.deep.equal({}); + expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({}); + expect(logWarnStub.calledTwice).to.be.true; + expect(logErrorStub.calledOnce).to.be.true; + }); + + describe('targetingControls.alwaysIncludeDeals', function () { + let bid4; + + beforeEach(function() { + bid4 = utils.deepClone(bid1); + bid4.adserverTargeting = { + hb_deal: '4321', + hb_pb: '0.1', + hb_adid: '567891011', + hb_bidder: 'appnexus', + }; + bid4.bidder = bid4.bidderCode = 'appnexus'; + bid4.cpm = 0.1; // losing bid so not included if enableSendAllBids === false + bid4.dealId = '4321'; + enableSendAllBids = false; + + bidsReceived.push(bid4); + }); + + it('does not include losing deals when alwaysIncludeDeals not set', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + // Rubicon wins bid and has deal, but alwaysIncludeDeals is false, so only top bid plus deal_id + // appnexus does not get sent since alwaysIncludeDeals is not defined + expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ + 'hb_deal_rubicon': '1234', + 'hb_deal': '1234', + 'hb_pb': '0.53', + 'hb_adid': '148018fe5e', + 'hb_bidder': 'rubicon', + 'foobar': '300x250' + }); + }); + + it('does not include losing deals when alwaysIncludeDeals set to false', function () { + config.setConfig({ + targetingControls: { + alwaysIncludeDeals: false + } + }); + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + // Rubicon wins bid and has deal, but alwaysIncludeDeals is false, so only top bid plus deal_id + // appnexus does not get sent since alwaysIncludeDeals is false + expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ + 'hb_deal_rubicon': '1234', // This is just how it works before this PR, always added no matter what for winner if they have deal + 'hb_deal': '1234', + 'hb_pb': '0.53', + 'hb_adid': '148018fe5e', + 'hb_bidder': 'rubicon', + 'foobar': '300x250' + }); + }); + + it('includes losing deals when alwaysIncludeDeals set to true and also winning deals bidder KVPs', function () { + config.setConfig({ + targetingControls: { + alwaysIncludeDeals: true + } + }); + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + // Rubicon wins bid and has a deal, so all KVPs for them are passed (top plus bidder specific) + // Appnexus had deal so passed through + expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ + 'hb_deal_rubicon': '1234', + 'hb_deal': '1234', + 'hb_pb': '0.53', + 'hb_adid': '148018fe5e', + 'hb_bidder': 'rubicon', + 'foobar': '300x250', + 'hb_pb_rubicon': '0.53', + 'hb_adid_rubicon': '148018fe5e', + 'hb_bidder_rubicon': 'rubicon', + 'hb_deal_appnexus': '4321', + 'hb_pb_appnexus': '0.1', + 'hb_adid_appnexus': '567891011', + 'hb_bidder_appnexus': 'appnexus' + }); + }); + + it('includes winning bid even when it is not a deal, plus other deal KVPs', function () { + config.setConfig({ + targetingControls: { + alwaysIncludeDeals: true + } + }); + let bid5 = utils.deepClone(bid4); + bid5.adserverTargeting = { + hb_pb: '3.0', + hb_adid: '111111', + hb_bidder: 'pubmatic', + }; + bid5.bidder = bid5.bidderCode = 'pubmatic'; + bid5.cpm = 3.0; // winning bid! + delete bid5.dealId; // no deal with winner + bidsReceived.push(bid5); + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + // Pubmatic wins but no deal. So only top bid KVPs for them is sent + // Rubicon has a dealId so passed through + // Appnexus has a dealId so passed through + expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ + 'hb_bidder': 'pubmatic', + 'hb_adid': '111111', + 'hb_pb': '3.0', + 'foobar': '300x250', + 'hb_deal_rubicon': '1234', + 'hb_pb_rubicon': '0.53', + 'hb_adid_rubicon': '148018fe5e', + 'hb_bidder_rubicon': 'rubicon', + 'hb_deal_appnexus': '4321', + 'hb_pb_appnexus': '0.1', + 'hb_adid_appnexus': '567891011', + 'hb_bidder_appnexus': 'appnexus' + }); + }); }); - it('selects the top bid when _sendAllBids true', () => { - config.setConfig({ enableSendAllBids: true }); + it('selects the top bid when enableSendAllBids true', function () { + enableSendAllBids = true; let targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // we should only get the targeting data for the one requested adunit expect(Object.keys(targeting).length).to.equal(1); - let sendAllBidCpm = Object.keys(targeting['/123456/header-bid-tag-0']).filter(key => key.indexOf('hb_pb_') != -1) + let sendAllBidCpm = Object.keys(targeting['/123456/header-bid-tag-0']).filter(key => key.indexOf(CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_') != -1) // we shouldn't get more than 1 key for hb_pb_${bidder} expect(sendAllBidCpm.length).to.equal(1); // expect the winning CPM to be equal to the sendAllBidCPM - expect(targeting['/123456/header-bid-tag-0']['hb_pb_rubicon']).to.deep.equal(targeting['/123456/header-bid-tag-0']['hb_pb']); + expect(targeting['/123456/header-bid-tag-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_rubicon']).to.deep.equal(targeting['/123456/header-bid-tag-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]); + }); + + it('ensures keys are properly generated when enableSendAllBids is true and multiple bidders use native', function() { + const nativeAdUnitCode = '/19968336/prebid_native_example_1'; + enableSendAllBids = true; + + // update mocks for this test to return native bids + amBidsReceivedStub.callsFake(function() { + return [nativeBid1, nativeBid2]; + }); + amGetAdUnitsStub.callsFake(function() { + return [nativeAdUnitCode]; + }); + + let targeting = targetingInstance.getAllTargeting([nativeAdUnitCode]); + expect(targeting[nativeAdUnitCode].hb_native_image).to.equal(nativeBid1.native.image.url); + expect(targeting[nativeAdUnitCode].hb_native_linkurl).to.equal(nativeBid1.native.clickUrl); + expect(targeting[nativeAdUnitCode].hb_native_title).to.equal(nativeBid1.native.title); + expect(targeting[nativeAdUnitCode].hb_native_image_dgad).to.exist.and.to.equal(nativeBid2.native.image.url); + expect(targeting[nativeAdUnitCode].hb_pb_dgads).to.exist.and.to.equal(nativeBid2.pbMg); + expect(targeting[nativeAdUnitCode].hb_native_body_appne).to.exist.and.to.equal(nativeBid1.native.body); + }); + + it('does not include adpod type bids in the getBidsReceived results', function () { + let adpodBid = utils.deepClone(bid1); + adpodBid.video = { context: 'adpod', durationSeconds: 15, durationBucket: 15 }; + adpodBid.cpm = 5; + bidsReceived.push(adpodBid); + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.contain.keys('hb_deal', 'hb_adid', 'hb_bidder'); + expect(targeting['/123456/header-bid-tag-0']['hb_adid']).to.equal(bid1.adId); }); }); // end getAllTargeting tests - describe('Targeting in concurrent auctions', () => { - describe('check getOldestBid', () => { - let bidExpiryStub; - let auctionManagerStub; - beforeEach(() => { - bidExpiryStub = sinon.stub(targetingModule, 'isBidExpired').returns(true); - auctionManagerStub = sinon.stub(auctionManager, 'getBidsReceived'); + describe('getAllTargeting without bids return empty object', function () { + let amBidsReceivedStub; + let amGetAdUnitsStub; + let bidExpiryStub; + + beforeEach(function () { + amBidsReceivedStub = sandbox.stub(auctionManager, 'getBidsReceived').callsFake(function() { + return []; }); + amGetAdUnitsStub = sandbox.stub(auctionManager, 'getAdUnitCodes').callsFake(function() { + return ['/123456/header-bid-tag-0']; + }); + bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); + }); + + it('returns targetingSet correctly', function () { + let targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + // we should only get the targeting data for the one requested adunit to at least exist even though it has no keys to set + expect(Object.keys(targeting).length).to.equal(1); + }); + }); // end getAllTargeting without bids return empty object - afterEach(() => { - bidExpiryStub.restore(); - auctionManagerStub.restore(); + describe('Targeting in concurrent auctions', function () { + describe('check getOldestBid', function () { + let bidExpiryStub; + let auctionManagerStub; + beforeEach(function () { + bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); + auctionManagerStub = sandbox.stub(auctionManager, 'getBidsReceived'); }); - it('should use bids from pool to get Winning Bid', () => { + it('should use bids from pool to get Winning Bid', function () { let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1'}), createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-2'}), @@ -166,7 +548,31 @@ describe('targeting tests', () => { expect(bids[1].adId).to.equal('adid-2'); }); - it('should not use rendered bid to get winning bid', () => { + it('should honor useBidCache', function() { + useBidCache = true; + + auctionManagerStub.returns([ + createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1'}), + createBidReceived({bidder: 'appnexus', cpm: 5, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-2'}), + ]); + + let adUnitCodes = ['code-0']; + targetingInstance.setLatestAuctionForAdUnit('code-0', 2); + + let bids = targetingInstance.getWinningBids(adUnitCodes); + + expect(bids.length).to.equal(1); + expect(bids[0].adId).to.equal('adid-1'); + + useBidCache = false; + + bids = targetingInstance.getWinningBids(adUnitCodes); + + expect(bids.length).to.equal(1); + expect(bids[0].adId).to.equal('adid-2'); + }); + + it('should not use rendered bid to get winning bid', function () { let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', status: 'rendered'}), createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-2'}), @@ -183,13 +589,13 @@ describe('targeting tests', () => { expect(bids[1].adId).to.equal('adid-3'); }); - it('should use oldest bids from bid pool to get winning bid', () => { - // Pool is having 4 bids from 2 auctions. There are 2 bids from rubicon, #2 which is first bid will be selected to take part in auction. + it('should use highest cpm bid from bid pool to get winning bid', function () { + // Pool is having 4 bids from 2 auctions. There are 2 bids from rubicon, #2 which is highest cpm bid will be selected to take part in auction. let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1'}), createBidReceived({bidder: 'rubicon', cpm: 9, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-0', adId: 'adid-2'}), createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-3'}), - createBidReceived({bidder: 'rubicon', cpm: 10, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-0', adId: 'adid-4'}), + createBidReceived({bidder: 'rubicon', cpm: 8, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-0', adId: 'adid-4'}), ]; auctionManagerStub.returns(bidsReceived); @@ -201,19 +607,15 @@ describe('targeting tests', () => { }); }); - describe('check bidExpiry', () => { + describe('check bidExpiry', function () { let auctionManagerStub; let timestampStub; - beforeEach(() => { - auctionManagerStub = sinon.stub(auctionManager, 'getBidsReceived'); - timestampStub = sinon.stub(utils, 'timestamp'); + beforeEach(function () { + auctionManagerStub = sandbox.stub(auctionManager, 'getBidsReceived'); + timestampStub = sandbox.stub(utils, 'timestamp'); }); - afterEach(() => { - auctionManagerStub.restore(); - timestampStub.restore(); - }); - it('should not include expired bids in the auction', () => { + it('should not include expired bids in the auction', function () { timestampStub.returns(200000); // Pool is having 4 bids from 2 auctions. All the bids are expired and only bid #3 is passing the bidExpiry check. let bidsReceived = [ @@ -232,4 +634,164 @@ describe('targeting tests', () => { }); }); }); + + describe('sortByDealAndPriceBucket', function() { + it('will properly sort bids when some bids have deals and some do not', function () { + let bids = [{ + adUnitTargeting: { + hb_adid: 'abc', + hb_pb: '1.00', + hb_deal: '1234' + } + }, { + adUnitTargeting: { + hb_adid: 'def', + hb_pb: '0.50', + } + }, { + adUnitTargeting: { + hb_adid: 'ghi', + hb_pb: '20.00', + hb_deal: '4532' + } + }, { + adUnitTargeting: { + hb_adid: 'jkl', + hb_pb: '9.00', + hb_deal: '9864' + } + }, { + adUnitTargeting: { + hb_adid: 'mno', + hb_pb: '50.00', + } + }, { + adUnitTargeting: { + hb_adid: 'pqr', + hb_pb: '100.00', + } + }]; + bids.sort(sortByDealAndPriceBucket); + expect(bids[0].adUnitTargeting.hb_adid).to.equal('ghi'); + expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adUnitTargeting.hb_adid).to.equal('abc'); + expect(bids[3].adUnitTargeting.hb_adid).to.equal('pqr'); + expect(bids[4].adUnitTargeting.hb_adid).to.equal('mno'); + expect(bids[5].adUnitTargeting.hb_adid).to.equal('def'); + }); + + it('will properly sort bids when all bids have deals', function () { + let bids = [{ + adUnitTargeting: { + hb_adid: 'abc', + hb_pb: '1.00', + hb_deal: '1234' + } + }, { + adUnitTargeting: { + hb_adid: 'def', + hb_pb: '0.50', + hb_deal: '4321' + } + }, { + adUnitTargeting: { + hb_adid: 'ghi', + hb_pb: '2.50', + hb_deal: '4532' + } + }, { + adUnitTargeting: { + hb_adid: 'jkl', + hb_pb: '2.00', + hb_deal: '9864' + } + }]; + bids.sort(sortByDealAndPriceBucket); + expect(bids[0].adUnitTargeting.hb_adid).to.equal('ghi'); + expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adUnitTargeting.hb_adid).to.equal('abc'); + expect(bids[3].adUnitTargeting.hb_adid).to.equal('def'); + }); + + it('will properly sort bids when no bids have deals', function () { + let bids = [{ + adUnitTargeting: { + hb_adid: 'abc', + hb_pb: '1.00' + } + }, { + adUnitTargeting: { + hb_adid: 'def', + hb_pb: '0.10' + } + }, { + adUnitTargeting: { + hb_adid: 'ghi', + hb_pb: '10.00' + } + }, { + adUnitTargeting: { + hb_adid: 'jkl', + hb_pb: '10.01' + } + }, { + adUnitTargeting: { + hb_adid: 'mno', + hb_pb: '1.00' + } + }, { + adUnitTargeting: { + hb_adid: 'pqr', + hb_pb: '100.00' + } + }]; + bids.sort(sortByDealAndPriceBucket); + expect(bids[0].adUnitTargeting.hb_adid).to.equal('pqr'); + expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adUnitTargeting.hb_adid).to.equal('ghi'); + expect(bids[3].adUnitTargeting.hb_adid).to.equal('abc'); + expect(bids[4].adUnitTargeting.hb_adid).to.equal('mno'); + expect(bids[5].adUnitTargeting.hb_adid).to.equal('def'); + }); + }); + + describe('setTargetingForAst', function () { + let sandbox, + apnTagStub; + beforeEach(function() { + sandbox = sinon.createSandbox(); + sandbox.stub(targetingInstance, 'resetPresetTargetingAST'); + apnTagStub = sandbox.stub(window.apntag, 'setKeywords'); + }); + afterEach(function () { + sandbox.restore(); + }); + + it('should set single addUnit code', function() { + let adUnitCode = 'testdiv-abc-ad-123456-0'; + sandbox.stub(targetingInstance, 'getAllTargeting').returns({ + 'testdiv1-abc-ad-123456-0': {hb_bidder: 'appnexus'} + }); + targetingInstance.setTargetingForAst(adUnitCode); + expect(targetingInstance.getAllTargeting.called).to.equal(true); + expect(targetingInstance.resetPresetTargetingAST.called).to.equal(true); + expect(apnTagStub.callCount).to.equal(1); + expect(apnTagStub.getCall(0).args[0]).to.deep.equal('testdiv1-abc-ad-123456-0'); + expect(apnTagStub.getCall(0).args[1]).to.deep.equal({HB_BIDDER: 'appnexus'}); + }); + + it('should set array of addUnit codes', function() { + let adUnitCodes = ['testdiv1-abc-ad-123456-0', 'testdiv2-abc-ad-123456-0'] + sandbox.stub(targetingInstance, 'getAllTargeting').returns({ + 'testdiv1-abc-ad-123456-0': {hb_bidder: 'appnexus'}, + 'testdiv2-abc-ad-123456-0': {hb_bidder: 'appnexus'} + }); + targetingInstance.setTargetingForAst(adUnitCodes); + expect(targetingInstance.getAllTargeting.called).to.equal(true); + expect(targetingInstance.resetPresetTargetingAST.called).to.equal(true); + expect(apnTagStub.callCount).to.equal(2); + expect(apnTagStub.getCall(1).args[0]).to.deep.equal('testdiv2-abc-ad-123456-0'); + expect(apnTagStub.getCall(1).args[1]).to.deep.equal({HB_BIDDER: 'appnexus'}); + }); + }); }); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 4c72917d7a7..485dd5cf077 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -9,12 +9,12 @@ import { createBidReceived } from 'test/fixtures/fixtures'; import { auctionManager, newAuctionManager } from 'src/auctionManager'; -import { targeting, newTargeting } from 'src/targeting'; +import { targeting, newTargeting, filters } from 'src/targeting'; import { config as configObj } from 'src/config'; import * as ajaxLib from 'src/ajax'; import * as auctionModule from 'src/auction'; import { newBidder, registerBidder } from 'src/adapters/bidderFactory'; -import * as targetingModule from 'src/targeting'; +import find from 'core-js/library/fn/array/find'; var assert = require('chai').assert; var expect = require('chai').expect; @@ -23,10 +23,9 @@ var urlParse = require('url-parse'); var prebid = require('src/prebid'); var utils = require('src/utils'); -// var bidmanager = require('src/bidmanager'); var bidfactory = require('src/bidfactory'); -var adloader = require('src/adloader'); -var adaptermanager = require('src/adaptermanager'); +var adloader = require('test/mocks/adloaderStub'); +var adapterManager = require('src/adapterManager').default; var events = require('src/events'); var adserver = require('src/adserver'); var CONSTANTS = require('src/constants.json'); @@ -109,6 +108,7 @@ var createSlotArrayScenario2 = function createSlotArrayScenario2() { window.googletag = { _slots: [], + _targeting: {}, pubads: function () { var self = this; return { @@ -118,6 +118,18 @@ window.googletag = { setSlots: function (slots) { self._slots = slots; + }, + + setTargeting: function(key, arrayOfValues) { + self._targeting[key] = arrayOfValues; + }, + + getTargeting: function() { + return self._targeting; + }, + + clearTargeting: function() { + self._targeting = {}; } }; } @@ -134,39 +146,69 @@ var createTagAST = function() { window.apntag = { keywords: [], tags: createTagAST(), - setKeywords: function(key, params) { + setKeywords: function(key, params, options) { var self = this; if (!self.tags.hasOwnProperty(key)) { return; } self.tags[key].keywords = this.tags[key].keywords || {}; + if (typeof options === 'object' && options !== null && options.overrideKeyValue === true) { + utils._each(params, function(param, id) { + self.tags[key].keywords[id] = param; + }); + } else { + utils._each(params, function (param, id) { + if (!self.tags[key].keywords.hasOwnProperty(id)) { + self.tags[key].keywords[id] = param; + } else if (!utils.isArray(self.tags[key].keywords[id])) { + self.tags[key].keywords[id] = [self.tags[key].keywords[id]].concat(param); + } else { + self.tags[key].keywords[id] = self.tags[key].keywords[id].concat(param); + } + }) + } + }, + getTag: function(tagId) { + return this.tags[tagId]; + }, + modifyTag: function(tagId, params) { + var output = {}; + + utils._each(this.tags[tagId], function(tag, id) { + output[id] = tag; + }); + utils._each(params, function(param, id) { - if (!self.tags[key].keywords.hasOwnProperty(id)) { self.tags[key].keywords[id] = param; } else if (!utils.isArray(self.tags[key].keywords[id])) { self.tags[key].keywords[id] = [self.tags[key].keywords[id]].concat(param); } else { self.tags[key].keywords[id] = self.tags[key].keywords[id].concat(param); } + output[id] = param; }); + + this.tags[tagId] = output; } -}; +} describe('Unit: Prebid Module', function () { let bidExpiryStub; - before(() => { - bidExpiryStub = sinon.stub(targetingModule, 'isBidExpired').callsFake(() => true); + beforeEach(function () { + bidExpiryStub = sinon.stub(filters, 'isBidNotExpired').callsFake(() => true); + configObj.setConfig({ useBidCache: true }); }); - after(function() { + afterEach(function() { $$PREBID_GLOBAL$$.adUnits = []; - targetingModule.isBidExpired.restore(); + bidExpiryStub.restore(); + configObj.setConfig({ useBidCache: false }); }); describe('getAdserverTargetingForAdUnitCodeStr', function () { - beforeEach(() => { + beforeEach(function () { resetAuction(); }); it('should return targeting info as a string', function () { const adUnitCode = config.adUnitCodes[0]; $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: true }); - var expected = 'foobar=0x0%2C300x250%2C300x600&hb_size=300x250&hb_pb=10.00&hb_adid=233bcbee889d46d&hb_bidder=appnexus&hb_size_triplelift=0x0&hb_pb_triplelift=10.00&hb_adid_triplelift=222bb26f9e8bd&hb_bidder_triplelift=triplelift&hb_size_appnexus=300x250&hb_pb_appnexus=10.00&hb_adid_appnexus=233bcbee889d46d&hb_bidder_appnexus=appnexus&hb_size_pagescience=300x250&hb_pb_pagescience=10.00&hb_adid_pagescience=25bedd4813632d7&hb_bidder_pagescienc=pagescience&hb_size_brightcom=300x250&hb_pb_brightcom=10.00&hb_adid_brightcom=26e0795ab963896&hb_bidder_brightcom=brightcom&hb_size_brealtime=300x250&hb_pb_brealtime=10.00&hb_adid_brealtime=275bd666f5a5a5d&hb_bidder_brealtime=brealtime&hb_size_pubmatic=300x250&hb_pb_pubmatic=10.00&hb_adid_pubmatic=28f4039c636b6a7&hb_bidder_pubmatic=pubmatic&hb_size_rubicon=300x600&hb_pb_rubicon=10.00&hb_adid_rubicon=29019e2ab586a5a&hb_bidder_rubicon=rubicon'; + var expected = 'foobar=0x0%2C300x250%2C300x600&' + CONSTANTS.TARGETING_KEYS.SIZE + '=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '=233bcbee889d46d&' + CONSTANTS.TARGETING_KEYS.BIDDER + '=appnexus&' + CONSTANTS.TARGETING_KEYS.SIZE + '_triplelift=0x0&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_triplelift=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_triplelift=222bb26f9e8bd&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_triplelift=triplelift&' + CONSTANTS.TARGETING_KEYS.SIZE + '_appnexus=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_appnexus=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_appnexus=233bcbee889d46d&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_appnexus=appnexus&' + CONSTANTS.TARGETING_KEYS.SIZE + '_pagescience=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_pagescience=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_pagescience=25bedd4813632d7&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_pagescienc=pagescience&' + CONSTANTS.TARGETING_KEYS.SIZE + '_brightcom=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_brightcom=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_brightcom=26e0795ab963896&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_brightcom=brightcom&' + CONSTANTS.TARGETING_KEYS.SIZE + '_brealtime=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_brealtime=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_brealtime=275bd666f5a5a5d&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_brealtime=brealtime&' + CONSTANTS.TARGETING_KEYS.SIZE + '_pubmatic=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_pubmatic=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_pubmatic=28f4039c636b6a7&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_pubmatic=pubmatic&' + CONSTANTS.TARGETING_KEYS.SIZE + '_rubicon=300x600&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_rubicon=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_rubicon=29019e2ab586a5a&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_rubicon=rubicon'; var result = $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr(adUnitCode); assert.equal(expected, result, 'returns expected string of ad targeting info'); }); @@ -192,50 +234,50 @@ describe('Unit: Prebid Module', function () { }); describe('getAdServerTargeting', function () { - beforeEach(() => { + beforeEach(function () { resetAuction(); }); - afterEach(() => { + afterEach(function () { resetAuction(); }); it('should return current targeting data for slots', function () { $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: true }); - const targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); - const expected = getAdServerTargeting(); + const targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); + const expected = getAdServerTargeting(['/19968336/header-bid-tag-0, /19968336/header-bid-tag1']); assert.deepEqual(targeting, expected, 'targeting ok'); }); - it('should return correct targeting with default settings', () => { - var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); + it('should return correct targeting with default settings', function () { + var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); var expected = { '/19968336/header-bid-tag-0': { foobar: '0x0,300x250,300x600', - hb_size: '300x250', - hb_pb: '10.00', - hb_adid: '233bcbee889d46d', - hb_bidder: 'appnexus' + [CONSTANTS.TARGETING_KEYS.SIZE]: '300x250', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '233bcbee889d46d', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus' }, '/19968336/header-bid-tag1': { foobar: '728x90', - hb_size: '728x90', - hb_pb: '10.00', - hb_adid: '24bd938435ec3fc', - hb_bidder: 'appnexus' + [CONSTANTS.TARGETING_KEYS.SIZE]: '728x90', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '24bd938435ec3fc', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus' } }; assert.deepEqual(targeting, expected); }); - it('should return correct targeting with bid landscape targeting on', () => { + it('should return correct targeting with bid landscape targeting on', function () { $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: true }); - var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); - var expected = getAdServerTargeting(); + var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); + var expected = getAdServerTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); assert.deepEqual(targeting, expected); }); - it("should include a losing bid's custom ad targeting key", () => { + it("should include a losing bid's custom ad targeting key", function () { // Let's make sure we're getting the expected losing bid. assert.equal(auction.getBidsReceived()[0]['bidderCode'], 'triplelift'); assert.equal(auction.getBidsReceived()[0]['cpm'], 0.112256); @@ -248,7 +290,7 @@ describe('Unit: Prebid Module', function () { auction.getBidsReceived = function() { return _bidsReceived }; - var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); + var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); // Ensure targeting for both ad placements includes the custom key. assert.equal( @@ -259,24 +301,24 @@ describe('Unit: Prebid Module', function () { var expected = { '/19968336/header-bid-tag-0': { foobar: '300x250,300x600', - hb_size: '300x250', - hb_pb: '10.00', - hb_adid: '233bcbee889d46d', - hb_bidder: 'appnexus', - always_use_me: 'abc' + always_use_me: 'abc', + [CONSTANTS.TARGETING_KEYS.SIZE]: '300x250', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '233bcbee889d46d', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus' }, '/19968336/header-bid-tag1': { foobar: '728x90', - hb_size: '728x90', - hb_pb: '10.00', - hb_adid: '24bd938435ec3fc', - hb_bidder: 'appnexus' + [CONSTANTS.TARGETING_KEYS.SIZE]: '728x90', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '24bd938435ec3fc', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus' } }; assert.deepEqual(targeting, expected); }); - it('should not overwrite winning bids custom keys targeting key', () => { + it('should not overwrite winning bids custom keys targeting key', function () { resetAuction(); // mimic a bidderSetting.standard key here for each bid and alwaysUseBid true for every bid let _bidsReceived = getBidResponses(); @@ -289,7 +331,7 @@ describe('Unit: Prebid Module', function () { $$PREBID_GLOBAL$$.bidderSettings = { 'standard': { adserverTargeting: [{ - key: 'hb_bidder', + key: CONSTANTS.TARGETING_KEYS.BIDDER, val: function(bidResponse) { return bidResponse.bidderCode; } @@ -299,7 +341,7 @@ describe('Unit: Prebid Module', function () { return bidResponse.adId; } }, { - key: 'hb_pb', + key: CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, val: function(bidResponse) { return bidResponse.pbMg; } @@ -312,32 +354,31 @@ describe('Unit: Prebid Module', function () { } }; - var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); + var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); var expected = { '/19968336/header-bid-tag-0': { foobar: '300x250', - hb_size: '300x250', - hb_pb: '10.00', - hb_adid: '233bcbee889d46d', - hb_bidder: 'appnexus', - custom_ad_id: '233bcbee889d46d' + custom_ad_id: '233bcbee889d46d', + [CONSTANTS.TARGETING_KEYS.SIZE]: '300x250', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '233bcbee889d46d', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus' }, '/19968336/header-bid-tag1': { foobar: '728x90', - hb_size: '728x90', - hb_pb: '10.00', - hb_adid: '24bd938435ec3fc', - hb_bidder: 'appnexus', + [CONSTANTS.TARGETING_KEYS.SIZE]: '728x90', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '24bd938435ec3fc', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus', custom_ad_id: '24bd938435ec3fc' } }; - assert.deepEqual(targeting, expected); $$PREBID_GLOBAL$$.bidderSettings = {}; }); - it('should not send standard targeting keys when the bid has `sendStandardTargeting` set to `false`', () => { + it('should not send standard targeting keys when the bid has `sendStandardTargeting` set to `false`', function () { let _bidsReceived = getBidResponses(); _bidsReceived.forEach(bid => { bid.adserverTargeting.custom_ad_id = bid.adId; @@ -346,7 +387,7 @@ describe('Unit: Prebid Module', function () { auction.getBidsReceived = function() { return _bidsReceived }; - var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); + var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); var expected = { '/19968336/header-bid-tag-0': { @@ -376,8 +417,7 @@ describe('Unit: Prebid Module', function () { let auction; let ajaxStub; let cbTimeout = 3000; - let auctionManagerInstance = newAuctionManager(); - let targeting = newTargeting(auctionManagerInstance); + let targeting; let RESPONSE = { 'version': '0.0.1', @@ -407,16 +447,18 @@ describe('Unit: Prebid Module', function () { 'trackers': [{ 'impression_urls': ['http://lax1-ib.adnxs.com/impression'] }] - } + }, + 'viewability': { + 'config': ''} }] }] }; - before(() => { + before(function () { $$PREBID_GLOBAL$$.bidderSettings = {}; currentPriceBucket = configObj.getConfig('priceGranularity'); configObj.setConfig({ priceGranularity: customConfigObject }); - sinon.stub(adaptermanager, 'makeBidRequests').callsFake(() => ([{ + sinon.stub(adapterManager, 'makeBidRequests').callsFake(() => ([{ 'bidderCode': 'appnexus', 'auctionId': '20882439e3238c', 'bidderRequestId': '331f3cf3f1d9c8', @@ -448,12 +490,14 @@ describe('Unit: Prebid Module', function () { )); }); - after(() => { + after(function () { configObj.setConfig({ priceGranularity: currentPriceBucket }); - adaptermanager.makeBidRequests.restore(); + adapterManager.makeBidRequests.restore(); }) - beforeEach(() => { + beforeEach(function () { + let auctionManagerInstance = newAuctionManager(); + targeting = newTargeting(auctionManagerInstance); let adUnits = [{ code: 'div-gpt-ad-1460505748561-0', sizes: [[300, 250], [300, 600]], @@ -475,36 +519,36 @@ describe('Unit: Prebid Module', function () { }); }); - afterEach(() => { + afterEach(function () { ajaxStub.restore(); }); - it('should get correct hb_pb when using bid.cpm is between 0 to 5', () => { + it('should get correct ' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + ' when using bid.cpm is between 0 to 5', function() { RESPONSE.tags[0].ads[0].cpm = 2.1234; auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0']['hb_pb']).to.equal('2.12'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('2.12'); }); - it('should get correct hb_pb when using bid.cpm is between 5 to 8', () => { + it('should get correct ' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + ' when using bid.cpm is between 5 to 8', function() { RESPONSE.tags[0].ads[0].cpm = 6.78; auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0']['hb_pb']).to.equal('6.75'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('6.75'); }); - it('should get correct hb_pb when using bid.cpm is between 8 to 20', () => { + it('should get correct ' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + ' when using bid.cpm is between 8 to 20', function() { RESPONSE.tags[0].ads[0].cpm = 19.5234; auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0']['hb_pb']).to.equal('19.50'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('19.50'); }); - it('should get correct hb_pb when using bid.cpm is between 20 to 25', () => { + it('should get correct ' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + ' when using bid.cpm is between 20 to 25', function() { RESPONSE.tags[0].ads[0].cpm = 21.5234; auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0']['hb_pb']).to.equal('21.00'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('21.00'); }); }); @@ -545,7 +589,9 @@ describe('Unit: Prebid Module', function () { 'trackers': [{ 'impression_urls': ['http://lax1-ib.adnxs.com/impression'] }] - } + }, + 'viewability': { + 'config': ''} }] }] }; @@ -577,7 +623,9 @@ describe('Unit: Prebid Module', function () { 'trackers': [{ 'impression_urls': ['http://lax1-ib.adnxs.com/impression'] }] - } + }, + 'viewability': { + 'config': ''} }] }] }; @@ -677,9 +725,9 @@ describe('Unit: Prebid Module', function () { }); }; - before(() => { + before(function () { currentPriceBucket = configObj.getConfig('priceGranularity'); - sinon.stub(adaptermanager, 'makeBidRequests').callsFake(() => ([{ + sinon.stub(adapterManager, 'makeBidRequests').callsFake(() => ([{ 'bidderCode': 'appnexus', 'auctionId': '20882439e3238c', 'bidderRequestId': '331f3cf3f1d9c8', @@ -710,16 +758,16 @@ describe('Unit: Prebid Module', function () { }])); }); - after(() => { + after(function () { configObj.setConfig({ priceGranularity: currentPriceBucket }); - adaptermanager.makeBidRequests.restore(); + adapterManager.makeBidRequests.restore(); }) - afterEach(() => { + afterEach(function () { ajaxStub.restore(); }); - it('should get correct hb_pb with cpm between 0 - 5', () => { + it('should get correct ' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + ' with cpm between 0 - 5', function() { initTestConfig({ adUnits: [createAdUnit('div-gpt-ad-1460505748561-0')], adUnitCodes: ['div-gpt-ad-1460505748561-0'] @@ -730,10 +778,10 @@ describe('Unit: Prebid Module', function () { auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0']['hb_pb']).to.equal('3.25'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.25'); }); - it('should get correct hb_pb with cpm between 21 - 100', () => { + it('should get correct ' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + ' with cpm between 21 - 100', function() { initTestConfig({ adUnits: [createAdUnit('div-gpt-ad-1460505748561-0')], adUnitCodes: ['div-gpt-ad-1460505748561-0'] @@ -744,10 +792,10 @@ describe('Unit: Prebid Module', function () { auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0']['hb_pb']).to.equal('43.00'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('43.00'); }); - it('should only apply price granularity if bid media type matches', () => { + it('should only apply price granularity if bid media type matches', function () { initTestConfig({ adUnits: [ createAdUnit('div-gpt-ad-1460505748561-0', 'video') ], adUnitCodes: ['div-gpt-ad-1460505748561-0'] @@ -758,18 +806,25 @@ describe('Unit: Prebid Module', function () { auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0']['hb_pb']).to.equal('3.00'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.00'); }); }); describe('getBidResponses', function () { + it('should return empty obj when last auction Id had no responses', function () { + auctionManager.getLastAuctionId = () => 999994; + var result = $$PREBID_GLOBAL$$.getBidResponses(); + assert.deepEqual(result, {}, 'expected bid responses are returned'); + }); + it('should return expected bid responses when not passed an adunitCode', function () { + auctionManager.getLastAuctionId = () => 654321; var result = $$PREBID_GLOBAL$$.getBidResponses(); var compare = getBidResponsesFromAPI(); assert.deepEqual(result, compare, 'expected bid responses are returned'); }); - it('should return bid responses for most recent requestId only', () => { + it('should return bid responses for most recent auctionId only', function () { const responses = $$PREBID_GLOBAL$$.getBidResponses(); assert.equal(responses[Object.keys(responses)[0]].bids.length, 4); }); @@ -788,12 +843,12 @@ describe('Unit: Prebid Module', function () { describe('setTargetingForGPTAsync', function () { let logErrorSpy; - beforeEach(() => { + beforeEach(function () { logErrorSpy = sinon.spy(utils, 'logError'); resetAuction(); }); - afterEach(() => { + afterEach(function () { utils.logError.restore(); resetAuction(); }); @@ -820,7 +875,7 @@ describe('Unit: Prebid Module', function () { $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: true }); $$PREBID_GLOBAL$$.setTargetingForGPTAsync('/19968336/header-bid-tag-0'); - expect(slots[0].spySetTargeting.args).to.deep.contain.members([['hb_bidder', 'appnexus'], ['hb_adid_appnexus', '233bcbee889d46d'], ['hb_pb_appnexus', '10.00']]); + expect(slots[0].spySetTargeting.args).to.deep.contain.members([[CONSTANTS.TARGETING_KEYS.BIDDER, 'appnexus'], [CONSTANTS.TARGETING_KEYS.AD_ID + '_appnexus', '233bcbee889d46d'], [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_appnexus', '10.00']]); }); it('should set targeting when passed an array of ad unit codes with enableSendAllBids', function () { @@ -829,7 +884,7 @@ describe('Unit: Prebid Module', function () { $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: true }); $$PREBID_GLOBAL$$.setTargetingForGPTAsync(['/19968336/header-bid-tag-0']); - expect(slots[0].spySetTargeting.args).to.deep.contain.members([['hb_bidder', 'appnexus'], ['hb_adid_appnexus', '233bcbee889d46d'], ['hb_pb_appnexus', '10.00']]); + expect(slots[0].spySetTargeting.args).to.deep.contain.members([[CONSTANTS.TARGETING_KEYS.BIDDER, 'appnexus'], [CONSTANTS.TARGETING_KEYS.AD_ID + '_appnexus', '233bcbee889d46d'], [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_appnexus', '10.00']]); }); it('should set targeting from googletag data', function () { @@ -876,19 +931,19 @@ describe('Unit: Prebid Module', function () { var expected = [ [ - 'hb_bidder', + CONSTANTS.TARGETING_KEYS.BIDDER, 'appnexus' ], [ - 'hb_adid', + CONSTANTS.TARGETING_KEYS.AD_ID, '233bcbee889d46d' ], [ - 'hb_pb', + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET, '10.00' ], [ - 'hb_size', + CONSTANTS.TARGETING_KEYS.SIZE, '300x250' ], [ @@ -935,7 +990,7 @@ describe('Unit: Prebid Module', function () { var spyLogError = null; var spyLogMessage = null; var inIframe = true; - let triggerPixelStub; + var triggerPixelStub; function pushBidResponseToAuction(obj) { adResponse = Object.assign({ @@ -975,7 +1030,7 @@ describe('Unit: Prebid Module', function () { inIframe = true; sinon.stub(utils, 'inIframe').callsFake(() => inIframe); - triggerPixelStub = sinon.stub(utils, 'triggerPixel'); + triggerPixelStub = sinon.stub(utils.internal, 'triggerPixel'); }); afterEach(function () { @@ -983,7 +1038,7 @@ describe('Unit: Prebid Module', function () { utils.logError.restore(); utils.logMessage.restore(); utils.inIframe.restore(); - utils.triggerPixel.restore(); + triggerPixelStub.restore(); }); it('should require doc and id params', function () { @@ -1023,7 +1078,7 @@ describe('Unit: Prebid Module', function () { assert.ok(spyLogError.calledWith(error), 'expected error was logged'); }); - it('should log an error when not in an iFrame', () => { + it('should log an error when not in an iFrame', function () { pushBidResponseToAuction({ ad: "" }); @@ -1033,7 +1088,7 @@ describe('Unit: Prebid Module', function () { assert.ok(spyLogError.calledWith(error), 'expected error was logged'); }); - it('should not render videos', () => { + it('should not render videos', function () { pushBidResponseToAuction({ mediatype: 'video' }); @@ -1069,7 +1124,7 @@ describe('Unit: Prebid Module', function () { assert.deepEqual($$PREBID_GLOBAL$$.getAllWinningBids()[0], adResponse); }); - it('fires billing url if present on s2s bid', () => { + it('fires billing url if present on s2s bid', function () { const burl = 'http://www.example.com/burl'; pushBidResponseToAuction({ ad: '
    ad
    ', @@ -1084,12 +1139,13 @@ describe('Unit: Prebid Module', function () { }); }); - describe('requestBids', () => { + describe('requestBids', function () { let logMessageSpy; let makeRequestsStub; let xhr; let adUnits; let clock; + let bidsBackHandlerStub = sinon.stub(); const BIDDER_CODE = 'sampleBidder'; let bids = [{ @@ -1126,9 +1182,9 @@ describe('Unit: Prebid Module', function () { 'start': 1000 }]; - beforeEach(() => { + beforeEach(function () { logMessageSpy = sinon.spy(utils, 'logMessage'); - makeRequestsStub = sinon.stub(adaptermanager, 'makeBidRequests'); + makeRequestsStub = sinon.stub(adapterManager, 'makeBidRequests'); makeRequestsStub.returns(bidRequests); xhr = sinon.useFakeXMLHttpRequest(); @@ -1139,26 +1195,32 @@ describe('Unit: Prebid Module', function () { ] }]; let adUnitCodes = ['adUnit-code']; - let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: 2000}); + let auction = auctionModule.newAuction({ + adUnits, + adUnitCodes, + callback: bidsBackHandlerStub, + cbTimeout: 2000 + }); let createAuctionStub = sinon.stub(auctionModule, 'newAuction'); createAuctionStub.returns(auction); }); - afterEach(() => { + afterEach(function () { clock.restore(); - adaptermanager.makeBidRequests.restore(); + adapterManager.makeBidRequests.restore(); auctionModule.newAuction.restore(); utils.logMessage.restore(); xhr.restore(); }); - it('should execute callback after timeout', () => { + it('should execute callback after timeout', function () { let spec = { code: BIDDER_CODE, isBidRequestValid: sinon.stub(), buildRequests: sinon.stub(), interpretResponse: sinon.stub(), - getUserSyncs: sinon.stub() + getUserSyncs: sinon.stub(), + onTimeout: sinon.stub() }; registerBidder(spec); @@ -1168,7 +1230,7 @@ describe('Unit: Prebid Module', function () { clock = sinon.useFakeTimers(); let requestObj = { - bidsBackHandler: function bidsBackHandlerCallback() {}, + bidsBackHandler: null, // does not need to be defined because of newAuction mock in beforeEach timeout: 2000, adUnits: adUnits }; @@ -1180,23 +1242,111 @@ describe('Unit: Prebid Module', function () { clock.tick(1); assert.ok(logMessageSpy.calledWith(sinon.match(re)), 'executeCallback called'); + + expect(bidsBackHandlerStub.getCall(0).args[1]).to.equal(true, + 'bidsBackHandler should be called with timedOut=true'); + + sinon.assert.called(spec.onTimeout); + }); + + it('should execute callback after setTargeting', function () { + let spec = { + code: BIDDER_CODE, + isBidRequestValid: sinon.stub(), + buildRequests: sinon.stub(), + interpretResponse: sinon.stub(), + onSetTargeting: sinon.stub() + }; + + registerBidder(spec); + spec.buildRequests.returns([{'id': 123, 'method': 'POST'}]); + spec.isBidRequestValid.returns(true); + spec.interpretResponse.returns(bids); + + const bidId = 1; + const auctionId = 1; + let adResponse = Object.assign({ + auctionId: auctionId, + adId: String(bidId), + width: 300, + height: 250, + adUnitCode: bidRequests[0].bids[0].adUnitCode, + adserverTargeting: { + 'hb_bidder': BIDDER_CODE, + 'hb_adid': bidId, + 'hb_pb': bids[0].cpm, + 'hb_size': '300x250', + }, + bidder: bids[0].bidderCode, + }, bids[0]); + auction.getBidsReceived = function() { return [adResponse]; } + auction.getAuctionId = () => auctionId; + + clock = sinon.useFakeTimers(); + let requestObj = { + bidsBackHandler: null, // does not need to be defined because of newAuction mock in beforeEach + timeout: 2000, + adUnits: adUnits + }; + + $$PREBID_GLOBAL$$.requestBids(requestObj); + $$PREBID_GLOBAL$$.setTargetingForGPTAsync(); + + sinon.assert.called(spec.onSetTargeting); }); }) - describe('requestBids', () => { + describe('requestBids', function () { + let sandbox; + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + afterEach(function () { + sandbox.restore(); + }); + describe('bidRequests is empty', function () { + it('should log warning message and execute callback if bidRequests is empty', function () { + let bidsBackHandler = function bidsBackHandlerCallback() {}; + let spyExecuteCallback = sinon.spy(bidsBackHandler); + let logWarnSpy = sandbox.spy(utils, 'logWarn'); + + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [ + { + code: 'test1', + bids: [], + }, { + code: 'test2', + bids: [], + } + ], + bidsBackHandler: spyExecuteCallback + }); + + assert.ok(logWarnSpy.calledWith('No valid bid requests returned for auction'), 'expected warning message was logged'); + assert.ok(spyExecuteCallback.calledOnce, 'callback executed when bidRequests is empty'); + }); + }); + }); + + describe('requestBids', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = request => requests.push(request); }); - afterEach(() => xhr.restore()); + afterEach(function () { + xhr.restore(); + }); var adUnitsBackup; var auctionManagerStub; - let logMessageSpy + let logMessageSpy; + let logInfoSpy; + let logErrorSpy; let spec = { code: 'sampleBidder', @@ -1207,23 +1357,30 @@ describe('Unit: Prebid Module', function () { }; registerBidder(spec); - describe('part 1', () => { - beforeEach(() => { + describe('part 1', function () { + let auctionArgs; + + beforeEach(function () { adUnitsBackup = auction.getAdUnits auctionManagerStub = sinon.stub(auctionManager, 'createAuction').callsFake(function() { + auctionArgs = arguments[0]; return auction; }); logMessageSpy = sinon.spy(utils, 'logMessage'); + logInfoSpy = sinon.spy(utils, 'logInfo'); + logErrorSpy = sinon.spy(utils, 'logError'); }); - afterEach(() => { + afterEach(function () { auction.getAdUnits = adUnitsBackup; auctionManager.createAuction.restore(); utils.logMessage.restore(); + utils.logInfo.restore(); + utils.logError.restore(); resetAuction(); }); - it('should log message when adUnits not configured', () => { + it('should log message when adUnits not configured', function () { $$PREBID_GLOBAL$$.adUnits = []; try { $$PREBID_GLOBAL$$.requestBids({}); @@ -1233,7 +1390,51 @@ describe('Unit: Prebid Module', function () { assert.ok(logMessageSpy.calledWith('No adUnits configured. No bids requested.'), 'expected message was logged'); }); - it('should execute callback immediately if adUnits is empty', () => { + it('should always attach new transactionIds to adUnits passed to requestBids', function () { + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [ + { + code: 'test1', + transactionId: 'd0676a3c-ff32-45a5-af65-8175a8e7ddca', + bids: [] + }, { + code: 'test2', + bids: [] + } + ] + }); + + expect(auctionArgs.adUnits[0]).to.have.property('transactionId') + .and.to.match(/[a-f0-9\-]{36}/i) + .and.not.to.equal('d0676a3c-ff32-45a5-af65-8175a8e7ddca'); + expect(auctionArgs.adUnits[1]).to.have.property('transactionId') + .and.to.match(/[a-f0-9\-]{36}/i); + }); + + it('should notify targeting of the latest auction for each adUnit', function () { + let latestStub = sinon.stub(targeting, 'setLatestAuctionForAdUnit'); + let getAuctionStub = sinon.stub(auction, 'getAuctionId').returns(2); + + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [ + { + code: 'test1', + bids: [] + }, { + code: 'test2', + bids: [] + } + ] + }); + + expect(latestStub.firstCall.calledWith('test1', 2)).to.equal(true); + expect(latestStub.secondCall.calledWith('test2', 2)).to.equal(true); + + latestStub.restore(); + getAuctionStub.restore(); + }); + + it('should execute callback immediately if adUnits is empty', function () { var bidsBackHandler = function bidsBackHandlerCallback() {}; var spyExecuteCallback = sinon.spy(bidsBackHandler); @@ -1246,7 +1447,7 @@ describe('Unit: Prebid Module', function () { ' empty'); }); - it('should not propagate exceptions from bidsBackHandler', () => { + it('should not propagate exceptions from bidsBackHandler', function () { $$PREBID_GLOBAL$$.adUnits = []; var requestObj = { @@ -1260,18 +1461,254 @@ describe('Unit: Prebid Module', function () { $$PREBID_GLOBAL$$.requestBids(requestObj); }).not.to.throw(); }); + + describe('checkAdUnitSetup', function() { + describe('positive tests for validating adUnits', function() { + it('should maintain adUnit structure and adUnit.sizes is replaced', function () { + let fullAdUnit = [{ + code: 'test1', + sizes: [[300, 250], [300, 600]], + mediaTypes: { + banner: { + sizes: [[300, 250]] + }, + video: { + playerSize: [[640, 480]] + }, + native: { + image: { + sizes: [150, 150], + aspect_ratios: [140, 140] + }, + icon: { + sizes: [75, 75] + } + } + }, + bids: [] + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: fullAdUnit + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[640, 480]]); + expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.deep.equal([[640, 480]]); + expect(auctionArgs.adUnits[0].mediaTypes.native.image.sizes).to.deep.equal([150, 150]); + expect(auctionArgs.adUnits[0].mediaTypes.native.icon.sizes).to.deep.equal([75, 75]); + expect(auctionArgs.adUnits[0].mediaTypes.native.image.aspect_ratios).to.deep.equal([140, 140]); + + let noOptnlFieldAdUnit = [{ + code: 'test2', + bids: [], + sizes: [[300, 250], [300, 600]], + mediaTypes: { + banner: { + sizes: [[300, 250]] + }, + video: { + context: 'outstream' + }, + native: { + image: { + required: true + }, + icon: { + required: true + } + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: noOptnlFieldAdUnit + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[300, 250]]); + expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; + + let mixedAdUnit = [{ + code: 'test3', + bids: [], + sizes: [[300, 250], [300, 600]], + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[400, 350]] + }, + native: { + image: { + aspect_ratios: [200, 150], + required: true + } + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: mixedAdUnit + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[400, 350]]); + expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; + + let altVideoPlayerSize = [{ + code: 'test4', + bids: [], + sizes: [[600, 600]], + mediaTypes: { + video: { + playerSize: [640, 480] + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: altVideoPlayerSize + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[640, 480]]); + expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.deep.equal([[640, 480]]); + expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; + assert.ok(logInfoSpy.calledWith('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'), 'expected message was logged'); + }); + + it('should normalize adUnit.sizes and adUnit.mediaTypes.banner.sizes', function () { + let normalizeAdUnit = [{ + code: 'test5', + bids: [], + sizes: [300, 250], + mediaTypes: { + banner: { + sizes: [300, 250] + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: normalizeAdUnit + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[300, 250]]); + expect(auctionArgs.adUnits[0].mediaTypes.banner.sizes).to.deep.equal([[300, 250]]); + }); + }); + + describe('negative tests for validating adUnits', function() { + it('should throw error message and delete an object/property', function () { + let badBanner = [{ + code: 'testb1', + bids: [], + sizes: [[300, 250], [300, 600]], + mediaTypes: { + banner: { + name: 'test' + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: badBanner + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[300, 250], [300, 600]]); + expect(auctionArgs.adUnits[0].mediaTypes.banner).to.be.undefined; + assert.ok(logErrorSpy.calledWith('Detected a mediaTypes.banner object did not include sizes. This is a required field for the mediaTypes.banner object. Removing invalid mediaTypes.banner object from request.')); + + let badVideo1 = [{ + code: 'testb2', + bids: [], + sizes: [[600, 600]], + mediaTypes: { + video: { + playerSize: ['600x400'] + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: badVideo1 + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[600, 600]]); + expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.be.undefined; + expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; + assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); + + let badVideo2 = [{ + code: 'testb3', + bids: [], + sizes: [[600, 600]], + mediaTypes: { + video: { + playerSize: [['300', '200']] + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: badVideo2 + }); + expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[600, 600]]); + expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.be.undefined; + expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; + assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); + + let badNativeImgSize = [{ + code: 'testb4', + bids: [], + mediaTypes: { + native: { + image: { + sizes: '300x250' + } + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: badNativeImgSize + }); + expect(auctionArgs.adUnits[0].mediaTypes.native.image.sizes).to.be.undefined; + expect(auctionArgs.adUnits[0].mediaTypes.native.image).to.exist; + assert.ok(logErrorSpy.calledWith('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.')); + + let badNativeImgAspRat = [{ + code: 'testb5', + bids: [], + mediaTypes: { + native: { + image: { + aspect_ratios: '300x250' + } + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: badNativeImgAspRat + }); + expect(auctionArgs.adUnits[0].mediaTypes.native.image.aspect_ratios).to.be.undefined; + expect(auctionArgs.adUnits[0].mediaTypes.native.image).to.exist; + assert.ok(logErrorSpy.calledWith('Please use an array of sizes for native.image.aspect_ratios field. Removing invalid mediaTypes.native.image.aspect_ratios property from request.')); + + let badNativeIcon = [{ + code: 'testb6', + bids: [], + mediaTypes: { + native: { + icon: { + sizes: '300x250' + } + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: badNativeIcon + }); + expect(auctionArgs.adUnits[0].mediaTypes.native.icon.sizes).to.be.undefined; + expect(auctionArgs.adUnits[0].mediaTypes.native.icon).to.exist; + assert.ok(logErrorSpy.calledWith('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.')); + }); + }); + }); }); - describe('multiformat requests', () => { + describe('multiformat requests', function () { let spyCallBids; let createAuctionStub; let adUnits; - beforeEach(() => { + beforeEach(function () { adUnits = [{ code: 'adUnit-code', mediaTypes: { - banner: {}, + banner: { + sizes: [[300, 250]] + }, native: {}, }, sizes: [[300, 250], [300, 600]], @@ -1281,35 +1718,36 @@ describe('Unit: Prebid Module', function () { ] }]; adUnitCodes = ['adUnit-code']; + configObj.setConfig({maxRequestsPerOrigin: Number.MAX_SAFE_INTEGER || 99999999}); let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: timeout}); - spyCallBids = sinon.spy(adaptermanager, 'callBids'); + spyCallBids = sinon.spy(adapterManager, 'callBids'); createAuctionStub = sinon.stub(auctionModule, 'newAuction'); createAuctionStub.returns(auction); }) - afterEach(() => { + afterEach(function () { auctionModule.newAuction.restore(); - adaptermanager.callBids.restore(); + adapterManager.callBids.restore(); }); - it('bidders that support one of the declared formats are allowed to participate', () => { + it('bidders that support one of the declared formats are allowed to participate', function () { $$PREBID_GLOBAL$$.requestBids({adUnits}); - sinon.assert.calledOnce(adaptermanager.callBids); + sinon.assert.calledOnce(adapterManager.callBids); - const spyArgs = adaptermanager.callBids.getCall(0); + const spyArgs = adapterManager.callBids.getCall(0); const biddersCalled = spyArgs.args[0][0].bids; // appnexus and sampleBidder both support banner expect(biddersCalled.length).to.equal(2); }); - it('bidders that do not support one of the declared formats are dropped', () => { + it('bidders that do not support one of the declared formats are dropped', function () { delete adUnits[0].mediaTypes.banner; $$PREBID_GLOBAL$$.requestBids({adUnits}); - sinon.assert.calledOnce(adaptermanager.callBids); + sinon.assert.calledOnce(adapterManager.callBids); - const spyArgs = adaptermanager.callBids.getCall(0); + const spyArgs = adapterManager.callBids.getCall(0); const biddersCalled = spyArgs.args[0][0].bids; // only appnexus supports native @@ -1317,12 +1755,12 @@ describe('Unit: Prebid Module', function () { }); }); - describe('part 2', () => { + describe('part 2', function () { let spyCallBids; let createAuctionStub; let adUnits; - before(() => { + before(function () { adUnits = [{ code: 'adUnit-code', sizes: [[300, 250], [300, 600]], @@ -1353,24 +1791,24 @@ describe('Unit: Prebid Module', function () { createAuctionStub.returns(auction); }); - after(() => { + after(function () { auctionModule.newAuction.restore(); }); - beforeEach(() => { - spyCallBids = sinon.spy(adaptermanager, 'callBids'); + beforeEach(function () { + spyCallBids = sinon.spy(adapterManager, 'callBids'); }) - afterEach(() => { - adaptermanager.callBids.restore(); + afterEach(function () { + adapterManager.callBids.restore(); }) - it('should callBids if a native adUnit has all native bidders', () => { + it('should callBids if a native adUnit has all native bidders', function () { $$PREBID_GLOBAL$$.requestBids({adUnits}); - sinon.assert.calledOnce(adaptermanager.callBids); + sinon.assert.calledOnce(adapterManager.callBids); }); - it('should call callBids function on adaptermanager', () => { + it('should call callBids function on adapterManager', function () { let adUnits = [{ code: 'adUnit-code', sizes: [[300, 250], [300, 600]], @@ -1379,10 +1817,10 @@ describe('Unit: Prebid Module', function () { ] }]; $$PREBID_GLOBAL$$.requestBids({adUnits}); - assert.ok(spyCallBids.called, 'called adaptermanager.callBids'); + assert.ok(spyCallBids.called, 'called adapterManager.callBids'); }); - it('splits native type to individual native assets', () => { + it('splits native type to individual native assets', function () { let adUnits = [{ code: 'adUnit-code', nativeParams: {type: 'image'}, @@ -1392,7 +1830,7 @@ describe('Unit: Prebid Module', function () { ] }]; $$PREBID_GLOBAL$$.requestBids({adUnits}); - const spyArgs = adaptermanager.callBids.getCall(0); + const spyArgs = adapterManager.callBids.getCall(0); const nativeRequest = spyArgs.args[1][0].bids[0].nativeParams; expect(nativeRequest).to.deep.equal({ image: {required: true}, @@ -1406,7 +1844,7 @@ describe('Unit: Prebid Module', function () { }); }); - describe('part-3', () => { + describe('part-3', function () { let auctionManagerInstance = newAuctionManager(); let auctionManagerStub; let adUnits1 = getAdUnits().filter((adUnit) => { @@ -1455,7 +1893,7 @@ describe('Unit: Prebid Module', function () { }; beforeEach(function() { - spyCallBids = sinon.spy(adaptermanager, 'callBids'); + spyCallBids = sinon.spy(adapterManager, 'callBids'); auctionManagerStub = sinon.stub(auctionManager, 'createAuction'); auctionManagerStub.onCall(0).returns(auction1); auctionManagerStub.onCall(1).returns(auction2); @@ -1463,10 +1901,10 @@ describe('Unit: Prebid Module', function () { afterEach(function() { auctionManager.createAuction.restore(); - adaptermanager.callBids.restore(); + adapterManager.callBids.restore(); }); - it('should not queue bid requests when a previous bid request is in process', () => { + it('should not queue bid requests when a previous bid request is in process', function () { var requestObj1 = { bidsBackHandler: function bidsBackHandlerCallback() {}, timeout: 2000, @@ -1487,20 +1925,20 @@ describe('Unit: Prebid Module', function () { assert.ok(spyCallBids.calledTwice, 'When two requests for bids are made both should be' + ' callBids immediately'); - let result = targeting.getAllTargeting(); // $$PREBID_GLOBAL$$.getAdserverTargeting(); + let result = targeting.getAllTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); // $$PREBID_GLOBAL$$.getAdserverTargeting(); let expected = { '/19968336/header-bid-tag-0': { 'foobar': '0x0,300x250,300x600', - 'hb_size': '300x250', - 'hb_pb': '10.00', - 'hb_adid': '233bcbee889d46d', - 'hb_bidder': 'appnexus' + [CONSTANTS.TARGETING_KEYS.SIZE]: '300x250', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '233bcbee889d46d', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus' }, '/19968336/header-bid-tag1': { - 'hb_bidder': 'appnexus', - 'hb_adid': '24bd938435ec3fc', - 'hb_pb': '10.00', - 'hb_size': '728x90', + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '24bd938435ec3fc', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.SIZE]: '728x90', 'foobar': '728x90' } } @@ -1509,8 +1947,8 @@ describe('Unit: Prebid Module', function () { }); }); - describe('onEvent', () => { - it('should log an error when handler is not a function', () => { + describe('onEvent', function () { + it('should log an error when handler is not a function', function () { var spyLogError = sinon.spy(utils, 'logError'); var event = 'testEvent'; $$PREBID_GLOBAL$$.onEvent(event); @@ -1519,7 +1957,7 @@ describe('Unit: Prebid Module', function () { utils.logError.restore(); }); - it('should log an error when id provided is not valid for event', () => { + it('should log an error when id provided is not valid for event', function () { var spyLogError = sinon.spy(utils, 'logError'); var event = 'bidWon'; $$PREBID_GLOBAL$$.onEvent(event, Function, 'testId'); @@ -1528,7 +1966,7 @@ describe('Unit: Prebid Module', function () { utils.logError.restore(); }); - it('should call events.on with valid parameters', () => { + it('should call events.on with valid parameters', function () { var spyEventsOn = sinon.spy(events, 'on'); $$PREBID_GLOBAL$$.onEvent('bidWon', Function); assert.ok(spyEventsOn.calledWith('bidWon', Function)); @@ -1536,15 +1974,15 @@ describe('Unit: Prebid Module', function () { }); }); - describe('offEvent', () => { - it('should return when id provided is not valid for event', () => { + describe('offEvent', function () { + it('should return when id provided is not valid for event', function () { var spyEventsOff = sinon.spy(events, 'off'); $$PREBID_GLOBAL$$.offEvent('bidWon', Function, 'testId'); assert.ok(spyEventsOff.notCalled); events.off.restore(); }); - it('should call events.off with valid parameters', () => { + it('should call events.off with valid parameters', function () { var spyEventsOff = sinon.spy(events, 'off'); $$PREBID_GLOBAL$$.offEvent('bidWon', Function); assert.ok(spyEventsOff.calledWith('bidWon', Function)); @@ -1552,24 +1990,24 @@ describe('Unit: Prebid Module', function () { }); }); - describe('emit', () => { - it('should be able to emit event without arguments', () => { + describe('emit', function () { + it('should be able to emit event without arguments', function () { var spyEventsEmit = sinon.spy(events, 'emit'); - events.emit(CONSTANTS.EVENTS.AUCTION_END); - assert.ok(spyEventsEmit.calledWith('auctionEnd')); + events.emit(CONSTANTS.EVENTS.REQUEST_BIDS); + assert.ok(spyEventsEmit.calledWith('requestBids')); events.emit.restore(); }); }); - describe('registerBidAdapter', () => { - it('should register bidAdaptor with adaptermanager', () => { - var registerBidAdapterSpy = sinon.spy(adaptermanager, 'registerBidAdapter'); + describe('registerBidAdapter', function () { + it('should register bidAdaptor with adapterManager', function () { + var registerBidAdapterSpy = sinon.spy(adapterManager, 'registerBidAdapter'); $$PREBID_GLOBAL$$.registerBidAdapter(Function, 'biddercode'); - assert.ok(registerBidAdapterSpy.called, 'called adaptermanager.registerBidAdapter'); - adaptermanager.registerBidAdapter.restore(); + assert.ok(registerBidAdapterSpy.called, 'called adapterManager.registerBidAdapter'); + adapterManager.registerBidAdapter.restore(); }); - it('should catch thrown errors', () => { + it('should catch thrown errors', function () { var spyLogError = sinon.spy(utils, 'logError'); var errorObject = { message: 'bidderAdaptor error' }; var bidderAdaptor = sinon.stub().throws(errorObject); @@ -1582,8 +2020,8 @@ describe('Unit: Prebid Module', function () { }); }); - describe('createBid', () => { - it('should return a bid object', () => { + describe('createBid', function () { + it('should return a bid object', function () { const statusCode = 1; const bid = $$PREBID_GLOBAL$$.createBid(statusCode); assert.isObject(bid, 'bid is an object'); @@ -1595,31 +2033,29 @@ describe('Unit: Prebid Module', function () { }); }); - describe('loadScript', () => { - it('should call adloader.loadScript', () => { - const loadScriptSpy = sinon.spy(adloader, 'loadScript'); + describe('loadScript', function () { + it('should call adloader.loadScript', function () { const tagSrc = ''; const callback = Function; const useCache = false; $$PREBID_GLOBAL$$.loadScript(tagSrc, callback, useCache); - assert.ok(loadScriptSpy.calledWith(tagSrc, callback, useCache), 'called adloader.loadScript'); - adloader.loadScript.restore(); + assert.ok(adloader.loadScriptStub.calledWith(tagSrc, callback, useCache), 'called adloader.loadScript'); }); }); - describe('aliasBidder', () => { - it('should call adaptermanager.aliasBidder', () => { - const aliasBidAdapterSpy = sinon.spy(adaptermanager, 'aliasBidAdapter'); + describe('aliasBidder', function () { + it('should call adapterManager.aliasBidder', function () { + const aliasBidAdapterSpy = sinon.spy(adapterManager, 'aliasBidAdapter'); const bidderCode = 'testcode'; const alias = 'testalias'; $$PREBID_GLOBAL$$.aliasBidder(bidderCode, alias); - assert.ok(aliasBidAdapterSpy.calledWith(bidderCode, alias), 'called adaptermanager.aliasBidAdapterSpy'); - adaptermanager.aliasBidAdapter.restore(); + assert.ok(aliasBidAdapterSpy.calledWith(bidderCode, alias), 'called adapterManager.aliasBidAdapterSpy'); + adapterManager.aliasBidAdapter(); }); - it('should log error when not passed correct arguments', () => { + it('should log error when not passed correct arguments', function () { const logErrorSpy = sinon.spy(utils, 'logError'); const error = 'bidderCode and alias must be passed as arguments'; @@ -1629,8 +2065,8 @@ describe('Unit: Prebid Module', function () { }); }); - describe('setPriceGranularity', () => { - it('should log error when not passed granularity', () => { + describe('setPriceGranularity', function () { + it('should log error when not passed granularity', function () { const logErrorSpy = sinon.spy(utils, 'logError'); const error = 'Prebid Error: no value passed to `setPriceGranularity()`'; @@ -1639,7 +2075,7 @@ describe('Unit: Prebid Module', function () { utils.logError.restore(); }); - it('should log error when not passed a valid config object', () => { + it('should log error when not passed a valid config object', function () { const logErrorSpy = sinon.spy(utils, 'logError'); const error = 'Invalid custom price value passed to `setPriceGranularity()`'; const badConfig = { @@ -1662,7 +2098,7 @@ describe('Unit: Prebid Module', function () { utils.logError.restore(); }); - it('should set customPriceBucket with custom config buckets', () => { + it('should set customPriceBucket with custom config buckets', function () { let customPriceBucket = configObj.getConfig('customPriceBucket'); const goodConfig = { 'buckets': [{ @@ -1681,21 +2117,21 @@ describe('Unit: Prebid Module', function () { }); }); - describe('emit event', () => { + describe('emit event', function () { let auctionManagerStub; - beforeEach(() => { + beforeEach(function () { auctionManagerStub = sinon.stub(auctionManager, 'createAuction').callsFake(function() { return auction; }); }); - afterEach(() => { + afterEach(function () { auctionManager.createAuction.restore(); }); }); - describe('removeAdUnit', () => { - it('should remove given adUnit in adUnits array', () => { + describe('removeAdUnit', function () { + it('should remove given adUnit in adUnits array', function () { const adUnit1 = { code: 'adUnit1', bids: [{ @@ -1721,18 +2157,77 @@ describe('Unit: Prebid Module', function () { $$PREBID_GLOBAL$$.removeAdUnit('adUnit1'); assert.deepEqual($$PREBID_GLOBAL$$.adUnits, [adUnit2]); }); + it('should remove all adUnits in adUnits array if no adUnits are given', function () { + const adUnit1 = { + code: 'adUnit1', + bids: [{ + bidder: 'appnexus', + params: { placementId: '123' } + }] + }; + const adUnit2 = { + code: 'adUnit2', + bids: [{ + bidder: 'rubicon', + params: { + accountId: '1234', + siteId: '1234', + zoneId: '1234' + } + }] + }; + const adUnits = [adUnit1, adUnit2]; + $$PREBID_GLOBAL$$.adUnits = adUnits; + $$PREBID_GLOBAL$$.removeAdUnit(); + assert.deepEqual($$PREBID_GLOBAL$$.adUnits, []); + }); + it('should remove adUnits which match addUnitCodes in adUnit array argument', function () { + const adUnit1 = { + code: 'adUnit1', + bids: [{ + bidder: 'appnexus', + params: { placementId: '123' } + }] + }; + const adUnit2 = { + code: 'adUnit2', + bids: [{ + bidder: 'rubicon', + params: { + accountId: '1234', + siteId: '1234', + zoneId: '1234' + } + }] + }; + const adUnit3 = { + code: 'adUnit3', + bids: [{ + bidder: 'rubicon3', + params: { + accountId: '12345', + siteId: '12345', + zoneId: '12345' + } + }] + }; + const adUnits = [adUnit1, adUnit2, adUnit3]; + $$PREBID_GLOBAL$$.adUnits = adUnits; + $$PREBID_GLOBAL$$.removeAdUnit([adUnit1.code, adUnit2.code]); + assert.deepEqual($$PREBID_GLOBAL$$.adUnits, [adUnit3]); + }); }); - describe('getDealTargeting', () => { - beforeEach(() => { + describe('getDealTargeting', function () { + beforeEach(function () { resetAuction(); }); - afterEach(() => { + afterEach(function () { resetAuction(); }); - it('should truncate deal keys', () => { + it('should truncate deal keys', function () { $$PREBID_GLOBAL$$._bidsReceived = [ { 'bidderCode': 'appnexusDummyName', @@ -1757,12 +2252,12 @@ describe('Unit: Prebid Module', function () { 'alwaysUseBid': true, 'auctionId': 123456, 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '233bcbee889d46d', - 'hb_pb': '10.00', - 'hb_size': '300x250', 'foobar': '300x250', - 'hb_deal_appnexusDummyName': '1234' + [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus', + [CONSTANTS.TARGETING_KEYS.AD_ID]: '233bcbee889d46d', + [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', + [CONSTANTS.TARGETING_KEYS.SIZE]: '300x250', + [CONSTANTS.TARGETING_KEYS.DEAL + '_appnexusDummyName']: '1234' } } ]; @@ -1782,18 +2277,18 @@ describe('Unit: Prebid Module', function () { // expect(highestCpmBids[1]).to.deep.equal(auctionManager.getBidsReceived()[2]); // }); - it('returns an array containing the highest bid object for the given adUnitCode', () => { + it('returns an array containing the highest bid object for the given adUnitCode', function () { const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids('/19968336/header-bid-tag-0'); expect(highestCpmBids.length).to.equal(1); expect(highestCpmBids[0]).to.deep.equal(auctionManager.getBidsReceived()[1]); }); - it('returns an empty array when the given adUnit is not found', () => { + it('returns an empty array when the given adUnit is not found', function () { const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids('/stallone'); expect(highestCpmBids.length).to.equal(0); }); - it('returns an empty array when the given adUnit has no bids', () => { + it('returns an empty array when the given adUnit has no bids', function () { let _bidsReceived = getBidResponses()[0]; _bidsReceived.cpm = 0; auction.getBidsReceived = function() { return _bidsReceived }; @@ -1804,15 +2299,82 @@ describe('Unit: Prebid Module', function () { }); }); - describe('setTargetingForAst', () => { + describe('markWinningBidAsUsed', function () { + it('marks the bid object as used for the given adUnitCode/adId combination', function () { + // make sure the auction has "state" and does not reload the fixtures + const adUnitCode = '/19968336/header-bid-tag-0'; + const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); + auction.getBidsReceived = function() { return bidsReceived.bids }; + + // mark the bid and verify the state has changed to RENDERED + const winningBid = targeting.getWinningBids(adUnitCode)[0]; + $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode, adId: winningBid.adId }); + const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, + bid => bid.adId === winningBid.adId); + + expect(markedBid.status).to.equal(CONSTANTS.BID_STATUS.RENDERED); + resetAuction(); + }); + + it('try and mark the bid object, but fail because we supplied the wrong adId', function () { + const adUnitCode = '/19968336/header-bid-tag-0'; + const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); + auction.getBidsReceived = function() { return bidsReceived.bids }; + + const winningBid = targeting.getWinningBids(adUnitCode)[0]; + $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode, adId: 'miss' }); + const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, + bid => bid.adId === winningBid.adId); + + expect(markedBid.status).to.not.equal(CONSTANTS.BID_STATUS.RENDERED); + resetAuction(); + }); + + it('marks the winning bid object as used for the given adUnitCode', function () { + // make sure the auction has "state" and does not reload the fixtures + const adUnitCode = '/19968336/header-bid-tag-0'; + const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); + auction.getBidsReceived = function() { return bidsReceived.bids }; + + // mark the bid and verify the state has changed to RENDERED + const winningBid = targeting.getWinningBids(adUnitCode)[0]; + $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode }); + const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, + bid => bid.adId === winningBid.adId); + + expect(markedBid.status).to.equal(CONSTANTS.BID_STATUS.RENDERED); + resetAuction(); + }); + + it('marks a bid object as used for the given adId', function () { + // make sure the auction has "state" and does not reload the fixtures + const adUnitCode = '/19968336/header-bid-tag-0'; + const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); + auction.getBidsReceived = function() { return bidsReceived.bids }; + + // mark the bid and verify the state has changed to RENDERED + const winningBid = targeting.getWinningBids(adUnitCode)[0]; + $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adId: winningBid.adId }); + const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids, + bid => bid.adId === winningBid.adId); + + expect(markedBid.status).to.equal(CONSTANTS.BID_STATUS.RENDERED); + resetAuction(); + }); + }); + + describe('setTargetingForAst', function () { let targeting; let auctionManagerInstance; - beforeEach(() => { + beforeEach(function () { resetAuction(); auctionManagerInstance = newAuctionManager(); sinon.stub(auctionManagerInstance, 'getBidsReceived').callsFake(function() { - return [getBidResponses()[1]]; + let bidResponse = getBidResponses()[1]; + // add a pt0 value for special case. + bidResponse.adserverTargeting.pt0 = 'someVal'; + return [bidResponse]; }); sinon.stub(auctionManagerInstance, 'getAdUnitCodes').callsFake(function() { return ['/19968336/header-bid-tag-0']; @@ -1820,31 +2382,57 @@ describe('Unit: Prebid Module', function () { targeting = newTargeting(auctionManagerInstance); }); - afterEach(() => { + afterEach(function () { auctionManagerInstance.getBidsReceived.restore(); auctionManagerInstance.getAdUnitCodes.restore(); resetAuction(); }); - it('should set targeting for appnexus apntag object', () => { + it('should set targeting for appnexus apntag object', function () { const bids = auctionManagerInstance.getBidsReceived(); const adUnitCode = '/19968336/header-bid-tag-0'; var expectedAdserverTargeting = bids[0].adserverTargeting; var newAdserverTargeting = {}; + let regex = /pt[0-9]/; + for (var key in expectedAdserverTargeting) { - newAdserverTargeting[key.toUpperCase()] = expectedAdserverTargeting[key]; + if (key.search(regex) < 0) { + newAdserverTargeting[key.toUpperCase()] = expectedAdserverTargeting[key]; + } else { + newAdserverTargeting[key] = expectedAdserverTargeting[key]; + } } + targeting.setTargetingForAst(); + expect(newAdserverTargeting).to.deep.equal(window.apntag.tags[adUnitCode].keywords); + }); + it('should reset targeting for appnexus apntag object', function () { + const bids = auctionManagerInstance.getBidsReceived(); + const adUnitCode = '/19968336/header-bid-tag-0'; + + var expectedAdserverTargeting = bids[0].adserverTargeting; + var newAdserverTargeting = {}; + let regex = /pt[0-9]/; + + for (var key in expectedAdserverTargeting) { + if (key.search(regex) < 0) { + newAdserverTargeting[key.toUpperCase()] = expectedAdserverTargeting[key]; + } else { + newAdserverTargeting[key] = expectedAdserverTargeting[key]; + } + } targeting.setTargetingForAst(); expect(newAdserverTargeting).to.deep.equal(window.apntag.tags[adUnitCode].keywords); + targeting.resetPresetTargetingAST(); + expect(window.apntag.tags[adUnitCode].keywords).to.deep.equal({}); }); - it('should not find hb_adid key in lowercase for all bidders', () => { + it('should not find ' + CONSTANTS.TARGETING_KEYS.AD_ID + ' key in lowercase for all bidders', function() { const adUnitCode = '/19968336/header-bid-tag-0'; $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: true }); targeting.setTargetingForAst(); - const keywords = Object.keys(window.apntag.tags[adUnitCode].keywords).filter(keyword => (keyword.substring(0, 'hb_adid'.length) === 'hb_adid')); + const keywords = Object.keys(window.apntag.tags[adUnitCode].keywords).filter(keyword => (keyword.substring(0, CONSTANTS.TARGETING_KEYS.AD_ID.length) === CONSTANTS.TARGETING_KEYS.AD_ID)); expect(keywords.length).to.equal(0); }); }); @@ -1883,22 +2471,22 @@ describe('Unit: Prebid Module', function () { }); }); - describe('getAllPrebidWinningBids', () => { + describe('getAllPrebidWinningBids', function () { let auctionManagerStub; - beforeEach(() => { + beforeEach(function () { auctionManagerStub = sinon.stub(auctionManager, 'getBidsReceived'); }); - afterEach(() => { + afterEach(function () { auctionManagerStub.restore(); }); - it('should return prebid auction winning bids', () => { + it('should return prebid auction winning bids', function () { let bidsReceived = [ - createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', status: 'targetingSet'}), - createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-2'}), - createBidReceived({bidder: 'appnexus', cpm: 6, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-3'}), - createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-1', adId: 'adid-4'}), + createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', status: 'targetingSet', requestId: 'reqid-1'}), + createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-2', requestId: 'reqid-2'}), + createBidReceived({bidder: 'appnexus', cpm: 6, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-3', requestId: 'reqid-3'}), + createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-1', adId: 'adid-4', requestId: 'reqid-4'}), ]; auctionManagerStub.returns(bidsReceived) let bids = $$PREBID_GLOBAL$$.getAllPrebidWinningBids(); diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js new file mode 100644 index 00000000000..f0f26bf5653 --- /dev/null +++ b/test/spec/unit/secureCreatives_spec.js @@ -0,0 +1,45 @@ +import { + _sendAdToCreative +} from '../../../src/secureCreatives'; +import { expect } from 'chai'; +import * as utils from 'src/utils'; + +describe('secureCreatives', () => { + describe('_sendAdToCreative', () => { + beforeEach(function () { + sinon.stub(utils, 'logError'); + sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + utils.logError.restore(); + utils.logWarn.restore(); + }); + it('should macro replace ${AUCTION_PRICE} with the winning bid for ad and adUrl', () => { + const oldVal = window.googletag; + const oldapntag = window.apntag; + window.apntag = null + window.googletag = null; + const mockAdObject = { + adId: 'someAdId', + ad: '', + adUrl: 'http://creative.prebid.org/${AUCTION_PRICE}', + width: 300, + height: 250, + renderer: null, + cpm: '1.00', + adUnitCode: 'some_dom_id' + }; + const remoteDomain = '*'; + const source = { + postMessage: sinon.stub() + }; + + _sendAdToCreative(mockAdObject, remoteDomain, source); + expect(JSON.parse(source.postMessage.args[0][0]).ad).to.equal(''); + expect(JSON.parse(source.postMessage.args[0][0]).adUrl).to.equal('http://creative.prebid.org/1.00'); + window.googletag = oldVal; + window.apntag = oldapntag; + }); + }); +}); diff --git a/test/spec/url_spec.js b/test/spec/url_spec.js index cfa1b0c80b4..90047273043 100644 --- a/test/spec/url_spec.js +++ b/test/spec/url_spec.js @@ -1,31 +1,31 @@ import {format, parse} from '../../src/url'; import { expect } from 'chai'; -describe('helpers.url', () => { - describe('parse()', () => { +describe('helpers.url', function () { + describe('parse()', function () { let parsed; - beforeEach(() => { + beforeEach(function () { parsed = parse('http://example.com:3000/pathname/?search=test&foo=bar&bar=foo%26foo%3Dxxx#hash'); }); - it('extracts the protocol', () => { + it('extracts the protocol', function () { expect(parsed).to.have.property('protocol', 'http'); }); - it('extracts the hostname', () => { + it('extracts the hostname', function () { expect(parsed).to.have.property('hostname', 'example.com'); }); - it('extracts the port', () => { + it('extracts the port', function () { expect(parsed).to.have.property('port', 3000); }); - it('extracts the pathname', () => { + it('extracts the pathname', function () { expect(parsed).to.have.property('pathname', '/pathname/'); }); - it('extracts the search query', () => { + it('extracts the search query', function () { expect(parsed).to.have.property('search'); expect(parsed.search).to.eql({ foo: 'xxx', @@ -34,23 +34,23 @@ describe('helpers.url', () => { }); }); - it('extracts the hash', () => { + it('extracts the hash', function () { expect(parsed).to.have.property('hash', 'hash'); }); - it('extracts the host', () => { + it('extracts the host', function () { expect(parsed).to.have.property('host', 'example.com:3000'); }); }); - describe('parse(url, {noDecodeWholeURL: true})', () => { + describe('parse(url, {noDecodeWholeURL: true})', function () { let parsed; - beforeEach(() => { + beforeEach(function () { parsed = parse('http://example.com:3000/pathname/?search=test&foo=bar&bar=foo%26foo%3Dxxx#hash', {noDecodeWholeURL: true}); }); - it('extracts the search query', () => { + it('extracts the search query', function () { expect(parsed).to.have.property('search'); expect(parsed.search).to.eql({ foo: 'bar', @@ -60,8 +60,8 @@ describe('helpers.url', () => { }); }); - describe('format()', () => { - it('formats an object in to a URL', () => { + describe('format()', function () { + it('formats an object in to a URL', function () { expect(format({ protocol: 'http', hostname: 'example.com', @@ -72,21 +72,21 @@ describe('helpers.url', () => { })).to.equal('http://example.com:3000/pathname/?foo=bar&search=test&bar=foo%26foo%3Dxxx#hash'); }); - it('will use defaults for missing properties', () => { + it('will use defaults for missing properties', function () { expect(format({ hostname: 'example.com' })).to.equal('http://example.com'); }); }); - describe('parse(url, {decodeSearchAsString: true})', () => { + describe('parse(url, {decodeSearchAsString: true})', function () { let parsed; - beforeEach(() => { + beforeEach(function () { parsed = parse('http://example.com:3000/pathname/?search=test&foo=bar&bar=foo%26foo%3Dxxx#hash', {decodeSearchAsString: true}); }); - it('extracts the search query', () => { + it('extracts the search query', function () { expect(parsed).to.have.property('search'); expect(parsed.search).to.equal('?search=test&foo=bar&bar=foo&foo=xxx'); }); diff --git a/test/spec/userSync_spec.js b/test/spec/userSync_spec.js index 9a72629c71a..f55fe13c528 100644 --- a/test/spec/userSync_spec.js +++ b/test/spec/userSync_spec.js @@ -4,7 +4,7 @@ import { config } from 'src/config'; const utils = require('../../src/utils'); let { newUserSync } = require('../../src/userSync'); -describe('user sync', () => { +describe('user sync', function () { let triggerPixelStub; let logWarnStub; let timeoutStub; @@ -25,15 +25,15 @@ describe('user sync', () => { }) } let clock; - before(() => { + before(function () { clock = sinon.useFakeTimers(); }); - after(() => { + after(function () { clock.restore(); }); - beforeEach(() => { + beforeEach(function () { triggerPixelStub = sinon.stub(utils, 'triggerPixel'); logWarnStub = sinon.stub(utils, 'logWarn'); shuffleStub = sinon.stub(utils, 'shuffle').callsFake((array) => array.reverse()); @@ -41,7 +41,7 @@ describe('user sync', () => { insertUserSyncIframeStub = sinon.stub(utils, 'insertUserSyncIframe'); }); - afterEach(() => { + afterEach(function () { triggerPixelStub.restore(); logWarnStub.restore(); shuffleStub.restore(); @@ -49,7 +49,7 @@ describe('user sync', () => { insertUserSyncIframeStub.restore(); }); - it('should register and fire a pixel URL', () => { + it('should register and fire a pixel URL', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.syncUsers(); @@ -57,13 +57,13 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); }); - it('should clear queue after sync', () => { + it('should clear queue after sync', function () { const userSync = newTestUserSync(); userSync.syncUsers(); expect(triggerPixelStub.callCount).to.equal(0); }); - it('should delay firing a pixel by the expected amount', () => { + it('should delay firing a pixel by the expected amount', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'testBidder', 'http://example.com'); // This implicitly tests cookie and browser support @@ -72,7 +72,7 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(0)).to.not.be.null; }); - it('should register and fires multiple pixel URLs', () => { + it('should register and fires multiple pixel URLs', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'testBidder', 'http://example.com/1'); userSync.registerSync('image', 'testBidder', 'http://example.com/2'); @@ -84,45 +84,48 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(2)).to.be.null; }); - it('should not register pixel URL since it is not supported', () => { + it('should not register pixel URL since it is not supported', function () { const userSync = newTestUserSync({pixelEnabled: false}); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.be.null; }); - it('should register and load an iframe', () => { + it('should register and load an iframe', function () { const userSync = newTestUserSync({iframeEnabled: true}); userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync.syncUsers(); expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); }); - it('should only trigger syncs once per page', () => { + it('should only trigger syncs once per page per bidder', function () { const userSync = newTestUserSync({pixelEnabled: true}); userSync.registerSync('image', 'testBidder', 'http://example.com/1'); userSync.syncUsers(); userSync.registerSync('image', 'testBidder', 'http://example.com/2'); + userSync.registerSync('image', 'testBidder2', 'http://example.com/3'); userSync.syncUsers(); + expect(triggerPixelStub.callCount).to.equal(2); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); - expect(triggerPixelStub.getCall(1)).to.be.null; + expect(triggerPixelStub.getCall(1)).to.not.be.null; + expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.equal('http://example.com/3'); }); - it('should not fire syncs if cookies are not supported', () => { + it('should not fire syncs if cookies are not supported', function () { const userSync = newTestUserSync({pixelEnabled: true}, true); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.be.null; }); - it('should prevent registering invalid type', () => { + it('should prevent registering invalid type', function () { const userSync = newTestUserSync(); userSync.registerSync('invalid', 'testBidder', 'http://example.com'); expect(logWarnStub.getCall(0).args[0]).to.exist; }); - it('should expose the syncUsers method for the publisher to manually trigger syncs', () => { + it('should expose the syncUsers method for the publisher to manually trigger syncs', function () { // triggerUserSyncs should do nothing by default let userSync = newTestUserSync(); let syncUsersSpy = sinon.spy(userSync, 'syncUsers'); @@ -135,7 +138,7 @@ describe('user sync', () => { expect(syncUsersSpy.called).to.be.true; }); - it('should limit the sync per bidder', () => { + it('should limit the number of syncs per bidder', function () { const userSync = newTestUserSync({syncsPerBidder: 2}); userSync.registerSync('image', 'testBidder', 'http://example.com/1'); userSync.registerSync('image', 'testBidder', 'http://example.com/2'); @@ -148,7 +151,21 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(2)).to.be.null; }); - it('should balance out bidder requests', () => { + it('should not limit the number of syncs per bidder when set to 0', function() { + const userSync = newTestUserSync({syncsPerBidder: 0}); + userSync.registerSync('image', 'testBidder', 'http://example.com/1'); + userSync.registerSync('image', 'testBidder', 'http://example.com/2'); + userSync.registerSync('image', 'testBidder', 'http://example.com/3'); + userSync.syncUsers(); + expect(triggerPixelStub.getCall(0)).to.not.be.null; + expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2|3]/); + expect(triggerPixelStub.getCall(1)).to.not.be.null; + expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2|3]/); + expect(triggerPixelStub.getCall(2)).to.not.be.null; + expect(triggerPixelStub.getCall(2).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2|3]/); + }); + + it('should balance out bidder requests', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync.registerSync('image', 'atestBidder', 'http://example.com/3'); @@ -164,7 +181,7 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(3)).to.be.null; }); - it('should disable user sync', () => { + it('should disable user sync', function () { const userSync = newTestUserSync({syncEnabled: false}); userSync.registerSync('pixel', 'testBidder', 'http://example.com'); expect(logWarnStub.getCall(0).args[0]).to.exist; @@ -172,7 +189,7 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(0)).to.be.null; }); - it('should only sync enabled bidders', () => { + it('should only sync enabled bidders', function () { const userSync = newTestUserSync({enabledBidders: ['testBidderA']}); userSync.registerSync('image', 'testBidderA', 'http://example.com/1'); userSync.registerSync('image', 'testBidderB', 'http://example.com/2'); @@ -182,7 +199,7 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(1)).to.be.null; }); - it('should register config set after instantiation', () => { + it('should register config set after instantiation', function () { // start with userSync off const userSync = newTestUserSync({syncEnabled: false}); // turn it on with setConfig() @@ -192,4 +209,254 @@ describe('user sync', () => { expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); }); + + it('should register both image and iframe pixels with filterSettings.all config', function () { + const userSync = newTestUserSync({ + filterSettings: { + all: { + bidders: ['atestBidder', 'testBidder'], + filter: 'include' + }, + } + }); + userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync.syncUsers(); + expect(triggerPixelStub.getCall(0)).to.not.be.null; + expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); + expect(insertUserSyncIframeStub.getCall(0)).to.not.be.null; + expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); + }); + + it('should register iframe and not register image pixels based on filterSettings config', function () { + const userSync = newTestUserSync({ + filterSettings: { + image: { + bidders: '*', + filter: 'exclude' + }, + iframe: { + bidders: ['testBidder'] + } + } + }); + userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync.syncUsers(); + expect(triggerPixelStub.getCall(0)).to.be.null; + expect(insertUserSyncIframeStub.getCall(0)).to.not.be.null; + expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); + }); + + it('should throw a warning and default to basic resgistration rules when filterSettings config is invalid', function () { + // invalid config - passed invalid filter option + const userSync1 = newTestUserSync({ + filterSettings: { + iframe: { + bidders: ['testBidder'], + filter: 'includes' + } + } + }); + userSync1.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync1.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync1.syncUsers(); + expect(logWarnStub.getCall(0).args[0]).to.exist; + expect(triggerPixelStub.getCall(0)).to.not.be.null; + expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); + expect(insertUserSyncIframeStub.getCall(0)).to.be.null; + + // invalid config - bidders is not an array of strings + const userSync2 = newTestUserSync({ + filterSettings: { + iframe: { + bidders: ['testBidder', 0], + filter: 'include' + } + } + }); + userSync2.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync2.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync2.syncUsers(); + expect(logWarnStub.getCall(1).args[0]).to.exist; + expect(triggerPixelStub.getCall(1)).to.not.be.null; + expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.equal('http://example.com/1'); + expect(insertUserSyncIframeStub.getCall(0)).to.be.null; + + // invalid config - bidders list includes wildcard + const userSync3 = newTestUserSync({ + filterSettings: { + iframe: { + bidders: ['testBidder', '*'], + filter: 'include' + } + } + }); + userSync3.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync3.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync3.syncUsers(); + expect(logWarnStub.getCall(2).args[0]).to.exist; + expect(triggerPixelStub.getCall(2)).to.not.be.null; + expect(triggerPixelStub.getCall(2).args[0]).to.exist.and.to.equal('http://example.com/1'); + expect(insertUserSyncIframeStub.getCall(0)).to.be.null; + + // invalid config - incorrect wildcard + const userSync4 = newTestUserSync({ + filterSettings: { + iframe: { + bidders: '***', + filter: 'include' + } + } + }); + userSync4.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync4.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync4.syncUsers(); + expect(logWarnStub.getCall(3).args[0]).to.exist; + expect(triggerPixelStub.getCall(3)).to.not.be.null; + expect(triggerPixelStub.getCall(3).args[0]).to.exist.and.to.equal('http://example.com/1'); + expect(insertUserSyncIframeStub.getCall(0)).to.be.null; + + // invalid config - missing bidders field + const userSync5 = newTestUserSync({ + filterSettings: { + iframe: { + filter: 'include' + } + } + }); + userSync5.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync5.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync5.syncUsers(); + expect(logWarnStub.getCall(4).args[0]).to.exist; + expect(triggerPixelStub.getCall(4)).to.not.be.null; + expect(triggerPixelStub.getCall(4).args[0]).to.exist.and.to.equal('http://example.com/1'); + expect(insertUserSyncIframeStub.getCall(0)).to.be.null; + }); + + it('should overwrite logic of deprecated fields when filterSettings is defined', function () { + const userSync = newTestUserSync({ + pixelsEnabled: false, + iframeEnabled: true, + enabledBidders: ['ctestBidder'], + filterSettings: { + image: { + bidders: '*', + filter: 'include' + }, + iframe: { + bidders: ['testBidder'], + filter: 'exclude' + } + } + }); + userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); + userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); + userSync.syncUsers(); + expect(logWarnStub.getCall(0).args[0]).to.exist; + expect(triggerPixelStub.getCall(0)).to.not.be.null; + expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); + expect(insertUserSyncIframeStub.getCall(0)).to.be.null; + }); + + describe('publicAPI', function () { + describe('canBidderRegisterSync', function() { + describe('with filterSettings', function() { + it('should return false if filter settings does not allow it', function () { + const userSync = newUserSync({ + config: { + filterSettings: { + image: { + bidders: '*', + filter: 'include' + }, + iframe: { + bidders: ['testBidder'], + filter: 'include' + } + } + } + }); + expect(userSync.canBidderRegisterSync('iframe', 'otherTestBidder')).to.equal(false); + }); + it('should return true if filter settings does allow it', function () { + const userSync = newUserSync({ + config: { + filterSettings: { + image: { + bidders: '*', + filter: 'include' + }, + iframe: { + bidders: ['testBidder'], + filter: 'include' + } + } + } + }); + expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(true); + }); + }); + describe('almost deprecated - without filterSettings', function() { + describe('enabledBidders contains testBidder', function() { + it('should return false if type is iframe and iframeEnabled is false', function () { + const userSync = newUserSync({ + config: { + pixelEnabled: true, + iframeEnabled: false, + enabledBidders: ['testBidder'], + } + }); + expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(false); + }); + + it('should return true if type is iframe and iframeEnabled is true', function () { + const userSync = newUserSync({ + config: { + pixelEnabled: true, + iframeEnabled: true, + enabledBidders: ['testBidder'], + } + }); + expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(true); + }); + + it('should return false if type is image and pixelEnabled is false', function () { + const userSync = newUserSync({ + config: { + pixelEnabled: false, + iframeEnabled: true, + enabledBidders: ['testBidder'], + } + }); + expect(userSync.canBidderRegisterSync('image', 'testBidder')).to.equal(false); + }); + + it('should return true if type is image and pixelEnabled is true', function () { + const userSync = newUserSync({ + config: { + pixelEnabled: true, + iframeEnabled: true, + enabledBidders: ['testBidder'], + } + }); + expect(userSync.canBidderRegisterSync('image', 'testBidder')).to.equal(true); + }); + }); + + describe('enabledBidders does not container testBidder', function() { + it('should return false since testBidder is not in enabledBidders', function() { + const userSync = newUserSync({ + config: { + pixelEnabled: true, + iframeEnabled: true, + enabledBidders: ['otherTestBidder'], + } + }); + expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(false); + }); + }); + }); + }); + }); }); diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 6860605d343..5bee79d39c2 100755 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1,8 +1,9 @@ import { getAdServerTargeting } from 'test/fixtures/fixtures'; import { expect } from 'chai'; +import CONSTANTS from 'src/constants.json'; +import * as utils from 'src/utils'; var assert = require('assert'); -var utils = require('src/utils'); describe('Utils', function () { var obj_string = 's', @@ -101,7 +102,7 @@ describe('Utils', function () { var obj = getAdServerTargeting(); var output = utils.transformAdServerTargetingObj(obj[Object.keys(obj)[0]]); - var expected = 'foobar=0x0%2C300x250%2C300x600&hb_size=300x250&hb_pb=10.00&hb_adid=233bcbee889d46d&hb_bidder=appnexus&hb_size_triplelift=0x0&hb_pb_triplelift=10.00&hb_adid_triplelift=222bb26f9e8bd&hb_bidder_triplelift=triplelift&hb_size_appnexus=300x250&hb_pb_appnexus=10.00&hb_adid_appnexus=233bcbee889d46d&hb_bidder_appnexus=appnexus&hb_size_pagescience=300x250&hb_pb_pagescience=10.00&hb_adid_pagescience=25bedd4813632d7&hb_bidder_pagescienc=pagescience&hb_size_brightcom=300x250&hb_pb_brightcom=10.00&hb_adid_brightcom=26e0795ab963896&hb_bidder_brightcom=brightcom&hb_size_brealtime=300x250&hb_pb_brealtime=10.00&hb_adid_brealtime=275bd666f5a5a5d&hb_bidder_brealtime=brealtime&hb_size_pubmatic=300x250&hb_pb_pubmatic=10.00&hb_adid_pubmatic=28f4039c636b6a7&hb_bidder_pubmatic=pubmatic&hb_size_rubicon=300x600&hb_pb_rubicon=10.00&hb_adid_rubicon=29019e2ab586a5a&hb_bidder_rubicon=rubicon'; + var expected = 'foobar=0x0%2C300x250%2C300x600&' + CONSTANTS.TARGETING_KEYS.SIZE + '=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '=233bcbee889d46d&' + CONSTANTS.TARGETING_KEYS.BIDDER + '=appnexus&' + CONSTANTS.TARGETING_KEYS.SIZE + '_triplelift=0x0&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_triplelift=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_triplelift=222bb26f9e8bd&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_triplelift=triplelift&' + CONSTANTS.TARGETING_KEYS.SIZE + '_appnexus=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_appnexus=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_appnexus=233bcbee889d46d&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_appnexus=appnexus&' + CONSTANTS.TARGETING_KEYS.SIZE + '_pagescience=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_pagescience=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_pagescience=25bedd4813632d7&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_pagescienc=pagescience&' + CONSTANTS.TARGETING_KEYS.SIZE + '_brightcom=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_brightcom=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_brightcom=26e0795ab963896&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_brightcom=brightcom&' + CONSTANTS.TARGETING_KEYS.SIZE + '_brealtime=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_brealtime=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_brealtime=275bd666f5a5a5d&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_brealtime=brealtime&' + CONSTANTS.TARGETING_KEYS.SIZE + '_pubmatic=300x250&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_pubmatic=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_pubmatic=28f4039c636b6a7&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_pubmatic=pubmatic&' + CONSTANTS.TARGETING_KEYS.SIZE + '_rubicon=300x600&' + CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_rubicon=10.00&' + CONSTANTS.TARGETING_KEYS.AD_ID + '_rubicon=29019e2ab586a5a&' + CONSTANTS.TARGETING_KEYS.BIDDER + '_rubicon=rubicon'; assert.equal(output, expected); }); @@ -231,6 +232,56 @@ describe('Utils', function () { }); }); + describe('parseGPTSingleSizeArrayToRtbSize', function () { + it('should return size string with input single size array', function () { + var size = [300, 250]; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.deepEqual(output, {w: 300, h: 250}); + }); + + it('should return size string with input single size array', function () { + var size = ['300', '250']; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.deepEqual(output, {w: 300, h: 250}); + }); + + it('return undefined using string input', function () { + var size = '1'; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.equal(output, undefined); + }); + + it('return undefined using number input', function () { + var size = 1; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.equal(output, undefined); + }); + + it('return undefined using one length single array', function () { + var size = [300]; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.equal(output, undefined); + }); + + it('return undefined if the input is empty', function () { + var size = ''; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.equal(output, undefined); + }); + + it('return undefined if the input is not a number', function () { + var size = ['foo', 'bar']; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.equal(output, undefined); + }); + + it('return undefined if the input is not a number 2', function () { + var size = [300, 'foo']; + var output = utils.parseGPTSingleSizeArrayToRtbSize(size); + assert.equal(output, undefined); + }); + }); + describe('isA', function () { it('should return true with string object', function () { var output = utils.isA(obj_string, type_string); @@ -506,11 +557,11 @@ describe('Utils', function () { describe('getHighestCpm', function () { it('should pick the existing highest cpm', function () { - var previous = { + let previous = { cpm: 2, timeToRespond: 100 }; - var current = { + let current = { cpm: 1, timeToRespond: 100 }; @@ -518,11 +569,11 @@ describe('Utils', function () { }); it('should pick the new highest cpm', function () { - var previous = { + let previous = { cpm: 1, timeToRespond: 100 }; - var current = { + let current = { cpm: 2, timeToRespond: 100 }; @@ -530,16 +581,44 @@ describe('Utils', function () { }); it('should pick the fastest cpm in case of tie', function () { - var previous = { + let previous = { cpm: 1, timeToRespond: 100 }; - var current = { + let current = { cpm: 1, timeToRespond: 50 }; assert.equal(utils.getHighestCpm(previous, current), current); }); + + it('should pick the oldest in case of tie using responseTimeStamp', function () { + let previous = { + cpm: 1, + timeToRespond: 100, + responseTimestamp: 1000 + }; + let current = { + cpm: 1, + timeToRespond: 50, + responseTimestamp: 2000 + }; + assert.equal(utils.getOldestHighestCpmBid(previous, current), previous); + }); + + it('should pick the latest in case of tie using responseTimeStamp', function () { + let previous = { + cpm: 1, + timeToRespond: 100, + responseTimestamp: 1000 + }; + let current = { + cpm: 1, + timeToRespond: 50, + responseTimestamp: 2000 + }; + assert.equal(utils.getLatestHighestCpmBid(previous, current), current); + }); }); describe('polyfill test', function () { @@ -582,7 +661,7 @@ describe('Utils', function () { var value2 = utils.deepAccess(obj, 'test.first'); assert.equal(value2, 11); - var value3 = utils.deepAccess(obj, 1); + var value3 = utils.deepAccess(obj, '1'); assert.equal(value3, 2); }); @@ -597,6 +676,29 @@ describe('Utils', function () { }); }); + describe('deepSetValue', function() { + it('should set existing properties at various depths', function() { + const testObj = { + prop: 'value', + nestedObj: { + nestedProp: 'nestedValue' + } + }; + utils.deepSetValue(testObj, 'prop', 'newValue'); + assert.equal(testObj.prop, 'newValue'); + utils.deepSetValue(testObj, 'nestedObj.nestedProp', 'newNestedValue'); + assert.equal(testObj.nestedObj.nestedProp, 'newNestedValue'); + }); + + it('should create object levels between top and bottom of given path if they do not exist', function() { + const testObj = {}; + utils.deepSetValue(testObj, 'level1.level2', 'value'); + assert.notEqual(testObj.level1, undefined); + assert.notEqual(testObj.level1.level2, undefined); + assert.equal(testObj.level1.level2, 'value'); + }); + }); + describe('createContentToExecuteExtScriptInFriendlyFrame', function () { it('should return empty string if url is not passed', function () { var output = utils.createContentToExecuteExtScriptInFriendlyFrame(); @@ -611,8 +713,8 @@ describe('Utils', function () { }); }); - describe('getDefinedParams', () => { - it('builds an object consisting of defined params', () => { + describe('getDefinedParams', function () { + it('builds an object consisting of defined params', function () { const adUnit = { mediaType: 'video', comeWithMe: 'ifuwant2live', @@ -630,8 +732,8 @@ describe('Utils', function () { }); }); - describe('deepClone', () => { - it('deep copies objects', () => { + describe('deepClone', function () { + it('deep copies objects', function () { const adUnit = [{ code: 'swan', mediaTypes: {video: {context: 'outstream'}}, @@ -651,7 +753,7 @@ describe('Utils', function () { }); }); - describe('getUserConfiguredParams', () => { + describe('getUserConfiguredParams', function () { const adUnits = [{ code: 'adUnit1', bids: [{ @@ -664,7 +766,7 @@ describe('Utils', function () { }] }]; - it('should return params configured', () => { + it('should return params configured', function () { const output = utils.getUserConfiguredParams(adUnits, 'adUnit1', 'bidder1'); const expected = [{ key1: 'value1' @@ -672,38 +774,38 @@ describe('Utils', function () { assert.deepEqual(output, expected); }); - it('should return array containting empty object, if bidder present and no params are configured', () => { + it('should return array containting empty object, if bidder present and no params are configured', function () { const output = utils.getUserConfiguredParams(adUnits, 'adUnit1', 'bidder2'); const expected = [{}]; assert.deepEqual(output, expected); }); - it('should return empty array, if bidder is not present', () => { + it('should return empty array, if bidder is not present', function () { const output = utils.getUserConfiguredParams(adUnits, 'adUnit1', 'bidder3'); const expected = []; assert.deepEqual(output, expected); }); - it('should return empty array, if adUnit is not present', () => { + it('should return empty array, if adUnit is not present', function () { const output = utils.getUserConfiguredParams(adUnits, 'adUnit2', 'bidder3'); const expected = []; assert.deepEqual(output, expected); }); }); - describe('getTopWindowLocation', () => { + describe('getTopWindowLocation', function () { let sandbox; - beforeEach(() => { + beforeEach(function () { sandbox = sinon.sandbox.create(); }); - afterEach(() => { + afterEach(function () { sandbox.restore(); }); - it('returns window.location if not in iFrame', () => { - sandbox.stub(utils, 'getWindowLocation').returns({ + it('returns window.location if not in iFrame', function () { + sandbox.stub(utils.internal, 'getWindowLocation').returns({ href: 'https://www.google.com/', ancestorOrigins: {}, origin: 'https://www.google.com', @@ -716,10 +818,10 @@ describe('Utils', function () { hash: '' }); let windowSelfAndTopObject = { self: 'is same as top' }; - sandbox.stub(utils, 'getWindowSelf').returns( + sandbox.stub(utils.internal, 'getWindowSelf').returns( windowSelfAndTopObject ); - sandbox.stub(utils, 'getWindowTop').returns( + sandbox.stub(utils.internal, 'getWindowTop').returns( windowSelfAndTopObject ); var topWindowLocation = utils.getTopWindowLocation(); @@ -734,14 +836,14 @@ describe('Utils', function () { expect(topWindowLocation.host).to.equal('www.google.com'); }); - it('returns parsed dom string from ancestorOrigins if in iFrame & ancestorOrigins is populated', () => { - sandbox.stub(utils, 'getWindowSelf').returns( + it('returns parsed dom string from ancestorOrigins if in iFrame & ancestorOrigins is populated', function () { + sandbox.stub(utils.internal, 'getWindowSelf').returns( { self: 'is not same as top' } ); - sandbox.stub(utils, 'getWindowTop').returns( + sandbox.stub(utils.internal, 'getWindowTop').returns( { top: 'is not same as self' } ); - sandbox.stub(utils, 'getAncestorOrigins').returns('https://www.google.com/a/umich.edu/acs'); + sandbox.stub(utils.internal, 'getAncestorOrigins').returns('https://www.google.com/a/umich.edu/acs'); var topWindowLocation = utils.getTopWindowLocation(); expect(topWindowLocation).to.be.a('object'); expect(topWindowLocation.pathname).to.equal('/a/umich.edu/acs'); @@ -755,15 +857,15 @@ describe('Utils', function () { expect(topWindowLocation.host).to.be.oneOf(['www.google.com', 'www.google.com:443']); }); - it('returns parsed referrer string if in iFrame but no ancestorOrigins', () => { - sandbox.stub(utils, 'getWindowSelf').returns( + it('returns parsed referrer string if in iFrame but no ancestorOrigins', function () { + sandbox.stub(utils.internal, 'getWindowSelf').returns( { self: 'is not same as top' } ); - sandbox.stub(utils, 'getWindowTop').returns( + sandbox.stub(utils.internal, 'getWindowTop').returns( { top: 'is not same as self' } ); - sandbox.stub(utils, 'getAncestorOrigins').returns(null); - sandbox.stub(utils, 'getTopFrameReferrer').returns('https://www.example.com/'); + sandbox.stub(utils.internal, 'getAncestorOrigins').returns(null); + sandbox.stub(utils.internal, 'getTopFrameReferrer').returns('https://www.example.com/'); var topWindowLocation = utils.getTopWindowLocation(); expect(topWindowLocation).to.be.a('object'); expect(topWindowLocation.href).to.equal('https://www.example.com/'); @@ -778,8 +880,8 @@ describe('Utils', function () { }); }); - describe('convertCamelToUnderscore', () => { - it('returns converted string value using underscore syntax instead of camelCase', () => { + describe('convertCamelToUnderscore', function () { + it('returns converted string value using underscore syntax instead of camelCase', function () { let var1 = 'placementIdTest'; let test1 = utils.convertCamelToUnderscore(var1); expect(test1).to.equal('placement_id_test'); @@ -790,18 +892,18 @@ describe('Utils', function () { }); }); - describe('getAdUnitSizes', () => { - it('returns an empty response when adUnits is undefined', () => { + describe('getAdUnitSizes', function () { + it('returns an empty response when adUnits is undefined', function () { let sizes = utils.getAdUnitSizes(); expect(sizes).to.be.undefined; }); - it('returns an empty array when invalid data is present in adUnit object', () => { + it('returns an empty array when invalid data is present in adUnit object', function () { let sizes = utils.getAdUnitSizes({ sizes: 300 }); expect(sizes).to.deep.equal([]); }); - it('retuns an array of arrays when reading from adUnit.sizes', () => { + it('retuns an array of arrays when reading from adUnit.sizes', function () { let sizes = utils.getAdUnitSizes({ sizes: [300, 250] }); expect(sizes).to.deep.equal([[300, 250]]); @@ -809,7 +911,7 @@ describe('Utils', function () { expect(sizes).to.deep.equal([[300, 250], [300, 600]]); }); - it('returns an array of arrays when reading from adUnit.mediaTypes.banner.sizes', () => { + it('returns an array of arrays when reading from adUnit.mediaTypes.banner.sizes', function () { let sizes = utils.getAdUnitSizes({ mediaTypes: { banner: { sizes: [300, 250] } } }); expect(sizes).to.deep.equal([[300, 250]]); @@ -817,4 +919,94 @@ describe('Utils', function () { expect(sizes).to.deep.equal([[300, 250], [300, 600]]); }); }); + + describe('transformBidderParamKeywords', function () { + it('returns an array of objects when keyvalue is an array', function () { + let keywords = { + genre: ['rock', 'pop'] + }; + let result = utils.transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'genre', + value: ['rock', 'pop'] + }]); + }); + + it('returns an array of objects when keyvalue is a string', function () { + let keywords = { + genre: 'opera' + }; + let result = utils.transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'genre', + value: ['opera'] + }]); + }); + + it('returns an array of objects when keyvalue is a number', function () { + let keywords = { + age: 15 + }; + let result = utils.transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'age', + value: ['15'] + }]); + }); + + it('returns an array of objects when using multiple keys with values of differing types', function () { + let keywords = { + genre: 'classical', + mix: ['1', 2, '3', 4], + age: 10 + }; + let result = utils.transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'genre', + value: ['classical'] + }, { + key: 'mix', + value: ['1', '2', '3', '4'] + }, { + key: 'age', + value: ['10'] + }]); + }); + + it('returns an array of objects when the keyvalue uses an empty string', function() { + let keywords = { + test: [''], + test2: '' + }; + let result = utils.transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'test', + value: [''] + }, { + key: 'test2', + value: [''] + }]); + }); + + describe('insertElement', function () { + it('returns a node at the top of the target by default', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('body')[0]; + const inserted = utils.insertElement(toInsert, document, 'body'); + expect(inserted).to.equal(target.firstChild); + }); + it('returns a node at bottom of target if 4th argument is true', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('html')[0]; + const inserted = utils.insertElement(toInsert, document, 'html', true); + expect(inserted).to.equal(target.lastChild); + }); + it('returns a node at top of the head if no target is given', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('head')[0]; + const inserted = utils.insertElement(toInsert); + expect(inserted).to.equal(target.firstChild); + }); + }); + }); }); diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index b853da708fc..fc4924187e8 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -1,11 +1,11 @@ -import 'mocha'; +import 'mocha/mocha'; import chai from 'chai'; import { getCacheUrl, store } from 'src/videoCache'; import { config } from 'src/config'; const should = chai.should(); -describe('The video cache', () => { +describe('The video cache', function () { function assertError(callbackSpy) { callbackSpy.calledOnce.should.equal(true); callbackSpy.firstCall.args[0].should.be.an('error'); @@ -16,19 +16,21 @@ describe('The video cache', () => { should.not.exist(callbackSpy.firstCall.args[0]); } - describe('when the cache server is unreachable', () => { + describe('when the cache server is unreachable', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = (request) => requests.push(request); }); - afterEach(() => xhr.restore()); + afterEach(function () { + xhr.restore(); + }); - it('should execute the callback with an error when store() is called', () => { + it('should execute the callback with an error when store() is called', function () { const callback = sinon.spy(); store([ { vastUrl: 'my-mock-url.com' } ], callback); @@ -41,11 +43,11 @@ describe('The video cache', () => { }); }); - describe('when the cache server is available', () => { + describe('when the cache server is available', function () { let xhr; let requests; - beforeEach(() => { + beforeEach(function () { xhr = sinon.useFakeXMLHttpRequest(); requests = []; xhr.onCreate = (request) => requests.push(request); @@ -56,12 +58,12 @@ describe('The video cache', () => { }) }); - afterEach(() => { + afterEach(function () { xhr.restore(); config.resetConfig(); }); - it('should execute the callback with a successful result when store() is called', () => { + it('should execute the callback with a successful result when store() is called', function () { const uuid = 'c488b101-af3e-4a99-b538-00423e5a3371'; const callback = fakeServerCall( { vastUrl: 'my-mock-url.com' }, @@ -71,7 +73,7 @@ describe('The video cache', () => { callback.firstCall.args[1].should.deep.equal([{ uuid: uuid }]); }); - it('should execute the callback with an error if the cache server response has no responses property', () => { + it('should execute the callback with an error if the cache server response has no responses property', function () { const callback = fakeServerCall( { vastUrl: 'my-mock-url.com' }, '{"broken":[{"uuid":"c488b101-af3e-4a99-b538-00423e5a3371"}]}'); @@ -79,7 +81,7 @@ describe('The video cache', () => { callback.firstCall.args[1].should.deep.equal([]); }); - it('should execute the callback with an error if the cache server responds with malformed JSON', () => { + it('should execute the callback with an error if the cache server responds with malformed JSON', function () { const callback = fakeServerCall( { vastUrl: 'my-mock-url.com' }, 'Not JSON here'); @@ -87,7 +89,7 @@ describe('The video cache', () => { callback.firstCall.args[1].should.deep.equal([]); }); - it('should make the expected request when store() is called on an ad with a vastUrl', () => { + it('should make the expected request when store() is called on an ad with a vastUrl', function () { const expectedValue = ` @@ -98,10 +100,10 @@ describe('The video cache', () => { `; - assertRequestMade({ vastUrl: 'my-mock-url.com' }, expectedValue) + assertRequestMade({ vastUrl: 'my-mock-url.com', ttl: 25 }, expectedValue) }); - it('should make the expected request when store() is called on an ad with a vastUrl and a vastImpUrl', () => { + it('should make the expected request when store() is called on an ad with a vastUrl and a vastImpUrl', function () { const expectedValue = ` @@ -112,12 +114,49 @@ describe('The video cache', () => { `; - assertRequestMade({ vastUrl: 'my-mock-url.com', vastImpUrl: 'imptracker.com' }, expectedValue) + assertRequestMade({ vastUrl: 'my-mock-url.com', vastImpUrl: 'imptracker.com', ttl: 25 }, expectedValue) }); - it('should make the expected request when store() is called on an ad with vastXml', () => { + it('should make the expected request when store() is called on an ad with vastXml', function () { const vastXml = ''; - assertRequestMade({ vastXml: vastXml }, vastXml); + assertRequestMade({ vastXml: vastXml, ttl: 25 }, vastXml); + }); + + it('should make the expected request when store() is called while supplying a custom key param', function () { + const customKey1 = 'keyword_abc_123'; + const customKey2 = 'other_xyz_789'; + const vastXml1 = 'test1'; + const vastXml2 = 'test2'; + + const bids = [{ + vastXml: vastXml1, + ttl: 25, + customCacheKey: customKey1 + }, { + vastXml: vastXml2, + ttl: 25, + customCacheKey: customKey2 + }]; + + store(bids, function () { }); + const request = requests[0]; + request.method.should.equal('POST'); + request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache'); + request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8'); + let payload = { + puts: [{ + type: 'xml', + value: vastXml1, + ttlseconds: 25, + key: customKey1 + }, { + type: 'xml', + value: vastXml2, + ttlseconds: 25, + key: customKey2 + }] + }; + JSON.parse(request.requestBody).should.deep.equal(payload); }); function assertRequestMade(bid, expectedValue) { @@ -132,6 +171,7 @@ describe('The video cache', () => { puts: [{ type: 'xml', value: expectedValue, + ttlseconds: 25 }], }); } @@ -150,8 +190,8 @@ describe('The video cache', () => { }); }); -describe('The getCache function', () => { - beforeEach(() => { +describe('The getCache function', function () { + beforeEach(function () { config.setConfig({ cache: { url: 'https://prebid.adnxs.com/pbc/v1/cache' @@ -159,11 +199,11 @@ describe('The getCache function', () => { }) }); - afterEach(() => { + afterEach(function () { config.resetConfig(); }); - it('should return the expected URL', () => { + it('should return the expected URL', function () { const uuid = 'c488b101-af3e-4a99-b538-00423e5a3371'; const url = getCacheUrl(uuid); url.should.equal(`https://prebid.adnxs.com/pbc/v1/cache?uuid=${uuid}`); diff --git a/test/spec/video_spec.js b/test/spec/video_spec.js index 06cd653f444..dc6c11a7491 100644 --- a/test/spec/video_spec.js +++ b/test/spec/video_spec.js @@ -1,10 +1,11 @@ import { isValidVideoBid } from 'src/video'; -describe('video.js', () => { - it('validates valid instream bids', () => { +describe('video.js', function () { + it('validates valid instream bids', function () { const bid = { - adId: '123abc', - vastUrl: 'http://www.example.com/vastUrl' + adId: '456xyz', + vastUrl: 'http://www.example.com/vastUrl', + requestId: '123abc' }; const bidRequests = [{ bids: [{ @@ -19,9 +20,9 @@ describe('video.js', () => { expect(valid).to.equal(true); }); - it('catches invalid instream bids', () => { + it('catches invalid instream bids', function () { const bid = { - adId: '123abc' + requestId: '123abc' }; const bidRequests = [{ bids: [{ @@ -36,7 +37,7 @@ describe('video.js', () => { expect(valid).to.equal(false); }); - it('catches invalid bids when prebid-cache is disabled', () => { + it('catches invalid bids when prebid-cache is disabled', function () { const bidRequests = [{ bids: [{ bidder: 'vastOnlyVideoBidder', @@ -49,9 +50,9 @@ describe('video.js', () => { expect(valid).to.equal(false); }); - it('validates valid outstream bids', () => { + it('validates valid outstream bids', function () { const bid = { - adId: '123abc', + requestId: '123abc', renderer: { url: 'render.url', render: () => true, @@ -70,9 +71,9 @@ describe('video.js', () => { expect(valid).to.equal(true); }); - it('catches invalid outstream bids', () => { + it('catches invalid outstream bids', function () { const bid = { - adId: '123abc' + requestId: '123abc' }; const bidRequests = [{ bids: [{ diff --git a/test/test_index.js b/test/test_index.js index 51323d87437..206f1be1f52 100644 --- a/test/test_index.js +++ b/test/test_index.js @@ -1,4 +1,7 @@ require('test/helpers/prebidGlobal.js'); +require('test/mocks/adloaderStub.js'); var testsContext = require.context('.', true, /_spec$/); testsContext.keys().forEach(testsContext); + +window.$$PREBID_GLOBAL$$.processQueue(); diff --git a/wdio.conf.js b/wdio.conf.js new file mode 100644 index 00000000000..9e816bef24b --- /dev/null +++ b/wdio.conf.js @@ -0,0 +1,55 @@ +const browsers = require('./browsers.json'); + +function getCapabilities() { + function getPlatform(os) { + const platformMap = { + 'Windows': 'WINDOWS', + 'OS X': 'MAC', + } + return platformMap[os]; + } + + // remove the IE11 browser from functional tests + delete browsers['bs_ie_11_windows_10']; + + let capabilities = [] + Object.keys(browsers).forEach(key => { + let browser = browsers[key]; + capabilities.push({ + browserName: browser.browser, + platform: getPlatform(browser.os), + version: browser.browser_version, + acceptSslCerts: true, + 'browserstack.networkLogs': true, + 'browserstack.console': 'verbose', + build: 'Prebidjs E2E ' + new Date().toLocaleString() + }); + }); + return capabilities; +} + +exports.config = { + specs: [ + './test/spec/e2e/**/*.spec.js' + ], + services: ['browserstack'], + user: process.env.BROWSERSTACK_USERNAME, + key: process.env.BROWSERSTACK_ACCESS_KEY, + browserstackLocal: true, + // Do not increase this, since we have only 5 parallel tests in browserstack account + maxInstances: 5, + capabilities: getCapabilities(), + logLevel: 'silent', // Level of logging verbosity: silent | verbose | command | data | result | error + coloredLogs: true, + waitforTimeout: 60000, // Default timeout for all waitFor* commands. + connectionRetryTimeout: 60000, // Default timeout in milliseconds for request if Selenium Grid doesn't send response + connectionRetryCount: 3, // Default request retries count + framework: 'mocha', + mochaOpts: { + ui: 'bdd', + timeout: 60000, + compilers: ['js:babel-register'], + }, + // if you see error, update this to spec reporter and logLevel above to get detailed report. + reporters: ['concise'] +}; diff --git a/webpack.conf.js b/webpack.conf.js index 4b53aabef22..a5c75fa8a1a 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -1,15 +1,45 @@ var prebid = require('./package.json'); -var StringReplacePlugin = require('string-replace-webpack-plugin'); var path = require('path'); var webpack = require('webpack'); var helpers = require('./gulpHelpers'); var RequireEnsureWithoutJsonp = require('./plugins/RequireEnsureWithoutJsonp.js'); +var { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +var argv = require('yargs').argv; +var allowedModules = require('./allowedModules'); // list of module names to never include in the common bundle chunk var neverBundle = [ 'AnalyticsAdapter.js' ]; +var plugins = [ + new RequireEnsureWithoutJsonp() +]; + +if (argv.analyze) { + plugins.push( + new BundleAnalyzerPlugin() + ) +} + +plugins.push( // this plugin must be last so it can be easily removed for karma unit tests + new webpack.optimize.CommonsChunkPlugin({ + name: 'prebid', + filename: 'prebid-core.js', + minChunks: function(module) { + return ( + ( + module.context && module.context.startsWith(path.resolve('./src')) && + !(module.resource && neverBundle.some(name => module.resource.includes(name))) + ) || + module.resource && (allowedModules.src.concat(['core-js'])).some( + name => module.resource.includes(path.resolve('./node_modules/' + name)) + ) + ); + } + }) +); + module.exports = { devtool: 'source-map', resolve: { @@ -19,7 +49,7 @@ module.exports = { ], }, output: { - jsonpFunction: prebid.globalVarName+"Chunk" + jsonpFunction: prebid.globalVarName + "Chunk" }, module: { rules: [ @@ -29,6 +59,7 @@ module.exports = { use: [ { loader: 'babel-loader', + options: helpers.getAnalyticsOptions(), } ] }, @@ -40,56 +71,8 @@ module.exports = { loader: 'babel-loader', } ], - }, - { - test: /\.json$/, - loader: 'json-loader' - }, - { - test: /\.md$/, - loader: 'ignore-loader' - }, - { - test: /constants.json$/, - include: /(src)/, - loader: StringReplacePlugin.replace({ - replacements: [ - { - pattern: /%%REPO_AND_VERSION%%/g, - replacement: function (match, p1, offset, string) { - return `${prebid.repository.url.split('/')[3]}_prebid_${prebid.version}`; - } - } - ] - }) - }, - { - test: /\.js$/, - include: /(src|test|modules|integrationExamples)/, - loader: StringReplacePlugin.replace({ - replacements: [ - { - pattern: /\$\$PREBID_GLOBAL\$\$/g, - replacement: function (match, p1, offset, string) { - return prebid.globalVarName; - } - } - ] - }) } ] }, - plugins: [ - new StringReplacePlugin(), - new RequireEnsureWithoutJsonp(), - - // this plugin must be last so it can be easily removed for karma unit tests - new webpack.optimize.CommonsChunkPlugin({ - name: 'prebid', - filename: 'prebid-core.js', - minChunks: function(module, count) { - return !(count < 2 || neverBundle.includes(path.basename(module.resource))) - } - }) - ] + plugins };